2016-07-20 10 views
0

共変タイプTのインターフェースを考えてみましょう。私はTを使用するこのインターフェイスのすべての派生クラスのプロパティが読み取り専用であり、汎用クラスの場合は共変な場合を調べています。次に、このインタフェースが引数型としてTを使用するメソッドを定義しているとします。どのような違反が許可されていますか?例えばここで反変的な位置にある共変型を使用すると、どのような違反が有効になりますか?

、考えてみます。

interface ICov<out T> { 
    void maybe_safe_set(T v); 
} 
class ImplCov<T> : ICov<T> { 
    public readonly T a; 
    public readonly IEnumerable<T> b; 
    public readonly IEnumerable<IEnumerable<T>> c; 
    // public readonly IList<T> d; // but not this 

    public void maybe_safe_set(T v) { 
    // do things that can't modify state: the type of our 
    // readonly, covariant IEnumerable members can't be modified 
    } 
} 

をC#では、私はエラーを取得する:

Invalid variance: The type parameter 'T' must be contravariantly valid on 'ConsoleApplication.ICov.maybe_safe_set(T)'. 'T' is covariant.

Tが反変な位置に発見されたことから、驚くべきことではないとします。しかし、ここで起こり得る違反は考えられません。

+0

共分散は、型がインターフェイスからのみ消費され、渡されないことを意味します。このように考えると、 'out'キーワードは、型がインターフェースから出てくるだけで、' maybe_safe_set'メソッドの引数として入ります。代わりに、対立遺伝子変異体として定義されるべきである。インタフェースは契約であり、与えられた実装が何をするかはわかりません。 – juharr

+2

'public static class C {public static T value}'と 'public void maybe_safe_set(T v){C .Value = v}'と '((ICov )new ImplCov ())となります。maybe_safe_set(新しいオブジェクト()) 'である。今度は、 'object'を' C.Value'に 'string'型で代入しようとしています。 – PetSerAl

+1

'ImpleConv 'を 'ICov 'にキャストし、 'maybe_safe_set'を呼び出すことができ、' string'ではないものが渡され、ランタイム型エラーが発生する可能性があります。実際には、実装クラスの実装の詳細については何もしません。 – juharr

答えて

1

あなたは:

interface ICov<out T> // BAD! 
{ 
    void maybe_safe_set(T v); 
} 

ここで問題が来ます。いつものように、私たちは持っている:

class Animal { /* ... */ } 
class Dog : Animal { public void Woof() { } /* ... */ } 
class Cat : Animal { /* ... */ } 

を次に考えてみます。

class Impl : ICov<Dog> 
{ 
    public void maybe_safe_set(Dog v) 
    { 
    v.Woof(); // our 'Dog' v can really bark 
    } 
} 

をうまくコンパイルなります。その後

この:

var impl1 = new Impl(); 
ICov<Dog> impl2 = impl1; // OK, implements that 
ICov<Animal> impl3 = impl2; // OK, you claim interface is covariant ('out')! 

var badAnimal = new Cat(); 
impl3.maybe_safe_set(badAnimal); // ICov<Animal> takes in Animal, right? 

// oh my God, you mad a 'Cat' bark! 

常に人々が共同してcontravariance尋ねる同じ例です。

+0

私は良い例があると思いました。しかし、あなたは猫の樹皮を作るのを打つことはできません。それは将来の盗作のために永遠に提出されます。 –

+0

_「人々が協同性と反共性について質問するときは、常に同じ例です」_ - はい、それはいつもあります。これは疑問を示唆しています。なぜ、同じ例を持つ質問の1つと重複するものとしてマークするのではなく、なぜ質問に答えますか? –

0

私は他の誰も知りませんが、私はこのことも混乱していると感じています。それを理解するために、コンパイラがどのような違反を防止しているかを確認するためのサンプルを作成する必要があります。

インターフェイスでメソッドを最初に見てみましょう。これは、すべて有効です。

interface ICov<out T> {} 

public class BaseClass { } 
public class InheritedClass: BaseClass { } 

ICov<BaseClass> x = new MyCov<InheritedClass>(); 

Tが共変であるため、タイプICov<BaseClass>の変数はTBaseClassから派生MyCov<T>のインスタンスを参照することができます。

今、私たちは、インターフェイスにタイプTで動作する方法(コンパイラが防止されているもの?)今

interface ICov<out T> 
{ 
    List<T> ListOfT { get; set; } 
    // ^^^ compiler doesn't like this. 
} 

public class BaseClass { } 
public class InheritedClass: BaseClass { } 

public class MyCov<T> : ICov<T> { 
    public List<T> ListOfT { get; set; } = new List<T>(); 
} 

我々は正確に問題を見ることができますを追加することができれば、これは作成するだろうか:

var usesInheritedClass = new MyCov<InheritedClass>(); 

//This is legal, because the type parameter in ICov is covariant 
ICov<BaseClass> usesBaseClass = usesInheritedClass; 

//Here's where it goes bad. 
usesBaseClass.ListOfT.Add(new BaseClass()); 

usesInheritedClassには、InheritedClassのリストが含まれています。しかし、私はそれをICov<BaseClass>としてキャストすることができるので、InheritedClassのリストにBaseClassを追加できます。これはうまくいかないでしょう。

コンパイラのエラーは混乱しますが、正しい場所にあります。私の下流のエラーを整理して修正しようとはしません。これらのタイプが混乱する可能性があるシナリオを防ぐことによって、これらのダウンストリームエラーを完全に防ぎます。

+0

実際、私はこの正確な違反を避けるために 'readonly'と' IEnumerable'をこの質問に持ってきました!私は状態が問題だと思ったので、可能な限り不変なものにしようとしましたが、Jeppeが指摘するように型付きのメソッドは問題があります。 – concat

関連する問題