2

我对编译器无法理解的一些行为感到有些困惑。我已将其简化为以下代码示例:

    public interface IFoo { }
    public interface IBar<T> : IFoo { }
    public delegate void DHandler<T>(IBar<T> arg);

    public static class Demo
    {
        static void Garply<T>(DHandler<T> handler) { }

        public static void DoStuffWithInt()
        {
            Garply<int>(Handler);
        }

        static void Handler(IFoo arg) { }
    }

我的问题是我不希望代码编译,但确实如此。我不希望它编译,因为在签名中DHandler<int>需要,但是方法声明了,但事实并非如此(尽管反之亦然)。因此不是 a ,因此它的委托不能用作调用的参数。IBar<int>HandlerIFoo IBar<int>HandlerDHandler<int>Garply<int>

如果我更改代码以读取Handler(IBar<int> arg)它会编译。如果我将其更改为阅读Handler(IBar<string> arg),则不会。这两种行为都符合我的预期。

提示这个问题的实际问题是,当签名为 时Handler(IBar<int> arg),编译器抱怨我需要显式指定Garply调用的类型参数。在这个例子中微不足道,但在实际代码中,这将是一个真正的麻烦。我很困惑,因为 to 的参数Garply是一个带有签名的方法(IBar<int> arg),所以它的委托将是 a DHandler<int>,所以选择的Garply<T>将是明确的Garply<int>。但显然编译器看到了歧义。它正在调查,这导致我遇到上述难题,我只能猜测编译器可能在想“好吧,令杰森惊讶的是,我已经接受了 an IFooIBar<T>所以T必须指定 a 让我知道我应该把它编译为IBar<T>而不是IFoo“。这可以解释为什么它需要类型参数。但是任何人都可以对此有所了解吗?

4

1 回答 1

8

这是在 C# 2 中引入的委托变体。与在 C# 4 中引入的通用变体不同。

这是一个更简单的例子:

delegate void Foo(string x);    

class Test
{
    static void Main()
    {
        Foo foo = Bar;
    }

    static void Bar(object y) {}
}

关键是我们可以从该方法创建一个Foo委托的实例,因为只要给定了any就可以工作。当一个委托被调用时,它总是提供一个引用,并且有一个从到的引用转换。所以如果我有:BarBar objectFoostringstringobject

Foo f = ...;
f("fred");

...那个电话总是适合Bar.

同样,在您的情况下,Garply<T>使用 with进行的任何调用肯定handler是对的有效调用- 因此编译器很乐意创建适当的实例。HandlerDHandler<T>

Handler仅接受 an时的问题IBar<int>是编译器在推断类型参数时不会对参数使用可能的方法组转换。这是一个类型推断肯定更强大的领域,并且确实在 C# 3 和 C# 4 之间进行了改进——在一个非常相似的领域,尽管我永远记不起细节。

于 2012-12-16T21:23:11.243 回答