2011-07-04 5 views
7

私はMutating Readonly StructsについてEric Lipertsのブログを読んでいます。このブログでは、値のタイプが不変でなければならない理由として、このブログには多くの参考文献があります。 しかし、まだ一つのことは、明らかではないが、あなたが値型にアクセスするときは、常にそれのコピーを取得することを言うと、ここでの例です:不変の値のタイプ

struct Mutable 
{ 
    private int x; 
    public int Mutate() 
    { 
     this.x = this.x + 1; 
     return this.x; 
    } 
} 

class Test 
{ 
    public readonly Mutable m = new Mutable(); 
    static void Main(string[] args) 
    { 
     Test t = new Test(); 
     System.Console.WriteLine(t.m.Mutate()); 
     System.Console.WriteLine(t.m.Mutate()); 
     System.Console.WriteLine(t.m.Mutate()); 
    } 
} 

そして質問はこれを、なぜ私は

public readonly Mutable m = new Mutable(); 
を変更したとき

public Mutable m = new Mutable(); 

に至るまでは、ES を予想作業を開始します。

なぜ値型が不変でなければならないのかをより明確に説明できますか? 私はスレッドの安全性には優れていることを知っていますが、この場合も同じことを参照型に適用できます。

+0

リンク先のブログ投稿からさらに引用をコピーして貼り付けることなく、これに答える方法はわかりません。 'm'が 'readonly'であると' t.m'が値になり、変数ではなく、一時変数に置かれ、 'Mutate'メソッドの中で' this'である一時変数です。 'readonly'を取り除き、残りの要素はもはや適用されないので、' Mutate'の中の 'this'は' m'になります。 –

答えて

4

突然変異法による構造は、いくつかの状況では奇妙な動作をします。

既に発見した例は、読み取り専用フィールドです。読み取り専用フィールドを変更する必要がないため、防御的なコピーが必要です。

しかし、プロパティとしても使用されます。もう一度暗黙のコピーが行われ、コピーのみが変更されます。プロパティにセッターがある場合でも。

struct Mutable 
{ 
    private int x; 
    public int Mutate() 
    { 
     this.x = this.x + 1; 
     return this.x; 
    } 
} 

Mutable property{get;set;} 

void Main() 
{ 
    property=new Mutable(); 
    property.Mutate().Dump();//returns 1 
    property.Mutate().Dump();//returns 1 :(
} 

これは、変異方法が構造上問題であることを示しています。しかし、パブリックフィールドまたはプロパティを持つ変更可能な構造体がセッターを持つことは問題であることを示していません。

2

スレッドセーフは、明らかに技術的な理由があります。これは、値型および参照型(System.Stringを参照)に適用されます。

もっと一般的なガイドラインは、「値の型は不変でなければなりません」です。これはコードの可読性に関するもので、主に可変値が原因で起こりうる混乱から来ています。このコードスニペットは単なる例です。ほとんどの人は1,1,1の結果を期待しません。

+0

はい、それでもまだ読まれていなければ、すべてが期待通りに機能します。 より一般的な質問はそれではありませんが、なぜ値型が不変であることが示唆されていますか? – NDeveloper

+0

@ Ndev:私はもっと一般的な(なぜ)質問に答えたと思います。 @Damienはコードサンプルを分析しました。 –

+0

しかし、読みやすさはref型と同じではありませんか?または、これが参照を渡して突然変異する可能性があることを意味します。 – NDeveloper

2

私はC#を知らないので、質問の2番目の部分に答えようとします。

なぜ値型は不変でなければならないのですか?

ビューの設計のポイントを主導ドメインからのオブジェクトの2つのタイプがあります。

  • 値オブジェクト/タイプ - 自分のアイデンティティは、その値によって決定される(例えば番号:2は常に2である - のアイデンティティが数字2は常に同じですので、2 == 2は常に真です)
  • エンティティ(参照型) - これらは他の値型で構成されていてもよく、その識別情報はアイデンティティ自体によって識別されます。あなたとまったく同じように見える男でした、それはあなたではありません)

値の型が変更可能な場合は、数値2の値を変更することができたらどうなるか想像してみてください。2 == 1 + 1は保証されません。

は、より多くのためにこれらのリンクを参照してください。

+0

可変的な値型は問題がありますが、それはあなたの答えに聞こえるほど悪くありません。問題は通常、あなたが変えたいものの代わりに一時的なコピーを間違って突然変異させてはいけない、何かを突然変異させることができるということではありません。 – CodesInChaos

0

私はその一例についてトリッキーな事は1が、それは可能であるべきではないと主張できることだと思います。 Mutable読み取り専用のインスタンスを作成しましたが、Mutate()関数を使用してその値を変更することができます。したがって、ある意味では、不変性の概念に違反します。しかし厳密に言えば、プライベートフィールドxは読み取り専用ではないために機能します。あなたは不変性が実際に施行される可変クラス内の1つの簡単な変更を加える場合:

private readonly int x; 

は、その後に変異()関数は、コンパイルエラーが生成されます。

この例では、読み取り専用変数のコンテキストでcopy-by-valueがどのように機能するかを明確に示しています。 mを呼び出すたびに、インスタンスのコピーを作成するのではなく、インスタンスのコピーを作成します。後者は、Mutableが構造体ではなくクラスである場合に発生します。

私はあなたが1)インスタンスのコピーと2)読み取​​り専用のインスタンスのコピーを呼び出すたびに、コピー時に常にxの値はになりますが発生します。コピーでMutate()を呼び出すと、xが1に増えます。これは、x自体は読み込み専用ではないためです。しかし、次にMutate()を呼び出すと、元のデフォルト値0を呼び出しています。記事で "mは不変ですが、コピーはそうではありません"と述べています。元のインスタンスのすべてのコピーは、コピーされるオブジェクトが変更されることはないのに対し、コピーされるオブジェクトは変更可能であるため、xは0となります。

多分それが役に立ちます。

関連する問題