1

が a の代わりにa をList.AsReadOnly()返すという事実が私にとって困難なケースに遭遇しました。返されたコレクションは のであったため、自動的に にアップキャストできませんでした。これは奇妙に思えたので、.Net ソース コードを確認したところ、このメソッドが に対して行うのとは異なること、つまりインターフェイスではなく具象クラスを返すことを確認しました。ReadOnlyCollectionIReadOnlyCollectionValueDictionaryIReadOnlyCollectionAsReadOnly()ListDictionary

これがなぜなのか説明できる人はいますか?この不一致があることは、特に可能な場合、特にパブリックの場合にインターフェイスを使用したいため、不利益に思えます。

私のコードでは、コンシューマーは単なるプライベート メソッドであるため、そのパラメーター シグネチャを からIReadOnlyDictionary<T, IReadOnlyCollection<T>>に変更できると最初は考えていましたIReadOnlyDictionary<T, ReadOnlyCollection<T>>。しかし、これにより、プライベート メソッドがコレクションの値を変更する可能性があるように見えることに気付きました。そのため、インターフェイスを適切に使用するために、以前のコードに煩わしい明示的なキャストを追加しました。

.ToDictionary(
   item => item,
   item => (IReadOnlyCollection<T>) relatedItemsSelector(item)
      .ToList()
      .AsReadOnly() // Didn't expect to need the direct cast
)

ああ、私は常に共分散と反分散を混同しているので、誰かが自動キャストを妨げているものを教えてください。将来のためにそれらを覚えておく方法を賢明な方法で思い出させてください。(たとえば、コレクションは、_____ [入力/出力] パラメーターに対して ______バリアント [co/contra] ではありません。) インターフェイスの実装が多数存在する可能性があり、すべてをキャストするのは安全ではないため、これができない理由を理解しています。ディクショナリの個々の要素を要求された型に変換します。私がこの単純な側面でさえ吹き飛ばしていて、それを理解していない場合を除き、その場合、私を正しく設定するのを手伝ってくれることを願っています...

4

1 に答える 1

0

その理由は歴史的なものです。IReadOnly* インターフェイスは .NET 4.5 で追加されましたList<T>.AsReadOnly()が、.NET 2.0 で再び追加されました。戻り値の型を変更すると、重大な変更になります。

明示的なキャストはそれほど悪くありません。コンパイラは静的に検証できるため、これはランタイム キャストではありません (キャストは IL に出力されません)。ちなみに、IReadOnlyList<T>リストへのインデックス付きアクセスも提供するキャストすることができます。必要な型を返す拡張メソッドを作成することもできます (例: AsReadOnlyList())。

in{co,contra}variance については、C# のキーワード(contravariant) とout(covariant)を使用すると覚えやすいと思います。in型パラメーターは入力メソッドの引数としてのみ表示できますが、out型パラメーターは出力(戻り値) としてのみ表示できます。type などのパラメーターを受け入れるメソッドは、 typeBaseで安全に呼び出されるため、その方向にパラメーターをDerived安全にキャストできます。は正反対です。inout

例えば:

interface IIn<in T> { Set(T value); }
IIn<Base> b = ...
IIn<Derived> d = b;
d.Set(derived); // safe since any method accepting Base can handle Derived

interface IOut<out T> { T Get(); }
IOut<Derived> d = ...
IOut<Base> b = d;
b.Get(); // safe since any Derived is Base

読み取り専用ではないコレクション インターフェイスは、 *-variant にすることはできません。これらは と の両方である必要がinありout、安全ではないからです。コンパイラと CLR はそれを許可しません。.NET には、配列に関する安全でないバリアンスの形式があります。

var a = new[] { "s" };
var o = (object[])a;
o[0] = 1; // ArrayTypeMismatchException

彼らが一般的な分散による混乱をどのように回避したかったかがわかります。おそらく、彼らは (cotravariant) 方向を許可する書き込み専用インターフェースを追加することができたでしょうがin、私は彼らがそれに多くの価値を見いださなかったと推測しています.

于 2016-07-17T08:48:16.523 に答える