代表协方差混乱难题!

时间:2010-02-26 21:00:12

标签: c# delegates covariance

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

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

}

6 个答案:

答案 0 :(得分: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) {}
}

现在演示逆变

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

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

_delegate(new object());

此时,不良MyMethod的参数意味着属于SomeObject类型,但实际类型为{{ 1}}。这将是一件非常糟糕的事情,因此编译器会阻止它发生。

这一切都更有意义吗?

答案 1 :(得分:4)

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

答案 2 :(得分: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)
    {
    }
}

答案 3 :(得分:1)

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

答案 4 :(得分:1)

来自您提供的MSDN链接

  

协方差允许一种方法具有   更多派生的返回类型比什么   在代表中定义。   逆变法允许一种方法   派生程度较低的参数类型   而不是代表类型。

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

答案 5 :(得分:0)

协方差和逆变是关于理解继承的原则。

在协方差和逆变中,s.th。 &#34;传递&#34;,作为返回值或作为委托方法的参数。那是&#34;传递的&#34;必须被抓住&#34;在一个容器中。在C#中 - 或者编程行话本身 - 我们使用单词bucket作为我所谓的容器。有时你必须回到其他词语才能理解常用行话词的含义。

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

继承说你可以在动物桶中捕捉一只鸟,因为这只鸟是动物。因此,如果一个方法的参数必须捕获一只鸟,你可以在动物桶(动物类型的参数)中捕获它,这就是逆转。 如果你的方法,即你的代表返回一只鸟,那么&#34;桶&#34;也可以是类型为bird或less的派生类型(父类型),这意味着捕获方法返回值的变量必须与返回值具有相同或更少的派生类型。

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

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