两个C#泛型的扩展方法之间暧昧的一个电话,其中T:类等,其中T:结构暧昧、两个、结构、电话

2023-09-02 21:32:37 作者:野性稳江山.

考虑两个扩展方法:

public static T MyExtension<T>(this T o) where T:class
public static T MyExtension<T>(this T o) where T:struct

和一个类:

class MyClass() { ... }

现在调用扩展方法对上述类的一个实例:

Now call the extension method on a instance of the above class:

var o = new MyClass(...);
o.MyExtension(); //compiler error here..
o.MyExtension<MyClass>(); //tried this as well - still compiler error..

编译器说,调用该方法是,当我把它称为一类的暧昧电话。我本来认为它可以决定调用哪个扩展方法,因为MyClass的是一类,而不是一个结构?

The compiler says that calling the method is an ambiguous call when I call it on a class. I would have thought that it could determine which extension method to call, as MyClass is a class, not a struct?

推荐答案

编辑:我现在已经blogged关于此内容更多细节。

I've now blogged about this in more detail.

我原来(现在我认为不正确)认为:通用约束不考虑在重载和类型推断的阶段 - 他们只是用于验证重载解析的结果

My original (and I now believe incorrect) thought: generic constraints aren't taken into account during the overload resolution and type inference phases - they're only used to validate the result of the overload resolution.

编辑:好了,以后的很多的关于这个兜兜,我想我在那里。基本上,我首先想到的是几乎的正确。

Okay, after a lot of going round on this, I think I'm there. Basically my first thought was almost correct.

泛型类型的限制只能采取行动,从候选人中设置的删除方法的非常的一系列情况有限...特别是,只有当一个参数本身的类型是通用的;不只是一个类型参数,而是一个普通的类型,它的使用的泛型类型参数。在这一点上,它的约束类型参数的泛型类型的 的这些验证,而不是通用的方法,我们在调用类型参数的约束。

Generic type constraints only act to remove methods from a candidate set in a very limited set of circumstances... in particular, only when the type of a parameter itself is generic; not just a type parameter, but a generic type which uses a generic type parameter. At that point, it's the constraints on the type parameters of the generic type which are validated, not the constraints on the type parameters of the generic method you're calling.

例如:

// Constraint won't be considered when building the candidate set
void Foo<T>(T value) where T : struct

// The constraint *we express* won't be considered when building the candidate
// set, but then constraint on Nullable<T> will
void Foo<T>(Nullable<T> value) where T : struct

所以,如果你试图调用美孚&LT;对象&gt;(空)上述方法的不会的是候选集的一部分,因为可空&LT;对象&gt;值不符合可空&LT的制约; T&GT; 。如果有任何其他适用的方法,呼叫仍然可以成功。

So if you try to call Foo<object>(null) the above method won't be part of the candidate set, because Nullable<object> value fails to satisfy the constraints of Nullable<T>. If there are any other applicable methods, the call could still succeed.

现在在上面的情况下,该限制是完全一样...但他们并不需要。例如,考虑:

Now in the case above, the constraints are exactly the same... but they needn't be. For example, consider:

class Factory<TItem> where TItem : new()

void Foo<T>(Factory<T> factory) where T : struct

如果你试图调用美孚&LT;对象&gt;(空),该方法仍是候选集的一部分 - 因为当 TItem 对象,pssed在工厂&LT; TItem&GT; 仍持有,和这是的建设候选集时发生了什么检查。如果这被证明是最好的方法,它将再后来验证失败,附近的结束的的7.6.5.1:

If you try to call Foo<object>(null), the method will still be part of the candidate set - because when TItem is object, the constraint expressed in Factory<TItem> still holds, and that's what's checked when building up the candidate set. If this turns out to be the best method, it will then fail validation later, near the end of 7.6.5.1:

如果最好的方法是一个泛型方法,类型实参(提供或推断出的)核对(§4.4.4)在泛型方法声明的约束。如果任何类型的参数不符合该类型参数相应的约束(S),发生绑定时错误。

If the best method is a generic method, the type arguments (supplied or inferred) are checked against the constraints (§4.4.4) declared on the generic method. If any type argument does not satisfy the corresponding constraint(s) on the type parameter, a binding-time error occurs.

Eric的blog帖子包含更多这方面的细节。

Eric's blog post contains more detail on this.