まず、SOやブログで共変性と反変性について多くの説明を読みました。共変性と反変性に関するこのような素晴らしいシリーズを作成してくれたEricLippertに大いに感謝します。
しかし、私は少し頭を動かそうとしているというより具体的な質問があります。
エリックの説明によると、共変性と反変性はどちらも変換を表す形容詞であると私が理解している限りです。共変変換は型の順序を保持する変換であり、反変変換はそれを逆にする変換です。
私は、ほとんどの開発者が直感的に理解できるような方法で共分散を理解しています。
//covariant operation
Animal someAnimal = new Giraffe();
//assume returns Mammal, also covariant operation
someAnimal = Mammal.GetSomeMammal();
ここでの戻り操作は、両方の動物が哺乳類またはキリンよりもまだ大きいサイズを維持しているため、共変です。その点で、ほとんどの戻り演算は共変であり、反変演算は意味がありません。
//if return operations were contravariant
//the following would be illegal
//as Mammal would need to be stored in something
//equal to or less derived than Mammal
//which would mean that Animal is now less than or equal than Mammal
//therefore reversing the relationship
Animal someAnimal = Mammal.GetSomeMammal();
もちろん、このコードはほとんどの開発者にとって意味がありません。
私の混乱は、共変性の引数パラメーターにあります。あなたが次のような方法を持っていた場合
bool Compare(Mammal mammal1, Mammal mammal2);
私は常に、入力パラメーターが常に反変の振る舞いを強制することを学びました。タイプが入力パラメーターとして使用される場合、その動作は反変である必要があります。
ただし、次のコードの違いは何ですか
Mammal mammal1 = new Giraffe(); //covariant
Mammal mammal2 = new Dolphin(); //covariant
Compare(mammal1, mammal2); //covariant or contravariant?
//or
Compare(new Giraffe(), new Dolphin()); //covariant or contravariant?
あなたがこのようなことをすることができないのと同じトークンであなたはすることができません
//not valid
Mammal mammal1 = new Animal();
//not valid
Compare(new Animal(), new Dolphin());
私が求めているのは、メソッド引数が反変変換を通過させる理由だと思います。
長い投稿でごめんなさい、多分私はこれを間違って理解しています。
編集:
以下のいくつかの会話によると、たとえばデリゲートレイヤーを使用すると、明らかに共変性を示すことができることを理解しています。次の例を考えてみましょう
//legal, covariance
Mammal someMammal = new Mammal();
Animal someAnimal = someMammal;
// legal in C# 4.0, covariance (because defined in Interface)
IEnumerable<Mammal> mammalList = Enumerable.Empty<Mammal>();
IEnumerable<Animal> animalList = mammalList;
//because of this, one would assume
//that the following line is legal as well
void ProcessMammal(Mammal someMammal);
Action<Mammal> processMethod = ProcessMammal;
Action<Animal> someAction = processMethod;
もちろん、これは違法です。なぜなら、誰かが任意の動物をsomeActionに渡すことができるのに対し、ProcessMammalは、哺乳類またはより具体的なもの(哺乳類よりも小さい)を期待しているからです。これが、someActionがActionまたはより具体的なもののみである必要がある理由です(アクション)
ただし、これは中央にデリゲートのレイヤーを導入しています。反変の投影が発生するためには、中央にデリゲートが存在する必要がありますか?また、Processをインターフェイスとして定義する場合、引数パラメーターを反変型として宣言するのは、上記で示したデリゲートを誰かに実行させたくないからです。
public interface IProcess<out T>
{
void Process(T val);
}