如何在C#中创建只读对象属性?

如下所示,用户可以更改只读产品字段/属性:

class Program { static void Main(string[] args) { var product = Product.Create("Orange"); var order = Order.Create(product); order.Product.Name = "Banana"; // Main method shouldn't be able to change any property of product! } } public class Order { public Order(Product product) { this.Product = product; } public readonly Product Product; public static Order Create(Product product) { return new Order (product); } } public class Product { private Product(){} public string Name { get; set; } public static Product Create(string name) { return new Product { Name = name }; } } 

我认为这很基本但似乎并非如此。

如何在C#中创建只读对象属性或字段?!

谢谢,

您需要将Product的Name属性设置为private:

 public class Product { private Product(){} public string Name { get; private set; } public static Product Create(string name) { return new Product { Name = name }; } } 

readonly关键字阻止您将新实例放入字段中。

它不会神奇地使字段内的任何对象不可变。
如果你写的话你会发生什么?

 readonly Product x = Product.Create(); Product y; y = x; y.Name = "Changed!"; 

如果你想要一个不可变对象,你需要通过删除所有公共setter使类本身不可变。

您看到的问题是您将readonly修饰符与您认为的只读属性混淆。 readonly修饰符确保只能通过初始化或构造函数来分配字段,例如,这里是readonly有效用法:

 public class MyClass { private readonly int age = 27; // Valid, initialisation. } public class MyClass { private readonly int age; public MyClass() { age = 27; // Valid, construction. } } public class MyClass { private readonly int age; public int Age { get { return age; } set { age = value; } } // Invalid, it's a readonly field. } 

你发现的是,你的Person类本身是可变的,这意味着虽然Order.Product字段是readonly,但Person的内部结构却不是。 为此,如果要创建只读属性,您可能希望将类型创建为不可变 – 因为其内部结构/值不能更改。

只能在构造函数中修改只读字段(正如您所做的那样)。 如果您尝试在其他位置编辑该字段,则应该收到编译器错误。

readonly关键字表示该字段只能在构造函数中设置,并且该值不能更改。

如果该值是引用类型,则不会使该对象成为只读

如果您想要只读属性,只需省略setter。

为了更好地演示readonly的属性:

 order.Product = Product.Create("Apple") // <- not allowed because Product field is readonly order.Product.Name = "Apple" // <- allowed because Name is not readonly field or private property 

在设置属性名称private之后(例如public string Name { get; private set; } ):

 order.Product.Name = "Apple" // <- not allowed 

这个解决方案的问题当然是:

 new Product { Name = "Orange" } // <- not allowed if called from outside Product class 

你有2个选择:

  • 如果你不希望Product的任何属性是可修改的(例如是不可变的)那么只是不定义setter(或使setter私有)并且让product的构造函数(或工厂方法)初始化它们
  • 如果你仍然希望Product的属性可以修改,但是当它是Order的成员时,则创建一个接口IProduct,其属性名称定义为: public string Name { get; } public string Name { get; } ,make Product实现此接口,然后将Order类型的Product字段定义为: public readonly IProduct Product;

任何此解决方案都将满足您的两个要求:

 order.Product.Name = "Apple" // <- not allowed new Product { Name = "Orange" } // <- allowed when called from Product's Create method 

它们之间只有区别:

 order.Product.Name = "Apple" // <- not allowed Product product = new Product { Name = "Orange" } // not allowed in solution #1 when called from outside Product, allowed in solution #2 product.Name = "Apple" // <- not allowed in solution #1 but allowed in solution #2