2011-03-23 171 views
2

我在看this问题,我很好奇为什么这deosn't编译。扩展方法与泛型

鉴于此代码,任何人都可以解释为什么IBase.Test()的调用不解析为正确的扩展方法?

public interface IBase { } 

public interface IChildA : IBase { } 

public interface IChildB : IBase { } 

public static class BaseExtensions 
{ 
    public static IBase Test(this IBase self) { return self; } 
    public static T Test<T>(this T self) where T : IChildB { return self; } 
} 

public static class TestClass 
{ 
    public static void Test() 
    { 
     IChildA a = null; //Yeh i know its null but just testing for compile here.. 
     IBase firstTry = a.Test(); //Cannot resolve to BaseExtensions.Test(this IBase obj) 
     IBase secondTry = ((IBase)a).Test(); //Resolves to BaseExtensions.Test(this IBase obj) 

     IChildB b = null; 
     IChildB touchedB = b.Test(); 
    } 
} 

我得到的错误是

Error 166 The type 'IChildA' cannot be used as type parameter 'T' in the generic type or method 'BaseExtensions.Test<T>(T)'. There is no implicit reference conversion from 'IChildA' to 'IChildB'.

我有一种感觉,那是因为这将是ambigous对于任何实现IChildB和不知道使用哪种扩展方法,但错误消息不会呻吟它的一面,如果你删除IBase firstTry = a.Test();线,那么它编译罚款..

+0

您的编译器错误与您发布的代码不符(Test vs Touch)。请发布您发布的*精确*代码的*确切*错误 - 否则我们不会知道您可能做出的其他细微更改。 – 2011-03-23 09:55:07

+0

道歉..固定 – 2011-03-23 09:56:37

+0

另请参阅http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx大约一百人告诉我,我错了,这是一个明智的规则。 – 2013-09-23 21:33:14

回答

5

好吧,问题是,在重载解析过程中,编译器找到将适用的候选方法不含检查方法中指定的通用约束,挑选最具体的约束,然后然后检查通用约束。

在这种情况下,通用的方法是比非通用一个更具体(如类型参数替换后,它有效地与一个IChildA参数,而不是一个IBase参数的方法) - 但是它失败的约束。

我有一个blog post更详细地解释这一点,另一个using it in a horrible way

+0

阿哈,现在有道理,我有一个跆拳道时刻:) – 2011-03-23 10:09:03

+0

我看了第二个链接。就一个问题;为什么参数定义为'T? ignored = default(T?)'而不是'T ignored = default(T)'?我忽略了什么,或者他们做了什么不同的事情?我问这个问题的原因是因为乍一看,人们可能会认为一个可空参数是有效的, – Rob 2011-03-23 10:34:19

+1

@Rob:是的,关键是'T'是不可为空的类型,而'T''是可以为空的版本......'Nullable '上的'T:struct'约束是'T'当它是一个非结构...因为*参数*类型(替换后)检查约束,即使类型参数本身不是。当我在编写恶意代码博客文章时,“快速浏览”永远不是一个好的起点:) – 2011-03-23 10:42:46