C# 为什么是行动<;行动<;T>&燃气轮机;协变的?
这是我很难理解的事情。我理解C# 为什么是行动<;行动<;T>&燃气轮机;协变的?,c#,.net,covariance,contravariance,C#,.net,Covariance,Contravariance,这是我很难理解的事情。我理解操作是逆变的,可能被声明为逆变的 internal delegate void Action<in T>(T t); 内部委托无效操作(T); 然而,我不明白为什么一篇试图解释它的行动博客文章。特别是,我没有完全理解“输入协方差解释”小节中的含义 如果“派生->基本”对替换为“动作->动作”对,则同样自然 考虑这个例子: string s = "Hello World!"; object o = s; // fine 如果我们用Action Acti
操作
是逆变的,可能被声明为逆变的
internal delegate void Action<in T>(T t);
内部委托无效操作(T);
然而,我不明白为什么一篇试图解释它的行动博客文章。特别是,我没有完全理解“输入协方差解释”小节中的含义
如果“派生->基本”对替换为“动作->动作”对,则同样自然
考虑这个例子:
string s = "Hello World!";
object o = s; // fine
如果我们用Action
Action<string> s;
Action<object> o;
s = o; // reverse of T's polymorphism
然后
动作at1;
行动at2;
at1=at2;//逆变
这是有效的。然后
Action<Action<TDerived>> aat1;
Action<Action<TBase>> aat2;
aat2 = aat1; // covariant
动作。简言之,协变赋值的工作原理与普通OOP多态性类似,而逆变赋值的工作原理是反向的。考虑一下:
class Base { public void dosth(); }
class Derived : Base { public void domore(); }
Action<Action<Base>> a2 = x => { x(new Base()); };
Action<Action<Derived>> b2 = x => { x(new Base()); };
使用T的动作:
// this is all clear
Action<Base> a1 = x => x.dosth();
Action<Derived> b1 = a1;
使用new-Derived()
时,它仍然可以工作。然而,这不能笼统地说,因此是非法的。考虑这一点:
class Base { public void dosth(); }
class Derived : Base { public void domore(); }
Action<Action<Base>> a2 = x => { x(new Base()); };
Action<Action<Derived>> b2 = x => { x(new Base()); };
Action a2=x=>{x(newbase());};
动作b2=x=>{x(新基());};
错误:Action
期望domore()
存在,但Base
只提供dosth()
好的,所以首先让我们明确您所说的Action是什么意思
最近的一个问题是,编译器如何确定方差是类型安全的:
有一个继承树,对象作为根。树上的路径通常是这样的
object->Base->Child
树上较高类型的对象始终可以指定给树上较低类型的变量。泛型类型中的协方差意味着实现的类型按照树的方式进行关联
object->IEnumerable->IEnumerable->IEnumerable
object->IEnumerable->IEnumerable…
相反意味着实现的类型以一种与树相反的方式相互关联
object->Action->Action->Action
Action<string> s;
Action<object> o;
s = o; // reverse of T's polymorphism
当你更深一层时,你必须再次倒转树
object->Action->Action->Action->Action->Action
Action<string> s;
Action<object> o;
s = o; // reverse of T's polymorphism
p、 相反,对象层次结构不再是一棵树,而是一个有向无环图这很有意义。感谢您澄清输入/输出位置并不是判断类型差异的确定方法。我想我对此有点困惑。我也很喜欢箭的比喻。@Tejas:这不是比喻;这就是范畴理论中协方差的定义。范畴理论有两种基本类型的事物“对象”和“箭头”,它们连接成对的对象。协变函数是在一对相关对象之间保持箭头方向的函数。嗯,我不知道。好吧,我要去读你们的博客系列,希望这能消除我对差异的疑虑/误解。再次感谢。是的,我理解在Action
中包含一个类型将翻转差异的“规则”。但是就像我说的,我不明白这个规则背后的逻辑。@Tejas你的意思是你不知道为什么像
中的这样的反变量必须翻转方差?Eric Lippert做了很好的解释,但我不明白为什么每次你在操作
中包装一个类型时,它都会翻转方差。你从纯“数学”的角度解释得很好。我正在寻找一个更详细的解释为什么会发生这种情况。谢谢你的回答!
Action<Action<Base>> a2 = x => { x(new Base()); };
Action<Action<Derived>> b2 = x => { x(new Base()); };
static void DoSomething(Fish fish)
{
fish.Swim();
}
static void Meta(Action<Fish> action)
{
action(new Fish());
}
...
Action<Action<Fish>> aaf = Meta;
Action<Fish> af = DoSomething;
aaf(af);
Action<Action<Animal>> aaa = aaf;
aaa(af);
aaa( (Animal animal) => { animal.Feed(); } );
Fish --> Animal
Action<Fish> <-- Action<Animal>
Action<Action<Fish>> --> Action<Action<Animal>>
Action<Action<Action<Fish>>> <-- Action<Action<Action<Animal>>>
...