隐式和显式委托创建的区别(使用和不使用泛型)

5

请看下面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)’的类型参数。请明确指定类型参数。”

我不明白这个代码怎么会编译通过(在孤立的情况下):void DoSomething(Action action) { //... }因为 T 没有被声明... 这整段代码是不是在一个泛型类中? - Mehrdad Afshari
是的,我现在正在修改我的示例。应该读作Go<T>(){... - Nathan Ridley
不知道是不是只有我这样,但是当我编译你这里的代码时,它没有出现任何错误。假设我把这段代码放在非泛型类中。我正在使用C# 3.5 SP1。 - JoshBerke
我的代码实际上出现在一组通用扩展方法中。 - Nathan Ridley
我无法重现这个问题。当您将其包装在类中时,此代码在C# 2、3和4中编译得非常好。如果您想要对问题进行分析,请发布实际演示问题的代码。 - Eric Lippert
3个回答

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很棒),但在少数情况下会失败。在理想的世界中,将来版本中它会变得更易于理解和更强大...我们拭目以待!

谢谢!我接受了其他的答案,但你的回答对我仍然有帮助。加一! - Nathan Ridley

3
非泛型隐式委托创建只是语法糖,因此编译器为其生成的代码与泛型委托创建完全相同。
DoSomething(MyAction);

并且

DoSomething(new Action(MyAction));

由于它可以直接从方法参数和上下文推断出委托的类型,因此使用隐式委托更为方便。

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


谢谢,现在我明白了。以下是协变性与逆变性的文章链接,供那些不理解的人参考:http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science) - Nathan Ridley
我个人认为这更多与类型推断的限制有关,而不是协变/逆变。如果您阅读类型推断的规范部分(我现在无法记住它),您将看到方法组基本上没有太多机会。 - Jon Skeet

1

仅仅是一个旁注.. 由于某些原因,这在VB中可以工作。

似乎在将方法组/地址转换为委托时,C#和VB中的预处理器实现有所不同。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接