0
abstract class A<T> {
  List<T> Items { get; set; }
}

class B {}
class C : A<B> {}

class D : B {}
class E : A<D> {}

static class X {
  public A<B> GetThing(bool f) {
    return f ? new E() : new C();
  }
}

「ConsoleApplication4.E」と「ConsoleApplication4.C」の間に暗黙的な変換がないため、条件式のタイプを判別できません

これで「理由」はわかりましたが(と思います)、これをコンパイルする方法がわかりません。何らかの分散を定義し、それを継承するインターフェイスを作成する必要があると思いますが、よくわかりません。しかし、とにかく、E()から継承する必要がありC()ます。

テイカーはいますか?

ティア

4

3 に答える 3

15

C# で一般的な分散を使用するには、次の条件をすべて満たす必要があります。

  1. C# 4 を使用する
  2. 変化する型は、ジェネリック インターフェイスまたはジェネリック デリゲートである必要があります
  3. 型パラメーターは、"in" (反変) または "out" (共変) とマークする必要があります。
  4. 型パラメーターの注釈は、可能なすべての操作で型安全であることが証明されている型を生成する必要があります。
  5. 「ソース」タイプの引数と「宛先」タイプの引数には、それらの間で ID または参照変換が必要です。

プログラムは条件 1 と条件 5 を満たしていますが、条件 2、3、または 4 を満たしていません。

条件 4 に違反するものが必要なため、必要な分散を取得する方法はありません。条件 4 を除くすべての条件を満たした場合に何が起こるかを見てください。

// Suppose this declaration were legal:
interface IMyCollection<out T> 
{
  List<T> Items { get; set; } 
}

class Animal {}
class AnimalCollection : IMyCollection<Animal> 
{ 
  public List<Animal> { get; set; }
}

class Giraffe : Animal {}
class GiraffeCollection : IMyCollection<Giraffe> 
{
  public List<Giraffe> { get; set; } 
}

static class X 
{
  public IMyCollection<Animal> GetThing() 
  {
    // IMyCollection is covariant in T, so this is legal.
    return new GiraffeCollection(); 
  }
}

class Tiger : Animal {}

...

IMyCollection<Animal> animals = X.GetThing(); 
// GetThing() actually returns a GiraffeCollection
animals.Items = new List<Animal>() { new Tiger(); }

また、キリン コレクションには、トラを含む動物のリストが含まれるようになりました。

バリアンスを使用する場合は、タイプセーフにする必要があります。コンパイラはバリアンス アノテーションがタイプセーフであると判断できないため、IMyCollection の宣言を拒否します。

これをどのようにしてタイプセーフにするかを見てみましょう。問題は、アイテムがインターフェイスを介して書き込み可能であることです。

interface IMyCollection<out T> 
{
  IEnumerable<T> Items { get; }
}

class Animal {}
class AnimalCollection : IMyCollection<Animal> 
{ 
  public IEnumerable<Animal> { get { yield return new Tiger(); } }
}

class Giraffe : Animal {}
class GiraffeCollection : IMyCollection<Giraffe> 
{
  public IEnumerable<Giraffe> { get { yield return new Giraffe(); } } 
}

static class X 
{
  public IMyCollection<Animal> GetThing() 
  {
    return new GiraffeCollection();
  }
}

class Tiger : Animal {}

...

MyCollection<Animal> animals = X.GetThing(); 
// GetThing() actually returns a GiraffeCollection
foreach(Animal animal in animals.Items) { ... } 
// Items yields a giraffe, which is an animal

完全にタイプセーフです。これは合法的な C# 4 プログラムです。

C# 4 での共分散と反分散の設計の詳細に興味がある場合は、このテーマに関する多数の記事を読むことを検討してください。ここでそれらを見つけることができます:

http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/

これらは最新のものから古いものへの順序でリストされていることに注意してください。下から始めます。

特に、インターフェースのアノテーションがいつ有効であるかを判断するためのルールに興味がある場合は、この記事を参照してください (いくつかのエラーを発見して修正したので、この会話ができてよかったです)。

http://blogs.msdn.com/b/ericlippert/archive/2009/12/03/exact-rules-for-variance-validity.aspx

于 2011-04-07T14:11:11.230 に答える
3

Eと の間には関係がありませんC。したがって、戻り値の型はA<B>、それらの型の共通の親と見なされるものでなければなりません。Aこれが機能するには、ジェネリック引数が定義されているインターフェイスである必要もありますout

interface A<out T> { }

class B { }
class C : A<B> { }

class D : B { }
class E : A<D> { }

static class X
{
    public static A<B> GetThing(bool f)
    {
        if (f)
        {
            return new E();
        }
        return new C();
    }
}

三項演算子 ( ?:)では機能しませんでした。コンパイラは単にそれを好まない.


アップデート:

コメントセクションでの@Eric Lippertの優れた発言の後、次のようにキャストして三項演算子を使用できますA<B>

public static A<B> GetThing(bool f)
{
    return f ? (A<B>)new E() : new C();
}
于 2011-04-07T11:38:25.230 に答える
0

共通の親がないためC、これは機能しません。Eこのようなものを作成するには、クラスであれインターフェースであれ、共通の親タイプを作成する必要があります。

于 2011-04-07T11:38:56.010 に答える