2013-03-22 55 views
19
class Class1<T> 
{ 
    public virtual void Update(T entity) 
    { 
     Update(new List<T>() { entity }); //It's failed 
    } 

    public virtual void Update(IEnumerable<T> entities) 
    { 
    } 

    public virtual void Update<TSub>(TSub entity) where TSub : T 
    { 
    } 

    public virtual void Update<TSub>(IEnumerable<TSub> entities) where TSub : T 
    { 
    } 
} 

我有一段代码没有隐式引用转换。但它总是失败。有从“System.Collections.Generic.List <T>”到“T”

如果我用Update((new List<T>() { entity }).AsEnumerable())代替Update(new List<T>() { entity }),它会没事的。

删除第三种方法Update<TSub>(TSub entity) where TSub : T也可以。

有人可以告诉我为什么吗?

+7

您闯入了C#规范的黑暗部分 - 重载分辨率。结合泛型,'params',继承,多态性,通用约束,'dynamic',可选参数 - 使我的听力率提高http://msdn.microsoft.com/en-us/library/aa691336(v=vs.71 )的.aspx。 +1为好问题btw – 2013-03-22 10:50:26

回答

2

你实质上是在问为什么编译器没有创建从List<T>IEnumerable<T>的隐式转换。原因在于C#团队做出了一个慎重的设计决定,即潜在模糊性的案例必须由程序员解决,而不是由编译器解决。 (请注意,VB.NET团队做出了不同的决定,总是试图理智的东西是有感知的程序员意图一致)

像这样的情况下的优点是惊喜最小化 - 没有什么意外都可能发生在封面下;缺点是偶尔需要更详细的代码。

+1

它实际上是从'List '转换成'IEnumerable ' – Rik 2013-03-22 11:34:47

12

约束不是签名的一部分,Eric Lippert对此主题有很好的article

+0

+1。我找不到那篇文章。 – 2013-03-22 11:14:29

17

好的,我们仔细研究一下。我们有

Update(new List<T>()); 

而三位候选人 - 注意,我们只关心签名的考生,所以我们将剥去的返回类型和限制,这是未签名的一部分:

Update(IEnumerable<T> entities) 
Update<U>(U entity) 
Update<V>(IEnumerable<V> entities) 

我们的首要任务是做类型推断这些最后两个人选。如果推论失败,那么他们不适用。

考虑第二种方法

Update<U>(U entity) 

我们List<T>类型的参数和形式参数U。因此我们推断UList<T>

考虑第三种方法:

Update<V>(IEnumerable<V> entities) 

我们List<T>类型的参数和IEnumerable<V>类型的形式参数。 List<T>实现IEnumerable<T>因此我们推断,V是T.

OK,所以我们的候选人名单目前包括:

Update(IEnumerable<T> entities) 
Update<List<T>>(List<T> entity) 
Update<T>(IEnumerable<T> entities) 

是否所有这些候选人适用的?是。在每种情况下,List<T>都可以转换为正式的参数类型。我们无法消除其中的任何一个。

既然我们只有适用的候选人,我们必须确定哪一个是唯一最好的

我们可以立即消除第三个。第三个和第一个在它们的形式参数列表中是相同的。 C#的规则是当你在形式参数列表中有两个相同的方法时,其中一个“自然地”到达那里,其中一个通过类型替换到达那里,被替换的那个丢失。

我们也可以消除第一个。显然,第二个中的精确匹配比第一个中的精确匹配要好。

这留下第二个人作为最后一个人站立。它赢得重载分辨率的战斗。然后在最终验证期间,我们发现违反了约束条件:List<T>不保证是派生类T

因此重载解析失败。你的论点导致选择的最好的方法是无效的。

如果我打电话Update((new List<T>() { entity }).AsEnumerable()),它会没事的。

正确的。再次通过它。三名候选人:

Update(IEnumerable<T> entities) 
Update<U>(U entity) 
Update<V>(IEnumerable<V> entities) 

我们IEnumerable<T>类型的参数,所以我们推断第二和第三个是:

Update(IEnumerable<T> entities) 
Update<IEnumerable<T>>(IEnumerable<T> entity) 
Update<T>(IEnumerable<T> entities) 

现在我们有相同的参数列出了三个候选人适用。那些在建的地方自然比自然地方差,所以我们排除了第二和第三,只剩下第一个。它赢了,它没有任何限制被侵犯。

过,当你删除第三个方法

你的说法是错误的马上就好了;这将产生与第一种情况相同的错误。将第三位候选人取走不会导致第一位候选人突然开始击败第二位候选人。

+0

我仍然不明白为什么编译器需要“猜测”,如果我们让约束成为方法签名的一部分。编译器检查第三种方法(更新(...)),然后推断通用类型为List ,然后发现列表不是T,则消除第三个方法。 – 2013-03-23 05:29:20

+0

@MonghongLin:参见Andrew的回答中引用的文章。我试图解释为什么限制作为签名的一部分,使得超负荷解决的结果变得更糟,几乎没有人同意我的观点。 – 2013-03-23 06:36:31

+0

@ eric-lippert,谢谢你的回复。现在对我来说很清楚。但我还有一个问题。 '删除第三种方法时也可以。'当我完全删除第三种方法时,我不会收到错误。你能告诉我理由吗? – 2013-03-25 06:19:01

相关问题