構造が悪いもう一つの理由

興味深い視点で仕事に直面した。 プロジェクトでは、小さなデータ転送オブジェクトはすべて構造によって作成されます。 これがなぜ行われたのかという質問に対して、私はこの方法でデータが変更から保護されているという回答を受け取りました。 一方で、はい、原則として論理的に聞こえますが、どのメソッドもコピーのみを受け取り、何も置き換えることはできません。



しかし、一方で、このアプローチは解決するよりもはるかに多くの問題を抱えています。



まず、構造の使用はパフォーマンスとメモリの両方に影響します。 各呼び出しはコピーであり、構造体の配列はすべてラージオブジェクトヒープに収まることがほぼ保証されており、パフォーマンス(GCの増加により)とメモリ(断片化されたヒープは非常に悪い)の両方に再びヒットします。



第二に、構造の使用にはエラーが伴います。 例:



class Program

{

static void Main( string [] args)

{

Foo[] f = new [] { new Foo {Field = 5}};



for ( int i = 0; i < f.Length; i++)

{

Foo foo = f[i];

foo.Field = 10;

Console .WriteLine(foo.Field);

}



Console .WriteLine(f[0].Field);

}

}

struct Foo

{

public int Field;

}



* This source code was highlighted with Source Code Highlighter .








もちろん、画面には、

 10
 5


しかし、この「コース」は、コードを慎重に研究して初めて、そしてIDEによってのみ明らかになるという事実は、特にFooが別のプロジェクトで定義されている場合、Fooが他のビューアの構造であることを理解することはほとんど不可能です。 .NET開発者はリンクを使用することに慣れています(偶然、Resharperのバグによって雄弁にエコーされます-「for」サイクルを「foreach」に置き換えることをお勧めしますが、これは不可能です)。



そして第三に、実際には、「セキュリティ」の感覚は欺de的です。 配列はこれらの構造体に(構造体があっても)よく見られるため、これらの「不変」構造体は「不変」ではありません。 例:

class Program

{

static void Main( string [] args)

{

Foo f = new Foo

{

Field = 5,

Bars = new []

{

new Bar {Name = "My Name" }

}

};



BadFunction(f);

Console .WriteLine(f.Bars[0].Name);

}



private static void BadFunction(Foo foo)

{

foo.Field = 10;

foo.Bars[0] = new Bar {Name = "Other Name" };

}

}

struct Foo

{

public int Field;

public Bar[] Bars;

}

struct Bar

{

public string Name;

}



* This source code was highlighted with Source Code Highlighter .








実行後、「その他の名前」が表示されます。これは絶対に論理的であり、Fooが構造ではなくクラスであるかどうかは明らかです。 しかし、構造の場合、開発者が配列内の値を変更したいが、疑似不変性のtrapには入らないことを理解することは、やはり非常に困難です。



結論はそれ自体を示唆しています(もちろん、常識と特別なケースをキャンセルした人はいませんでした):

  1. 構造が本当に必要で正当化されている場合にのみ構造を使用します(P / Invokeなど)。

  2. 配列を戻り値の型として使用しないでください-IEnumerable <T>を使用するか、極端な場合はIList <T>を使用します(ただし、ReadOnlyCollection <T>としてのみ)。

  3. 構造を実際に使用する場合は、構造を本当に不変にします。




All Articles