38

これの何が問題なのですか?

interface IRepository<out T> where T : IBusinessEntity
{
    IQueryable<T> GetAll();
    void Save(T t);
    void Delete(T t);
}

それは言います:

無効な分散: 型パラメーター 'T' は、'MyNamespace.IRepository.Delete(T)' で反変的に有効でなければなりません。「T」は共変です。

4

3 に答える 3

67

コンパイラがそれを許可した場合に何が起こるかを考えてみましょう:

interface IR<out T>
{
    void D(T t);
}

class C : IR<Mammal>
{
    public void D(Mammal m)
    {
        m.GrowHair();
    }
}
...
IR<Animal> x = new C(); 
// legal because T is covariant and Mammal is convertible to Animal
x.D(new Fish()); // legal because IR<Animal>.D takes an Animal

そして、あなたは魚に髪を伸ばそうとしました。

「out」は、「T は出力位置でのみ使用される」ことを意味します。入力位置で使用しています。

于 2011-02-18T15:22:19.557 に答える
44

out型パラメーターは、共変、つまり戻り値の型でのみ使用できます。したがって、IQueryable<T> GetAll()正しいですが、void Delete(T t)そうではありません。

はクラスで共変と反変の両方で使用されるため、ここでは (nor )Tを使用できません。outin

この背後にある理論的背景について詳しく知りたい場合は、少し休憩して、ウィキペディアの記事「共分散と反分散」を読んでください。


お帰りなさい。では、リポジトリにこれらすべてのメソッドが必要であるが、共変インターフェースが必要な場合はどうすればよいでしょうか? 共変部分を独自のインターフェースに抽出できます。

interface IDataSource<out T> where T : IBusinessEntity
{
    IQueryable<T> GetAll();
}

interface IRepository<T> : IDataSource<T> where T : IBusinessEntity
{
    void Save(T t);
    void Delete(T t);
}

これは、.NET BCL がこの問題を解決する方法でもあります。IEnumerable<out T>共変ですが、「読み取り操作」のみをサポートします。ICollection<T>は のサブタイプでありIEnumerable<out T>、読み取り操作と書き込み操作を許可するため、それ自体を共変にすることはできません。

于 2011-02-18T13:13:23.053 に答える
23

次の 2 つの方法は間違っています。

void Save(T t);
void Delete(T t);

Tメソッド引数として持つことはできません。out Tジェネリック定義で共変( )にしたい場合にのみ、戻り値の型として。

または、反変性が必要な場合は、ジェネリックパラメーターをメソッド引数としてのみ使用し、型を返さないこともできます。

interface IRepository<in T> where T : IBusinessEntity
{
    void Save(T t);
    void Delete(T t);
}
于 2011-02-18T13:12:49.910 に答える