C#编译器允许接口代替通用派生版本?

Jas*_*unt 2 visual-studio-2010 .net-2.0 c#-4.0

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

    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) { }
    }
Run Code Online (Sandbox Code Playgroud)

我的问题是我不希望代码编译,但确实如此.我不希望它编译,因为签名中DHandler<int>需要IBar<int>,但Handler方法声明IFoo,这不是 IBar<int>(虽然反过来是真的).因此Handler不是a DHandler<int>,因此它的委托不能用作Garply<int>调用的参数.

如果我改变代码来读取Handler(IBar<int> arg)它编译.如果我改变它来阅读Handler(IBar<string> arg)它没有.这些行为都是我所期望的.

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

Jon*_*eet 8

这是委托方差,这是在C#2.这是引入一样的通用在C#4引入方差.

这是一个更简单的例子:

delegate void Foo(string x);    

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

    static void Bar(object y) {}
}
Run Code Online (Sandbox Code Playgroud)

关键是我们可以FooBar方法中创建委托的实例,因为Bar只要它被赋予任何 就可以工作object.当一个Foo委托被调用时,它将始终提供一个string引用,并且有一个引用转换stringobject.所以,如果我有:

Foo f = ...;
f("fred");
Run Code Online (Sandbox Code Playgroud)

......这个电话总是适合的Bar.

同样,在你的情况下,任何呼叫Garply<T>用做handler绝对是一个有效的电话Handler-这样的编译器是乐于创造一个合适的DHandler<T>实例.

Handler仅接受a 时的问题IBar<int>是,在推断类型参数时,编译器不会对参数使用可能的方法组转换.这是一个类型推断当然可能更强,并且实际上它得到改善的领域 - 在一个非常相似的领域,虽然我永远记不起细节 - 在C#3和C#4之间.