两个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) ,则该方法仍然是候选集合的一部分 – 因为当TItemobjectFactory<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(); } }