隐式和显式委托创建之间的区别(有和没有泛型)

时间:2009-05-14 14:36:00

标签: c# .net generics .net-2.0 delegates

请参阅下面Go()方法中的四行:

delegate void Action<T>(T arg);
delegate void Action();

void DoSomething<T>(Action<T> action)
{
    //...
}

void DoSomething(Action action)
{
    //...
}

void MyAction<T>(T arg)
{
    //...
}

void MyAction()
{
    //...
}

void Go<T>()
{
    DoSomething<T>(MyAction<T>); // throws compiler error - why?
    DoSomething(new Action<T>(MyAction<T>)); // no problems here
    DoSomething(MyAction); // what's the difference between this...
    DoSomething(new Action(MyAction)); // ... and this?
}

请注意,第一次调用生成的编译器错误是: 方法'Action(T)'的类型参数无法从用法中推断出来。尝试明确指定类型参数。

3 个答案:

答案 0 :(得分:14)

MyActionnew Action(MyAction)之间没有区别(当它们都有效时),除了前者在C#1中不起作用。这是implicit method group conversion。有些时候这是不适用的,最值得注意的是当编译器无法确定你想要什么类型的委托时,例如。

Delegate foo = new Action(MyAction); // Fine
Delegate bar = MyAction; // Nope, can't tell target type

这在您的问题中发挥作用,因为所涉及的两种方法都被重载了。这基本上导致了头痛。

至于仿制药方面 - 这很有意思。方法组并没有从C#3类型推断中得到太多的喜爱 - 我不确定它是否会在C#4中得到改进。如果你调用泛型方法并指定类型参数,那么类型推理工作得相当好 - 但是如果你试图以相反的方式进行,它就会失败:

using System;

class Test
{
    static void Main()
    {
        // Valid - it infers Foo<int>
        DoSomething<int>(Foo);
        // Valid - both are specified
        DoSomething<int>(Foo<int>);
        // Invalid - type inference fails
        DoSomething(Foo<int>);
        // Invalid - mismatched types, basically
        DoSomething<int>(Foo<string>);
    }

    static void Foo<T>(T input)
    {
    }

    static void DoSomething<T>(Action<T> action)
    {
        Console.WriteLine(typeof(T));
    }
}

C#3中的类型推断非常复杂,并且在大多数情况下运行良好(特别是它对LINQ很有用)但在其他一些情况下失败。在理想的世界中,在未来版本中理解会变得更加容易......我们会看到!

答案 1 :(得分:3)

非泛型隐式委托创建只是语法糖,因此编译器为

生成完全相同的代码
DoSomething(MyAction);

DoSomething(new Action(MyAction));

因为它可以直接从方法参数&amp;中推断出委托的类型。上下文。

使用泛型委托,由于协方差和逆变而必须指定委托类型(详见http://msdn.microsoft.com/en-us/library/ms173174(VS.80).aspx) - T in Action可以是方法中T的超类型,它仍然是被接受为代表方法。因此,您需要在委托中明确指定T,因为编译器无法自行解决它。

答案 2 :(得分:1)

只是一个旁注.. 出于某种原因,这适用于VB。

当我将Methodgroup / adderessof转换为委托时,似乎C#中的预处理器的实现和VB不同。