web-dev-qa-db-ja.com

Tは逆変的に有効でなければなりません

これの何が問題になっていますか?

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

それは言う:

無効な分散:タイプパラメータ「T」は、「MyNamespace.IRepository.Delete(T)」で逆分散的に有効である必要があります。 「T」は共変です。

34
Eduardo

コンパイラがそれを許可した場合に何が起こるかを考えてください。

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は出力位置でのみ使用される」ことを意味します。入力位置で使用しています。

62
Eric Lippert

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

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

この背後にある理論的背景についてもっと知りたい場合は、少し休憩して "Covariance and Contravariance" Wikipediaの記事 を読んでください。


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

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);
}

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

42
Heinzi

次の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);
}
21
Darin Dimitrov