两个C#扩展通用方法之间的模糊调用其中T:class和其他T:struct
考虑两种扩展方法:
public static T MyExtension<T>(this T o) where T:class public static T MyExtension<T>(this T o) where T:struct
还有一堂课:
class MyClass() { ... }
现在在上面的类的一个实例上调用扩展方法:
var o = new MyClass(...); o.MyExtension(); //compiler error here.. o.MyExtension<MyClass>(); //tried this as well - still compiler error..
编译器说调用这个方法在我把它称作一个类时是一个模糊的调用。 我会认为它可以确定调用哪个扩展方法,因为MyClass是一个类,而不是一个结构?
编辑:我现在更详细地博客这个 。
我原来的(我现在认为不正确)认为:通用约束在重载parsing和types推断阶段没有考虑 – 它们只用于validation重载parsing的结果。
编辑:好的,经过了很多这个,我想我在那里。 基本上我的第一个想法几乎是正确的。
genericstypes约束只能用于在非常有限的情况下从候选集合中删除方法……特别是只有当参数types本身是generics的时候才会这样做。 不只是一个types参数,而是一个使用通用types参数的genericstypes。 在这一点上,这是对通用types的types参数进行validation的约束,而不是对所调用的通用方法的types参数的约束。
例如:
// 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>
expression的约束仍然成立, 这就是构build候选组。 如果这是最好的方法,那么在7.6.5.1接近尾声的时候,validation会失败:
如果最好的方法是generics方法,则根据在generics方法中声明的约束(第4.4.4节)来检查types参数(提供或推断)。 如果任何types参数不满足types参数的相应约束,则会发生绑定时错误。
Eric的博客文章包含更多的细节。
Eric Lippert 在这里解释得比我想象的要好。
我自己遇到了这个。 我的解决scheme是
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)
我发现这个“有趣的”奇怪的方式来做到这一点在.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(); } }