[解決済み] IEnumerable vs IReadonlyCollection vs ReadonlyCollection (リストメンバーを公開する場合)
質問
私は、リストのメンバーを公開するというテーマについて、かなりの時間を費やして熟考してきました。私と同じような質問で、Jon Skeet が素晴らしい回答をしました。どうぞ、お気軽にご覧ください。
メンバーコレクションを公開するためのReadOnlyCollectionまたはIEnumerable?
私は通常、特にAPIを開発している場合、リストを公開することにかなり偏執的です。
私はいつもリストの公開にIEnumerableを使っています。それはとても安全で、それだけ柔軟性があるからです。ここで例を使ってみましょう。
public class Activity
{
private readonly IList<WorkItem> workItems = new List<WorkItem>();
public string Name { get; set; }
public IEnumerable<WorkItem> WorkItems
{
get
{
return this.workItems;
}
}
public void AddWorkItem(WorkItem workItem)
{
this.workItems.Add(workItem);
}
}
IEnumerableに対してコーディングする人は、ここでかなり安全です。後で順序付きリストか何かを使うことにしても、彼らのコードはどれも壊れないし、まだいい感じです。この欠点はIEnumerableがこのクラスの外のリストにキャストバックされる可能性があることです。
この理由から、多くの開発者はメンバーを公開するためにReadOnlyCollectionを使用します。これは、決してリストにキャストバックされることがないので、非常に安全です。私としては、IEnumerableの方がより柔軟性があり、リストとは異なるものを実装したくなったときに便利だからです。
私は、より気に入った新しいアイデアを思いつきました。IReadOnlyCollectionを使用します。
public class Activity
{
private readonly IList<WorkItem> workItems = new List<WorkItem>();
public string Name { get; set; }
public IReadOnlyCollection<WorkItem> WorkItems
{
get
{
return new ReadOnlyCollection<WorkItem>(this.workItems);
}
}
public void AddWorkItem(WorkItem workItem)
{
this.workItems.Add(workItem);
}
}
これはIEnumerableの柔軟性の一部を保持し、非常にうまくカプセル化されていると感じています。
私はこの質問を投稿して、私のアイデアについていくつかの意見を得ました。IEnumerable よりもこのソリューションが好きですか? ReadOnlyCollection の具体的な戻り値を使用する方が良いと思いますか? これはかなりの議論であり、私は私たち全員が思いつく利点/欠点が何であるかを試して見たいと思います。
あなたの意見に前もって感謝します。
EDIT
まず最初に、ここでの議論に多くの貢献をしてくれた皆さんに感謝します。私は確かに、一人ひとりから多くを学びましたし、心から感謝したいと思います。
私はいくつかの追加のシナリオと情報を追加しています。
IReadOnlyCollectionとIEnumerableには、いくつかの共通の落とし穴があります。
以下の例で考えてみましょう。
public IReadOnlyCollection<WorkItem> WorkItems
{
get
{
return this.workItems;
}
}
上の例は、インターフェースが読み込み専用であっても、リストにキャストし直して 変異させることができます。このインターフェースは、その名前に反して、不変性を保証するものではありません。したがって、新しいReadOnlyCollectionを返さなければなりません。新しいリスト(本質的にはコピー)を作成することで、オブジェクトの状態は安全かつ健全に保たれます。
Richibanは彼のコメントで最もよく言っています:インターフェースは何かができることを保証するだけで、何ができないかを保証するものではありません。
例として以下をご覧ください。
public IEnumerable<WorkItem> WorkItems
{
get
{
return new List<WorkItem>(this.workItems);
}
}
上記はキャストやミューテートが可能ですが、オブジェクトはまだイミュータブルです。
もうひとつの枠にはまらない発言は、コレクションクラスでしょう。次のように考えてみてください。
public class Bar : IEnumerable<string>
{
private List<string> foo;
public Bar()
{
this.foo = new List<string> { "123", "456" };
}
public IEnumerator<string> GetEnumerator()
{
return this.foo.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
上のクラスはfooを思い通りに変異させるためのメソッドを持つことができますが、あなたのオブジェクトは決してあらゆる種類のリストにキャストされて変異させられることはありません。
Carsten FührmannはIEnumerablesのyield returnステートメントについて素晴らしい指摘をしています。
皆さん、改めてありがとうございました。
どのように解決するのですか?
クラスライブラリの話をすると、IReadOnly*は本当に便利だと思うし、あなたはそれを正しく実行していると思う :)
イミュータブルコレクションのことですね...。以前はimmutablesしかなく、配列を拡大するのは大変な作業でした。そこで.netはフレームワークに何か違うもの、つまりmutableコレクションを含めることにしました。
objective-cのような他の今日の言語をチェックすると、実際にはルールが完全に逆転していることがわかります! 言い換えれば、インターフェイスは単にイミュータブルを公開し、内部ではミュータブルコレクションを使用します(もちろん、それはあります)。
というわけで、他の言語でのちょっとした経験から、.netのリストはとても強力だが、immutableコレクションは何らかの理由でそこにあったのだと思うようになりました:)
この場合、インターフェイスの呼び出し元を助ける問題ではなく、IList対Listのように、内部実装を変更する場合にすべてのコードを変更することを避けるために、IReadOnly*であなた自身、あなたのクラスを適切ではない方法で使用されないように保護しています。
関連
-
[解決済み】SmtpException: トランスポート接続からデータを読み取れません:net_io_connectionclosed
-
[解決済み] DBNullから他の型にオブジェクトをキャストすることができない
-
[解決済み】MetadataException: 指定されたメタデータ・リソースをロードできない
-
VSでscanfエラーを恒久的に解決するには、ソースファイルを作成し、自動的に#define _CRT_SECURE_NO_WARNINGS 1を追加してください。
-
[解決済み] なぜList<T>を継承しないのですか?
-
[解決済み] IList<string> または IEnumerable<string> からカンマ区切りリストを作成する。
-
[解決済み] IEnumerable<T>のforeachのLINQでの等価性
-
[解決済み】Pythonの「private」メソッドは、なぜ実際にはprivateではないのですか?
-
[解決済み】IEnumerable vs List - What to Use? どのように動作するのでしょうか?
-
[解決済み] メンバーコレクションを公開するにはReadOnlyCollectionかIEnumerableか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] メンバー '<メンバー名>' にインスタンス参照でアクセスできない
-
[解決済み】文字列が有効な DateTime " format dd/MM/yyyy " として認識されなかった。
-
[解決済み】「namespace x already contains a definition for x」エラーの修正方法は?VS2010にコンバートした後に発生しました。
-
[解決済み】取り消せないメンバはメソッドのように使えない?
-
[解決済み】Socket.Selectがエラー "An operation was attempted on something that is not a socket" を返す。
-
[解決済み] UnityでOnCollisionEnterが呼ばれない
-
[解決済み】aspNetCore 2.2.0 - AspNetCoreModuleV2 エラー
-
[解決済み】画像のペイントにTextureBrushを使用する方法
-
[解決済み】「namespace」なのに「type」のように使われる。
-
[解決済み] メンバーコレクションを公開するにはReadOnlyCollectionかIEnumerableか?