代表协方差混乱难题!

为什么这不起作用? 我不能正确理解代表协方差吗?

public delegate void MyDelegate(object obj) public class MyClass { public MyClass() { //Error: Expected method with 'void MyDelegate(object)' signature _delegate = MyMethod; } private MyDelegate _delegate; public void MyMethod(SomeObject obj) {} } 

正确 – 你没有正确理解协方差 – 但是如果你有相同的类型但是返回值,你的代码会工作,如下所示:

 public delegate object MyDelegate() public class MyClass { public MyClass() { _delegate = MyMethod; } private MyDelegate _delegate; public SomeObject MyMethod() { return null; } } 

这将certificate协方差 。 或者,您可以将其保留为参数,但切换类型:

 public delegate void MyDelegate(SomeObject obj) public class MyClass { public MyClass() { _delegate = MyMethod; } private MyDelegate _delegate; public void MyMethod(object obj) {} } 

这现在表现出逆差

我的经验法则是问自己,“鉴于代表,我能用它做什么?如果我可以传入一个会破坏方法的参数,那么转换应该失败。如果方法可以返回一些会破坏它的东西调用者 ,转换应该失败。“

在您的代码中,您可以调用:

 _delegate(new object()); 

那时,可怜的MyMethod有一个参数,它的类型是SomeObject ,但实际上object类型。 这将是一件非常糟糕的事情,因此编译器会阻止它发生。

这一切都更有意义吗?

参数是逆变的,返回类型是协变的。 如果使用不是SomeObject实例的object调用委托,则会出现输入错误。 另一方面,从包含在返回object的委托中的例程返回SomeObject很好。

您需要使用通用。

编辑:为什么? 因为正如另一张海报所指出的那样,Object和SomeObject并不等同于Object可能不是SomeObject。 这是语言中generics的全部要点。

 public delegate void MyDelegate(T obj) public class MyClass { public MyClass() { _delegate = MyMethod; } private MyDelegate _delegate; public void MyMethod(SomeObject obj) { } } 

MyDelegate类型声明您可以传递任何类型的对象。但是, MyMethod只接受SomeObject类型的SomeObject 。 如果我尝试调用传递不同类型对象的委托会发生什么: _delegate("a string object") ? 根据MyDelegate的声明,这应该是允许的,但是你的函数MyMethod实际上不能接收字符串参数。

从您提供的MSDN链接

协方差允许方法具有比委托中定义的更多派生返回类型 。 Contravariance允许使用参数类型的方法,该方法的派生类型少于委托类型。

您正在尝试使用不受支持的更多派生参数类型 (尽管.NET 4.0可能会解决许多协方差/逆变问题)。

协方差和反演是关于理解inheritance的原则。

在协方差和逆变中,s.th。 是“传递”,作为返回值或作为委托方法的参数。 “传递”的东西必须被“抓住”在容器中。 在C#中 – 或者编程行话本身 – 我们使用单词bucket作为我称之为sockets的东西。 有时你必须回到其他单词才能理解常用行话单词的含义。

无论如何,如果你理解inheritance,这很可能是这里的任何读者,那么唯一要注意的是容器,即用于捕获的桶必须是与正在进行的类型相同或更少的派生类型。通过 – 这对于协方差和逆变都是正确的。

遗产说你可以在动物桶中捕捉一只鸟,因为这只鸟是一种动物。 因此,如果方法的参数必须捕获一只鸟,你可以在动物桶(动物类型的参数)中捕获它,这就是逆转。 如果你的方法,即你的委托返回一只鸟,那么“桶”也可以是一种类型的鸟类或更少派生的(父类型),这意味着你捕获方法的返回值的变量必须是与返回值相同或更少的派生类型。

只要改变你的想法来区分正在传递的东西和那些捕获的东西,那么关于协方差和逆变的所有复杂性都会很好地消除。 然后你意识到同样的原则在起作用。 只是因为它只以一种方式流动,所以不能违反inheritance。

并且编译器是如此智能,当你以更专业的类型(再次,并且根据需要)转换存储桶时,然后只有那时你才能获得添加到更多派生类中的所有专用方法。 这就是它的美丽。 所以它是捕获,投射和使用你拥有和可能需要的东西。