2009-08-25 8 views
12

私はプロパティでList<T>を公開するべきではないことを知っていますが、それを行う適切な方法は何ですか?たとえば、このやって:リストを適切に公開<T>?

public static class Class1 
{ 
    private readonly static List<string> _list; 

    public static IEnumerable<string> List 
    { 
     get 
     { 
      return _list; 
      //return _list.AsEnumerable<string>(); behaves the same 
     } 
    } 

    static Class1() 
    { 
     _list = new List<string>(); 
     _list.Add("One"); 
     _list.Add("Two"); 
     _list.Add("Three"); 
    } 
} 

は私の発信者は、単にList<T>にキャストバックすることが可能になります。

private void button1_Click(object sender, EventArgs e) 
    { 
     var test = Class1.List as List<string>; 
     test.Add("Four"); // This really modifies Class1._list, which is bad™ 
    } 

私は本当に不変List<T>をしたいのであれば、私は常に新しいリストを作成しなければならないでしょうか?

public static IEnumerable<string> List 
    { 
     get 
     { 
      return new ReadOnlyCollection<string>(_list); 
     } 
    } 

しかし、私のリストには、誰かがそれにアクセスしようとするたびにクローン化されるよう、パフォーマンスのオーバーヘッドがある場合、私は心配している:例えば、これは(テストでは、キャスト後にnullである)動作するようですか?

+2

プロパティとして(Tの)リストを暴露/ wが間違って何ですか? – Jason

+0

拡張することはできません:http://blogs.msdn.com/fxcop/archive/2006/04/27/faq-why-does-donotexposegenericlists-recommend-that-i-expose-collection-lt-t-gt-リストの代わりに-lt-t-gt-david-kean.aspx –

+0

これのポイントは何か分かりませんか?私は、リストとしてプロパティを公開するだろう... ???これはスレッドセーフな問題ですか? –

答えて

2
+1

'AsReadOnly'は質問のサンプルコードとまったく同じ方法で' ReadOnlyCollection'のコンストラクタを呼び出します。 – LukeH

+1

私はまだそれを使用したいと思います。要点は、それが理由のために存在し、そのための文書が、それが何をしているかについていくらか詳細に入るということです。それを言って、いいえ、私の答えはマークのものではないものを追加することはありません! –

+1

これ、HenrikとMarcの答えの間で引き裂かれた。これは、AsReadOnly()についてはわかりませんでした。なぜなら、私はそれをキャッシュすることができるように(非常に簡潔で、私はそれをキャッシュすることができるからです。)ReadOnlyCollection _listWrapper;コンストラクタでそれを設定し、 )、それを行うための適切な方法であると思われる(私はEric Lippertがそれを包む良い方法として言及しているのを見た)。スレッドの安全性が問題になるかもしれませんが、それはとにかく一般的な問題です。 –

4

はい、いいえ。新しいオブジェクトが作成されるため、パフォーマンスのオーバーヘッドが発生します。いいえ、あなたのリストはクローンされておらず、ReadOnlyCollectionでラップされています。

7

プロパティは、実際に諸悪の根源ではないようList<T>を公開。特にfoo.Items.Add(...)のような予期される使用が許可されている場合。

public static IEnumerable<T> AsSafeEnumerable<T>(this IEnumerable<T> data) { 
    foreach(T item in data) yield return item; 
} 

しかし、現時点では、あなたの最大の問題は、スレッドセーフである:

あなたはAsEnumerable()にキャスト安全な代替を書くことができます。静的メンバーとして、特にASP.NETのようなものに大きな問題があるかもしれません。でもReadOnlyCollection既存のリストの上には、この苦しむでしょう:

 List<int> ints = new List<int> { 1, 2, 3 }; 
     var ro = ints.AsReadOnly(); 
     Console.WriteLine(ro.Count); // 3 
     ints.Add(4); 
     Console.WriteLine(ro.Count); // 4 

だから、単にAsReadOnlyとされるラップないあなたのオブジェクトは、スレッドセーフにするのに十分な。 コンシューマのデータを保護するだけです(ただし、同期またはコピーを行わない限り、他のスレッドがデータを追加している間にデータを列挙できます)。

+0

s/exiting/existing /:P(しかし+1!) –

+0

私はいつも歩留まりが存在することを忘れています... ASP.netでのスレッドの安全性は、ASPで可変静的クラスを使用しないようにしています。彼らは全体の要求に共有されているので、ネット。私は "スナップショット"が必要な場合は、とにかくコピーを回避する方法はないと思いますので、 'return new List (_list);'はそれを解決し、呼び出し元がリストにキャストすると、彼自身のコピーが、私のものではない。主なポイントは、Class1._listの「不変」を外部の呼び出し元に保ち、Class1のAdd/Remove関数を公開することです。 –

+1

はい、フルコピーは、リスト内の**オブジェクト**のプロパティを変更している人以外のすべての悪意を守ります。 –

2

複製のオーバーヘッドを心配する必要はありません。つまり、ReadOnlyCollectionでコレクションをラップすると、でなく、クローンになります。ラッパーを作成するだけです。基になるコレクションが変更されると、読み取り専用バージョンも変更されます。

新鮮なラッパーを繰り返し作成することを心配する場合は、別のインスタンス変数にキャッシュすることができます。

3

クラスに他の目的がない場合は、リストから継承し、addメソッドをオーバーライドして例外をスローすることができます。

+1

「Collection 」から継承する必要がありますdo this ... 'List 'は多くの 'virtual'メンバーを持っていません。 –

+0

しかし、それはLSP違反ですが、興味深い「ハックアイデア」にもかかわらずです。 –

+0

これはオプションかもしれませんが、フレームワークがすでに適切なメカニズムを提供しているはずだと思うので、間違っていると感じるだけです。 –

0

列挙体が途中でコレクションが変更された場合、AsEnumerableおよびReadOnlyCollectionに問題があります。これらはスレッドセーフではありません。それらを配列として戻して呼び出し時にキャッシュする方がはるかに良い選択肢になります。例えば

public static String[] List{ 
    get{ 
     return _List.ToArray(); 
    } 
} 

//While using ... 

String[] values = Class1.List; 

foreach(string v in values){ 
    ... 
} 

// instead of calling foreach(string v in Class1.List) 
// again and again, values in this context will not be 
// duplicated, however values are cached instance so 
// immediate changes will not be available, but its 
// thread safe 
foreach(string v in values){ 
    ... 
} 
+0

パブリッククラス契約の配列は悪い練習とみなされます – Przemaas

+0

いつもとは限りませんが、Eric Lippertはそれについていくつか考えました:http://blogs.msdn.com/ericlippert/archive/2008/09/22/arrays-considered-somewhat- harmful.aspx –

+0

@Przemaas、配列のような一般的なルールは悪い練習、その常に状況に依存する、あなたが逆アセンブルし、リストのソースを見れば、それはまた、独自の項目を格納する配列を持っています。内容を変更する必要がなく、読み込み専用のコレクションが必要な場合は、arrayを使用するのが最も速い方法です。 –

1

あなたがIEnumerableをとしてあなたのリストを公開した場合、私は戻ってリストにキャスト発信者を心配しないでしょう。クラスのコントラクトで、IEnumerableで定義されている操作だけがこのリストで許可されていることが明示的に示されています。したがって、あなたは暗黙のうちに、そのリストの実装がIEnumerableを実装するものに変わる可能性があることを暗示しています。

+0

それは本当ですか?そして、Svishが指摘したように、データを深くコピーすることなくそれを行うことは不可能です(近づいていますか?)。しかし、人々が足に自分自身をあまりにも簡単に撃たないようにするための少しの障壁がありました。 –

2

私は以前同様の質問を:私はあなたが内部的にList<T>を使用することをお勧めします、とCollection<T>またはIList<T>としてそれを返すということに基づいて

を。または、それを列挙するだけで、そのような追加または防御が必要ない場合は、IEnumerable<T>です。

あなたが戻ってきたものを他のものに投げ入れることができれば、私はただ気にしないと言います。人々があなたのコードを意図していない方法で使いたいなら、何らかの方法でコードを実行することができます。私は以前これについても質問をしていましたが、賢明なことはあなたが意図していることを明らかにすることだと言います。人々がそれを別のやり方で使うのであれば、それは彼らの問題です:p関連する質問:

関連する問題