考虑两种扩展方法:
public static T MyExtension<T>(this T o) where T:class
public static T MyExtension<T>(this T o) where T:struct
一堂课:
class MyClass() { ... }
现在在上述类的实例上调用extension方法:
var o = new MyClass(...);
o.MyExtension(); //compiler error here..
o.MyExtension<MyClass>(); //tried this as well - still compiler error..
编译器说当我在类上调用它时调用该方法是一个模糊的调用。我原以为它可以确定调用哪个扩展方法,因为MyClass是一个类,而不是一个结构?
答案 0 :(得分:36)
编辑:我现在更详细地blogged about this了。
我原来的(我现在认为不正确)认为:在重载决策和类型推断阶段不考虑泛型约束 - 它们仅用于验证重载决策的结果。
编辑:好的,在经历了很多之后,我想我就在那里。基本上我的第一个想法是几乎正确。通用类型约束仅用于从非常有限的环境中的候选集中删除方法...特别是,仅当参数本身的类型是通用的时;不只是一个类型参数,而是使用泛型类型参数的泛型类型。此时,它是对已验证的泛型类型的类型参数的约束,而不是您正在调用的泛型方法的类型参数的约束。
例如:
// 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<object>(null)
上述方法将不会成为候选集的一部分,因为Nullable<object> value
无法满足Nullable<T>
的约束}。如果有任何其他适用的方法,则呼叫仍然可以成功。
现在在上面的例子中,约束完全相同......但它们不一定是。例如,考虑:
class Factory<TItem> where TItem : new()
void Foo<T>(Factory<T> factory) where T : struct
如果您尝试拨打Foo<object>(null)
,该方法仍将是候选集的一部分 - 因为当TItem
为object
时,Factory<TItem>
中表达的约束仍然有效,和那是在构建候选集时检查了什么。如果这是最好的方法,那么它稍后将在7.6.5.1的 end 附近失败验证:
如果最佳方法是泛型方法,则根据泛型方法上声明的约束(第4.4.4节)检查类型参数(提供或推断)。如果任何类型参数不满足类型参数的相应约束,则会发生绑定时错误。
Eric blog post包含更多详细信息。
答案 1 :(得分:10)
Eric Lippert比我更好地解释,here。
我自己也遇到过这种情况。我的解决方案是
public void DoSomthing<T> (T theThing){
if (typeof (T).IsValueType)
DoSomthingWithStruct (theThing);
else
DoSomthingWithClass (theThing);
}
// edit - seems I just lived with boxing
public void DoSomthingWithStruct (object theThing)
public void DoSomthingWithClass(object theThing)
答案 2 :(得分:4)
我发现这种“有趣”奇怪的方式在.NET 4.5中使用默认参数值:)可能对于教育\推测目的比实际使用更有用,但我想展示它:
/// <summary>Special magic class that can be used to differentiate generic extension methods.</summary>
public class MagicValueType<TBase>
where TBase : struct
{
}
/// <summary>Special magic class that can be used to differentiate generic extension methods.</summary>
public class MagicRefType<TBase>
where TBase : class
{
}
struct MyClass1
{
}
class MyClass2
{
}
// Extensions
public static class Extensions
{
// Rainbows and pink unicorns happens here.
public static T Test<T>(this T t, MagicRefType<T> x = null)
where T : class
{
Console.Write("1:" + t.ToString() + " ");
return t;
}
// More magic, other pink unicorns and rainbows.
public static T Test<T>(this T t, MagicValueType<T> x = null)
where T : struct
{
Console.Write("2:" + t.ToString() + " ");
return t;
}
}
class Program
{
static void Main(string[] args)
{
MyClass1 t1 = new MyClass1();
MyClass2 t2 = new MyClass2();
MyClass1 t1result = t1.Test();
Console.WriteLine(t1result.ToString());
MyClass2 t2result = t2.Test();
Console.WriteLine(t2result.ToString());
Console.ReadLine();
}
}