问题描述
考虑两种扩展方法:
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?
推荐答案
我现在 在博客中详细介绍了这一点.
我最初的(现在我认为是错误的)想法:在重载解析和类型推断阶段不考虑泛型约束 - 它们仅用于验证重载解析的结果.
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
所以如果你尝试调用 Foo(null)
上面的方法不会成为候选集的一部分,因为 Nullable;value
不满足 Nullable
的约束.如果有其他适用的方法,调用仍然可以成功.
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
如果您尝试调用 Foo(null)
,该方法仍将是候选集的一部分 - 因为当 TItem
是 object
,Factory
中表达的约束仍然成立,这就是在构建候选集时检查的内容.如果这被证明是最好的方法,那么它会在之后的验证失败,接近 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 节)检查类型参数(提供或推断).如果任何类型参数不满足类型参数的相应约束,则会发生绑定时错误.
埃里克的 博文包含有关此的更多详细信息.
Eric's blog post contains more detail on this.
这篇关于两个 C# 扩展泛型方法之间的不明确调用,一个 where T:class 和另一个 where T:struct的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!