2016-04-21 2 views
8

スタックオーバーフローとインターネット全面的に、構造体を変更不可能に保つことが良い設計原則であることがわかります。残念ながら、実際にこれらの構造体が本当に不変になるような実装はありません。structを不変にするにはどうしたらいいですか?

構造体に内部に参照型がないと仮定すると、構造体を実際には不変にするにはどうすればよいですか?つまり、どのようにしてプリミティブフィールドのいずれか(おそらくコンパイル時/ランタイム例外による)の突然変異を防ぐのですか?

私は構造体は不変作る試みて簡単なテストを書きましたが、それでもSystem.ComponentModel.ImmutableObjectAttributeを使用していないが働いた:

class Program 
{ 
    static void Main(string[] args) 
    { 
     ImmutableStruct immStruct1 = new ImmutableStruct(); 
     Console.WriteLine(immStruct1); //Before mutation. 

     immStruct1.field1 = 1; 
     immStruct1.field2 = "Hello"; 
     immStruct1.field3 = new object(); 
     Console.WriteLine(immStruct1); //After 1st mutation. 

     immStruct1.field1 = 2; 
     immStruct1.field2 = "World"; 
     immStruct1.field3 = new object(); 
     Console.WriteLine(immStruct1); //After 2nd mutation. 

     Console.ReadKey(); 
    } 
} 

[ImmutableObject(true)] 
struct ImmutableStruct 
{ 
    public int field1; 
    public string field2; 
    public object field3; 

    public override string ToString() 
    { 
     string field3String = "null"; 
     if (field3 != null) 
     { 
      field3String = field3.GetHashCode().ToString(); 
     } 
     return String.Format("Field1: {0}, Field2: {1}, Field3: {2}", field1, field2, field3String); 
    } 
} 
+0

ビットOTですが、クラスを使用する代わりに構造体が必要な特定の理由がありますか?長年にわたるLOBプログラミングでは、構造体がクラスに比べて好ましいユースケースは見つかりませんでした。 – StingyJack

+0

フィールドに読み取り専用をマークしますか?また、あなたはこれを見たことがあります:http://stackoverflow.com/questions/29974301/should-a-c-sharp-struct-have-only-read-only-properties – Pawel

+0

頭に構造体は、C + +のビットフィールドの構造体に似ています。構造体はインスタンス化されており、構造体が決して変更されないことが私の望みです。私が偶然にデータを変更していないという約束があります。 –

答えて

12

フィールドをprivate readonlyにして、アルは、あなたがフィールドを読み取り専用とプロパティだけがいずれかのコンストラクタで初期化することができますゲッターゲッターのみのプロパティ

public struct ImmutableStruct 
{ 
    public ImmutableStruct(int f1, string f2, object f3) 
    { 
     Field1 = f1; 
     Field2 = f2; 
     Field3 = f3; 
    } 

    public int Field1 { get; } 
    public string Field2 { get; } 
    public object Field3 { get; } 
} 

注意を使用することができますC#6.0(Visual Studioの2015)を皮切りコンストラクタ

public struct ImmutableStruct 
{ 
    private readonly int _field1; 
    private readonly string _field2; 
    private readonly object _field3; 

    public ImmutableStruct(int f1, string f2, object f3) 
    { 
     _field1 = f1; 
     _field2 = f2; 
     _field3 = f3; 
    } 

    public int Field1 { get { return _field1; } } 
    public string Field2 { get { return _field2; } } 
    public object Field3 { get { return _field3; } } 
} 

の値クラスではフィールドまたはプロパティ初期化子public int Field1 { get; } = 7;も使用します。

コンストラクタが構造体上で実行されているとは限りません。例えば。構造体の配列がある場合は、各配列要素に対して明示的にイニシャライザを呼び出す必要があります。参照型の配列の場合、すべての要素は最初にnullに初期化されます。これにより、各要素にnewを呼び出す必要があることが明らかになります。しかし、構造体のような値型についてはそれを忘れるのは簡単です。

var immutables = new ImmutableStruct[10]; 
immutables[0] = new ImmutableStruct(5, "hello", new Person()); 
+2

Field3は参照型なので変更可能と見なされます。インスタンスを不変に保つために、ゲッターにオブジェクトのコピーを戻す必要があります。 – Crowcoder

+2

参照オブジェクト自体を変更不可能にします。 'String'は参照型ですが、不変です。構造体は値型を表しているので、可変参照型への参照はおそらく良い考えではありません。 –

+0

ニースの答え!それは動作し、最新です!ところで、プロパティ初期化子がある場合、それはクラス内でのみ発生します。それ以外の場合は、エラーが発生します: ''インスタンスのプロパティまたは構造体のフィールド初期化子を持つことはできません。 –

3

は、プライベートあなたの不変のデータを保管してください:

struct ImmutableStruct 
{ 
    private int field1; 
    private string field2; 
    private object field3; 

    public ImmutableStruct(int f1, string f2, object f3) 
    { 
     field1 = f1; 
     field2 = f2; 
     field3 = f3; 
    } 

    public int Field1 => field1; 
    public string Field2 => field2; 
    public object Field3 => field3; 
} 

以下雑然と:

struct ImmutableStruct 
{ 
    public ImmutableStruct(int f1, string f2, object f3) 
    { 
     Field1 = f1; 
     Field2 = f2; 
     Field3 = f3; 
    } 

    public int Field1 { get; } 
    public string Field2 { get; } 
    public object Field3 { get; } 
} 
関連する問題