2011-10-06 98 views
3

正如下面可以看到,用户能够改变只读产品字段/属性:如何在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#中创建一个只读对象属性或字段?

感谢,

+1

相关:[为什么微软建议对可变值的只读字段?](http://stackoverflow.com/questions/2804805/why-does-microsoft-advise-against-readonly-fields-with-mutable-values/2804850#2804850) –

回答

5

你需要让产品专用集的名称属性:

public class Product 
{ 
    private Product(){} 

    public string Name { get; private set; } 

    public static Product Create(string name) 
    { 
     return new Product { Name = name }; 
    } 
} 
5

readonly关键字阻止你把一个新的实例进入该领域。

它并不神奇地使任何对象内的字段不可变。
你有什么期望,如果你写

readonly Product x = Product.Create(); 

Product y; 
y = x; 
y.Name = "Changed!"; 

如果你想要一个不可变对象的情况发生,你需要做的类本身不变移除所有公共setter方法。

0

只读字段可以总是可以在构造函数中修改(如你正在做的)。如果您尝试在其他地方编辑该字段,则应该会收到编译器错误。

+0

这个这不是他的问题,尽管它最初可能并不明显。 'Product'是只读的,他的问题是他为什么可以稍后更改'Product.Name'。 –

+0

不,我刚给你看!该字段可以在任何地方更改。 –

+0

啊,是的,我错过了在主要的财产变化。我只在Order构造函数中看到它的初始化。在这种情况下,SLak上的+1回答! –

0

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

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

如果你想使财产只读的,只是省略了二传手。

1

您所看到的问题是您将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是只读的,Person的内部结构不是。为此,如果您想要创建一个只读属性,您可能希望将您的类型创建为不可变 - 因为其内部结构/值不能更改。

0

为了更好地展示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 

使物业名称私人的二传手之后(例如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种选择:

  • ,如果你不希望产品的任何属性是可修改(例如是不可变的),那么简单地不要定义setter(或使setter私有)并使产品的构造函数(或工厂方法)初始化它们
  • 如果您仍然希望产品属性可以修改,但不是当它是订单成员时,则创建一个界面IProduct,其属性名称定义为:public string Name { get; },使产品实现此界面,然后使产品字段类型Order定义为: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