37

次のコードでは、がに暗黙的に変換可能であるため、からelementsに暗黙的にキャストできると期待していました。baseElementsTBaseIBase

public interface IBase { }
public interface IDerived : IBase { }
public class VarianceBug
{
    public void Foo<TBase>() where TBase : IBase
    {
        IEnumerable<TBase> elements = null;
        IEnumerable<IDerived> derivedElements = null;
        IEnumerable<IBase> baseElements;

        // works fine
        baseElements = derivedElements;

        // error CS0266: Cannot implicitly convert type 
        //   'System.Collections.Generic.IEnumerable<TBase>' to 
        //   'System.Collections.Generic.IEnumerable<IBase>'. 
        //   An explicit conversion exists (are you missing a cast?)
        baseElements = elements;
    }
}

ただし、コメントに記載されているエラーが発生します。

仕様からの引用:

バリアントタイプパラメータで宣言されたインターフェイスまたはデリゲートタイプの場合、タイプT<A1, …, An>は分散変換可能であり、バリアントタイプパラメータごとに次のいずれかが当てはまります。T<B1, …, Bn>TT<X1, …, Xn>Xi

  • Xiは共変であり、からへの暗黙の参照またはID変換が存在AiしますBi

  • Xiは反変であり、からへの暗黙の参照またはID変換が存在BiしますAi

  • Xiは不変であり、ID変換はからに存在AiしますBi

私のコードを確認すると、仕様と一致しているようです。

  • IEnumerable<out T>インターフェイスタイプです

  • IEnumerable<out T>バリアント型パラメーターで宣言されます

  • T共変です

  • TBaseからへの暗黙の参照変換が存在しますIBase

それで-それはC#4コンパイラのバグですか?

4

2 に答える 2

51

差異は参照型に対してのみ機能します(またはID変換があります)。次を追加しない限り、それTBaseが参照型であるかどうかはわかりません: class

 public void Foo<TBase>() where TBase : class, IBase

私は書くことができたので:

public struct Evil : IBase {}
于 2010-05-06T18:16:23.470 に答える
14

マークは正しいです-私はちょうど同じ応答を貼り付けようとしていました。

共変性と反変性に関するFAQを参照してください。

http://blogs.msdn.com/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx

FAQから:

「分散は、型パラメーターが参照型である場合にのみサポートされます。」

分散は値型ではサポートされていません

以下もコンパイルされません。

// int is a value type, so the code doesn't compile.
IEnumerable<Object> objects = new List<int>(); // Compiler error here.
于 2010-05-06T18:20:26.887 に答える