Lee*_*son 45 .net c# generics extension-methods
考虑两种扩展方法:
public static T MyExtension<T>(this T o) where T:class
public static T MyExtension<T>(this T o) where T:struct
Run Code Online (Sandbox Code Playgroud)
一节课:
class MyClass() { ... }
Run Code Online (Sandbox Code Playgroud)
现在在上面的类的实例上调用extension方法:
var o = new MyClass(...);
o.MyExtension(); //compiler error here..
o.MyExtension<MyClass>(); //tried this as well - still compiler error..
Run Code Online (Sandbox Code Playgroud)
编译器说当我在类上调用它时调用该方法是一个模糊的调用.我原以为它可以确定调用哪个扩展方法,因为MyClass是一个类,而不是一个结构体?
Jon*_*eet 35
编辑:我现在更详细地在博客上写这篇文章.
我的原始(我现在认为不正确)认为:在重载决策和类型推断阶段不考虑通用约束 - 它们仅用于验证重载决策的结果.
编辑:好的,经过很多努力,我想我就在那里.基本上我的第一个想法几乎是正确的.
通用类型约束仅用于在非常有限的情况下从候选集中移除方法...特别是,仅当参数本身的类型是通用的时; 不仅仅是一个类型参数,而是一个使用泛型类型参数的泛型类型.此时,它是对已验证的泛型类型的类型参数的约束,而不是您正在调用的泛型方法的类型参数的约束.
例如:
// 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
Run Code Online (Sandbox Code Playgroud)
所以如果你试图调用Foo<object>(null)
上面的方法将不会成为候选集的一部分,因为Nullable<object> value
无法满足约束条件Nullable<T>
.如果有任何其他适用的方法,则呼叫仍然可以成功.
现在在上面的例子中,约束完全相同......但它们不一定是.例如,考虑:
class Factory<TItem> where TItem : new()
void Foo<T>(Factory<T> factory) where T : struct
Run Code Online (Sandbox Code Playgroud)
如果你试图调用Foo<object>(null)
,该方法将仍然是候选集的一部分-因为当TItem
是object
,表示对约束Factory<TItem>
仍然成立,这是建立在候选集时发生了什么检查.如果这是最好的方法,那么它将在稍后的7.6.5.1 结束时失败验证:
如果最佳方法是泛型方法,则根据泛型方法上声明的约束(第4.4.4节)检查类型参数(提供或推断).如果任何类型参数不满足类型参数上的相应约束,则会发生绑定时错误.
Eric的博客文章包含更多细节.
Cou*_*y D 10
Eric Lippert 在这里解释得比我更好.
我自己也遇到过这种情况.我的解决方案是
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)
Run Code Online (Sandbox Code Playgroud)
我发现使用默认参数值在 .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();
}
}
Run Code Online (Sandbox Code Playgroud)