8

私は C# の共分散の問題について多くのことを聞いたり読んだりしてきましたが、いくつかの質問とシナリオを提示したかったので、この問題に関する混乱を解消できることを願っています。

これらの例では、以下が常に定義されていると想定してください。

public class Apple : Fruit {}

私の最初の例:

IList<Apple> apples = GetApples();
IList<Fruit> fruits = apples;

これはうまくいくはずですよね?これを C# で数回テストしたところ、正常にコンパイルされ、正常に実行されました (最初の例のテストは、コンソールに情報を出力するポリモーフィック呼び出しがあったため、これよりもわずかに多くなりました)。

2 番目の例:

IList<Apple> apples = GetApples();
IList<object> fruits = apples;

この 2 番目の例では、これはコンパイルされるべきではなく、.NET 4.0 で解決されている共分散の問題の原因であると私は理解しています。間違っている場合は修正してください。また、.NET 4.0 では具体的な型間の共変性/反変性が許可されておらず、インターフェイスのみが許可されていることも認識しています。

最後に、いくつかの定義を取得したいと思います。これら 3 つの用語の背後にある意味がよくわかりません。

  • 共分散
  • 反変性
  • 不変(不変と同じ?)

最後の単語については、暗黙のルールを持つ変更を参照するために C++ でよく使用しました。たとえば、整数があり、1 から 10 の間の値しか持てない場合、「不変性」とは、1 から 10 の間の値しか持てないということです。私はこれを誤解している可能性があり、この定義は、この特定の説明のために C# に適切に変換されます。

編集

私の目標は、C# のジェネリック インターフェイスに関する共分散またはキャストの問題が何であるかを正確に理解することです。私が投稿した例は、問題がどこにあるのかについての私の理解です。すべての例が正常にコンパイル/機能する場合は、C# で最も一般的な共変性/反変性/キャストの問題を再現する例を提示してください。問題を特定して他の人に説明できるように、これを知る必要があります。

4

2 に答える 2

6

オブジェクトを変更するメソッドをサポートしているため、IList<T>インターフェースは共変であると定義されていません。Add

次の点を考慮してください。

IList<Apple> apples = GetApples();
IList<object> fruits = apples;
fruits.Add(new Banana());

Bananafromを取得できるようになりましたがapples、これは意図したものではありません。したがって、IListインターフェイスは共分散をサポートしておらず (決してサポートすることはありません)、コンパイル エラーが発生するはずです。

あなたは同じ問題を抱えているはずです

IList<Apple> apples = GetApples();
IList<Fruit> fruits = apples;
fruits.Add(new Banana());

なぜそれがあなたのためにコンパイルされたのかわかりません。

コレクションからの要素の読み取りのみをサポートするため、IEnumerable<out T>インターフェイスは共変にすることができます (.NET 4.0 以降にあります) 。IEnumerable


Scala 言語には、共変オブジェクトと反変オブジェクトの同様の概念があり、ジェネリックについて説明している「 Scala でのプログラミング」の章は、C# での共分散の入門としても役立つはずです。

于 2011-07-05T00:48:16.157 に答える
2

共分散と反分散の説明については、この記事をご覧ください。

http://msdn.microsoft.com/en-us/library/dd799517.aspx


CLR は既にジェネリック型の分散をある程度サポートしており、c# 4 ではこれを使用する構文が用意されています。ジェネリック分散を使用すると、分散はインターフェイスとデリゲート型の型パラメーターに適用されます。

共分散とは、戻り値をより一般的な型として扱えるようにすることであり、インターフェイス メソッドがその型のみを返す場合に可能です。この例では、派生インターフェイス インスタンスをベースとして再割り当てできますが、その逆はできません。

public interface ISomeInterfaceWithOut<out T>
{
    T GetSomething();
}

ISomeInterfaceWithOut<Apple> b = new Blah<Apple>();
ISomeInterfaceWithOut<Fruit> fruit = b;

反変性とは、パラメーターの型をより具体的な型として扱うことができることであり、インターフェイス メソッドがその型のみを使用する場合に可能です。この例では、基本インターフェイス インスタンスを派生として再割り当てできますが、その逆はできません。

public interface ISomeInterfaceWithIn<in T>
{
    void SetSomething(T instance);
}

ISomeInterfaceWithIn<Fruit> b = new Blah<Fruit>();
ISomeInterfaceWithIn<Apple> apple = b;

不変性とは、両方のケースが発生し、インターフェイス メソッドが型を返したり消費したりする場合です。共分散も反分散も適用できません。メソッドに両方のケースが含まれているため、「out T」共分散または「in T」反分散型パラメーターを定義することは許可されていないため、ここでは上記のような使用法は機能しません。

このことを考慮:

//it is not possible to declare 'out T' or 'in T' here - invalid variance
public interface ISomeInterface<T>
{
    T GetSomething();
    void SetSomething(T instance);
}

どちらの例もそのままでは機能しません。反変性/共変性は、ジェネリック型が「in」/「out」として宣言されているインターフェイスとデリゲートに適用されます。IList は不変です。

インターフェイスは .NET 4 から共変であるため、IEnumerable<T>4 以降ではこれを行うことができますが、3.5 ではできません。ここで果物を IList として使用すると、果物を宣言するときに機能しません。これは共変ではありません。

List<Apple> apples = new List<Apple>();
//List<Apple> apples implements IEnumerable<Apple>
IEnumerable<Fruit> fruits = apples;

の定義は次のとおりです。IEnumerable<T>

//Version=4.0.0.0
public interface IEnumerable<out T> : IEnumerable
{
    new IEnumerator<T> GetEnumerator();
}

//Version=2.0.0.0
public interface IEnumerable<T> : IEnumerable
{
    new IEnumerator<T> GetEnumerator();
}
于 2011-07-05T01:01:54.760 に答える