如何在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