从“system . collections.general”中没有隐式引用转换。列表的“T”

时间:2022-03-18 14:28:17
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((新列表 () {entity})替换更新(新列表 () {entity}),那么就可以了。

It will be ok too when you delete the third method Update<TSub>(TSub entity) where TSub : T.

当您删除第三个方法Update (TSub实体),TSub: T时,它也可以。

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 和一个正式参数U的参数,因此我们推断U是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.

我们有一个类型列表的参数 和一个可IEnumerable的形式参数 。List 实现IEnumerable ,因此我们推断V为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 不能保证是一个派生类的T。

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 。原因是c#团队做了一个深思熟虑的设计决定,潜在的模糊性必须由程序员来解决,而不是编译器。(注意,VB。NET团队做了一个不同的决定,总是尝试一些与程序员的意图一致的明智的东西。

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 和一个正式参数U的参数,因此我们推断U是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.

我们有一个类型列表的参数 和一个可IEnumerable的形式参数 。List 实现IEnumerable ,因此我们推断V为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 不能保证是一个派生类的T。

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 。原因是c#团队做了一个深思熟虑的设计决定,潜在的模糊性必须由程序员来解决,而不是编译器。(注意,VB。NET团队做了一个不同的决定,总是尝试一些与程序员的意图一致的明智的东西。

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.

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