2011-10-06 60 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