8

为什么这不起作用?我没有正确理解委托协方差吗?

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)
    {}

}
4

6 回答 6

12

正确-您没有正确理解协方差-但是:)如果您具有相同的类型但作为返回值,您的代码将起作用,如下所示:

public delegate object MyDelegate()

public class MyClass
{
    public MyClass()
    {
         _delegate = MyMethod;
    }

    private MyDelegate _delegate;

    public SomeObject MyMethod() { return null; }
}

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

public delegate void MyDelegate(SomeObject obj)

public class MyClass
{
    public MyClass()
    {
         _delegate = MyMethod;
    }

    private MyDelegate _delegate;

    public void MyMethod(object obj) {}
}

这现在证明了逆变

我的经验法则是问自己,“给定委托,我能用它做什么?如果我可以传入一个会破坏方法的参数,那么转换应该失败。如果方法可以返回一些会破坏caller,转换应该失败。”

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

_delegate(new object());

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

这一切更有意义吗?

于 2010-02-26T21:05:48.527 回答
4

参数是逆变的,返回类型是协变的。object如果要使用不是 的实例来调用委托SomeObject,则会出现打字错误。另一方面,SomeObject从包装在返回的委托中的例程返回object是可以的。

于 2010-02-26T21:06:59.013 回答
3

您需要使用泛型。

编辑:为什么?因为正如另一张海报所指出的,Object 和 SomeObject 并不等同于同一事物,因为 Object 可能不是 SomeObject。这就是语言中泛型的全部意义。

public delegate void MyDelegate<T>(T obj)

public class MyClass
{
    public MyClass()
    {
        _delegate = MyMethod;
    }

    private MyDelegate<SomeObject> _delegate;

    public void MyMethod(SomeObject obj)
    {
    }
}
于 2010-02-26T21:05:56.007 回答
1

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

于 2010-02-26T21:05:28.660 回答
1

从您提供的MSDN链接

协变允许方法具有 比委托中定义的更多派生的返回类型。逆变允许方法的 参数类型 比委托类型派生的要少。

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

于 2010-02-26T21:08:10.557 回答
0

协变和逆变是关于理解 Is-a-Principle 的继承。

在这两者中,协方差和逆变,s.th。被“传递”,作为返回值或作为委托方法的参数。“传递”的东西必须“捕获”在容器中。在 C# 中——或者像这样的编程术语——我们使用桶这个词来表示我所说的容器。有时您必须回退到其他词才能了解常用术语的含义。

无论如何,如果您了解继承,这里的任何读者很可能都会了解,那么唯一需要注意的是容器,即用于捕获的桶必须是相同类型或派生类型比现在的类型更少。通过——这对于协变和逆变都是正确的。

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

只需转换您的思维以区分正在传递的内容和捕获的内容,然后所有关于协变和逆变的复杂性都很好地解决了。然后你会意识到同样的原理在起作用。只是不能违反继承,因为它只以一种方式流动。

并且编译器非常聪明,以至于当您将桶转换为更专业的类型(再次,并且根据需要)时,然后并且只有这样您才能将所有专门的方法返回到更派生的类中。这就是它的美妙之处。所以它是捕捉、投射和使用你拥有的和可能需要的东西。

于 2015-07-03T12:52:58.010 回答