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
{
}
}
I have a piece of code. But it always failed.
我有一段代码。但它总是失败了。
If I replaced Update(new List<T>() { entity })
by Update((new List<T>() { entity }).AsEnumerable())
, it will be ok.
如果我用Update((新列表
It will be ok too when you delete the third method Update<TSub>(TSub entity) where TSub : T
.
当您删除第三个方法Update
Can anybody tell me why?
有人能告诉我为什么吗?
3 个解决方案
#1
17
OK, let's go through this carefully. We have
好的,让我们仔细检查一下。我们有
Update(new List<T>());
And three candidates -- note that we care only about the signatures of those candidates, so we'll strip away the return types and constraints, which are not part of the signature:
还有三个候选人——注意,我们只关心那些候选人的签名,所以我们会去掉那些不属于签名的返回类型和约束:
Update(IEnumerable<T> entities)
Update<U>(U entity)
Update<V>(IEnumerable<V> entities)
Our first task is to do type inference on those last two candidates. If inference fails then they are not applicable candidates.
我们的第一个任务是对最后两位候选人进行类型推断。如果推理失败,他们就不是合适的候选人。
Consider the second method
考虑第二种方法
Update<U>(U entity)
We have an argument of type List<T>
and a formal parameter U
. Therefore we infer that U
is List<T>
.
我们有一个类型List
Consider the third method:
考虑第三种方法:
Update<V>(IEnumerable<V> entities)
We have an argument of type List<T>
and a formal parameter of type IEnumerable<V>
. List<T>
implements IEnumerable<T>
so we deduce that V is T.
我们有一个类型列表的参数
OK, so our candidate list now consists of:
好的,所以我们的候选人名单现在包括:
Update(IEnumerable<T> entities)
Update<List<T>>(List<T> entity)
Update<T>(IEnumerable<T> entities)
Are all of these candidates applicable? Yes. In each case List<T>
is convertible to the formal parameter type. We cannot eliminate any of them yet.
所有这些候选人都适用吗?是的。在每个case列表中,
Now that we have only applicable candidates we must determine which one is the unique best.
既然我们只有合适的候选人,我们必须确定哪一个是最独特的。
We can immediately eliminate the third one. The third one and the first one are identical in their formal parameter lists. The rule of C# is that when you have two methods that are identical in their formal parameter lists, and one of them got there "naturally" and one of them got there via type substitution, the substituted one loses.
我们可以马上去掉第三个。第三个和第一个在正式的参数列表中是相同的。c#的规则是,当你有两种方法在它们的正式参数列表中是相同的,其中一个是“自然”的,其中一个是通过类型替换得到的,替代的是丢失的。
We can also eliminate the first one. Clearly the exact match in the second one is better than the inexact match in the first one.
我们也可以消去第一个。很明显,在第二场比赛中,准确的比赛比第一次的不精确的比赛要好。
That leaves the second one as the last man standing. It wins the overload resolution fight. Then during final validation we discover that the constraint is violated: List<T>
is not guaranteed to be a derived class of T
.
这让第二个人成为最后一个站着的人。它赢得了重载决战。在最后的验证过程中,我们发现约束被违反:List
Therefore overload resolution fails. Your arguments caused the best method chosen to be invalid.
因此重载解析失败。您的参数导致了选择无效的最佳方法。
If I call
Update((new List<T>() { entity }).AsEnumerable())
, it will be ok.如果我调用Update((新列表
() {entity}). asenumerable()),它将是ok的。
Correct. Go through it again. Three candidates:
正确的。经历一遍。三位候选人:
Update(IEnumerable<T> entities)
Update<U>(U entity)
Update<V>(IEnumerable<V> entities)
We have an argument of type IEnumerable<T>
, so we infer the second and third to be:
我们有一个类型IEnumerable
Update(IEnumerable<T> entities)
Update<IEnumerable<T>>(IEnumerable<T> entity)
Update<T>(IEnumerable<T> entities)
Now we have three applicable candidates with identical parameter lists. The ones that got there under construction are automatically worse than the natural ones, so we eliminate the second and third, leaving only the first. It wins, and it has no constraints to be violated.
现在,我们有三个相同的参数列表。那些在施工中得到的比天然的更糟,所以我们淘汰了第二和第三,只留下第一个。它赢了,而且它没有被违反的约束。
It will be ok too when you delete the third method
删除第三种方法也可以。
Your statement is false; this will produce the same error as the first scenario. Taking away the third candidate does not cause the first candidate to suddenly start beating the second candidate.
你的语句是错误的;这将产生与第一个场景相同的错误。拿走第三个候选人不会使第一个候选人突然开始击败第二个候选人。
#2
12
Constraints are not part of the signature, Eric Lippert has a great article about this topic.
约束不是签名的一部分,Eric Lippert有一篇关于这个主题的伟大文章。
#3
2
You are essentially asking why the compiler is not creating an implicit cast from List<T>
to IEnumerable<T>
. The reason is that the C# team made a deliberate design decision that cases of potential ambiguity must be resolved by the programmer, not the compiler. (Note that the VB.NET team made a different decision, to always attempt something sensible that is consistent with the perceived programmer intent.)
本质上,您是在问为什么编译器没有从List< t>中创建隐式转换到IEnumerable
The advantages in a case such as this are that surprise is minimized - nothing unexpected can happen under the covers; the disadvantage is the occasional need for more verbose code.
在这种情况下的优势是,惊喜是最小化的-没有什么意外可以发生在封面;缺点是偶尔需要更详细的代码。
#1
17
OK, let's go through this carefully. We have
好的,让我们仔细检查一下。我们有
Update(new List<T>());
And three candidates -- note that we care only about the signatures of those candidates, so we'll strip away the return types and constraints, which are not part of the signature:
还有三个候选人——注意,我们只关心那些候选人的签名,所以我们会去掉那些不属于签名的返回类型和约束:
Update(IEnumerable<T> entities)
Update<U>(U entity)
Update<V>(IEnumerable<V> entities)
Our first task is to do type inference on those last two candidates. If inference fails then they are not applicable candidates.
我们的第一个任务是对最后两位候选人进行类型推断。如果推理失败,他们就不是合适的候选人。
Consider the second method
考虑第二种方法
Update<U>(U entity)
We have an argument of type List<T>
and a formal parameter U
. Therefore we infer that U
is List<T>
.
我们有一个类型List
Consider the third method:
考虑第三种方法:
Update<V>(IEnumerable<V> entities)
We have an argument of type List<T>
and a formal parameter of type IEnumerable<V>
. List<T>
implements IEnumerable<T>
so we deduce that V is T.
我们有一个类型列表的参数
OK, so our candidate list now consists of:
好的,所以我们的候选人名单现在包括:
Update(IEnumerable<T> entities)
Update<List<T>>(List<T> entity)
Update<T>(IEnumerable<T> entities)
Are all of these candidates applicable? Yes. In each case List<T>
is convertible to the formal parameter type. We cannot eliminate any of them yet.
所有这些候选人都适用吗?是的。在每个case列表中,
Now that we have only applicable candidates we must determine which one is the unique best.
既然我们只有合适的候选人,我们必须确定哪一个是最独特的。
We can immediately eliminate the third one. The third one and the first one are identical in their formal parameter lists. The rule of C# is that when you have two methods that are identical in their formal parameter lists, and one of them got there "naturally" and one of them got there via type substitution, the substituted one loses.
我们可以马上去掉第三个。第三个和第一个在正式的参数列表中是相同的。c#的规则是,当你有两种方法在它们的正式参数列表中是相同的,其中一个是“自然”的,其中一个是通过类型替换得到的,替代的是丢失的。
We can also eliminate the first one. Clearly the exact match in the second one is better than the inexact match in the first one.
我们也可以消去第一个。很明显,在第二场比赛中,准确的比赛比第一次的不精确的比赛要好。
That leaves the second one as the last man standing. It wins the overload resolution fight. Then during final validation we discover that the constraint is violated: List<T>
is not guaranteed to be a derived class of T
.
这让第二个人成为最后一个站着的人。它赢得了重载决战。在最后的验证过程中,我们发现约束被违反:List
Therefore overload resolution fails. Your arguments caused the best method chosen to be invalid.
因此重载解析失败。您的参数导致了选择无效的最佳方法。
If I call
Update((new List<T>() { entity }).AsEnumerable())
, it will be ok.如果我调用Update((新列表
() {entity}). asenumerable()),它将是ok的。
Correct. Go through it again. Three candidates:
正确的。经历一遍。三位候选人:
Update(IEnumerable<T> entities)
Update<U>(U entity)
Update<V>(IEnumerable<V> entities)
We have an argument of type IEnumerable<T>
, so we infer the second and third to be:
我们有一个类型IEnumerable
Update(IEnumerable<T> entities)
Update<IEnumerable<T>>(IEnumerable<T> entity)
Update<T>(IEnumerable<T> entities)
Now we have three applicable candidates with identical parameter lists. The ones that got there under construction are automatically worse than the natural ones, so we eliminate the second and third, leaving only the first. It wins, and it has no constraints to be violated.
现在,我们有三个相同的参数列表。那些在施工中得到的比天然的更糟,所以我们淘汰了第二和第三,只留下第一个。它赢了,而且它没有被违反的约束。
It will be ok too when you delete the third method
删除第三种方法也可以。
Your statement is false; this will produce the same error as the first scenario. Taking away the third candidate does not cause the first candidate to suddenly start beating the second candidate.
你的语句是错误的;这将产生与第一个场景相同的错误。拿走第三个候选人不会使第一个候选人突然开始击败第二个候选人。
#2
12
Constraints are not part of the signature, Eric Lippert has a great article about this topic.
约束不是签名的一部分,Eric Lippert有一篇关于这个主题的伟大文章。
#3
2
You are essentially asking why the compiler is not creating an implicit cast from List<T>
to IEnumerable<T>
. The reason is that the C# team made a deliberate design decision that cases of potential ambiguity must be resolved by the programmer, not the compiler. (Note that the VB.NET team made a different decision, to always attempt something sensible that is consistent with the perceived programmer intent.)
本质上,您是在问为什么编译器没有从List< t>中创建隐式转换到IEnumerable
The advantages in a case such as this are that surprise is minimized - nothing unexpected can happen under the covers; the disadvantage is the occasional need for more verbose code.
在这种情况下的优势是,惊喜是最小化的-没有什么意外可以发生在封面;缺点是偶尔需要更详细的代码。