Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/282.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么是行动<;行动<;T>&燃气轮机;协变的?_C#_.net_Covariance_Contravariance - Fatal编程技术网

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>>>
...