An ambiguous call between two universal C # extension methods, where T: class and others, where T: struct

Consider two extension methods:

public static T MyExtension<T>(this T o) where T:class public static T MyExtension<T>(this T o) where T:struct 

And the class:

 class MyClass() { ... } 

Now call the extension method on the instance of the specified class:

 var o = new MyClass(...); o.MyExtension(); //compiler error here.. o.MyExtension<MyClass>(); //tried this as well - still compiler error.. 

The compiler says that a method call is an ambiguous call when I call it in a class. I would think that it can determine which extension method to call, since MyClass is a class, not a structure?

+43
generics c # extension-methods
Oct. 25 '10 at 11:19
source share
3 answers

EDIT: Now I wrote about this in more detail.




My original (and I now consider it incorrect) thought: general restrictions are not taken into account when resolving overloads and the type of output - they are used only to check the result of resolving overloads.

EDIT: Well, after a lot on this, I think I'm there. Basically my first thought was almost correct.

Constraints of a general type are only valid for removing methods from a set of candidates in a very limited set of circumstances ... in particular, only when the type of the parameter itself is general; not just a type parameter, but a generic type that uses a type type parameter. At this point, these are restrictions on parameters of the type of the general type that are checked, and not restrictions on the parameters of the type of the general method that you call.

For example:

 // 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 

Therefore, if you try to call Foo<object>(null) , the above method will not be part of the candidate set, because the Nullable<object> value does not satisfy the Nullable<T> constraints. If there are other applicable methods, the call can still be successful.

Now in the case above, the restrictions are the same ... but they do not have to be. For example, consider:

 class Factory<TItem> where TItem : new() void Foo<T>(Factory<T> factory) where T : struct 

If you try to call Foo<object>(null) , this method will still be part of the candidate set, because when TItem is object , the constraint expressed in Factory<TItem> is preserved, and this is what the candidate is checked when creating the candidate . If this proves to be the best method, he will later complete the test, towards the end of 7.6.5.1:

If the best method is a general method, type arguments (supplied or deduced) are checked for constraints (Β§4.4.4) declared in the general method. If any type argument does not satisfy the corresponding restriction (s) in the type parameter, a binding time error occurs.

Eric 's blog post contains more information about this.

+34
Oct 25 '10 at 11:23
source share

Eric Lippert explains better than ever here .

I myself came across this. My decision was

 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) 
+10
Oct 25 '10 at 12:11
source share

I found this β€œinteresting” weird way to do it in .NET 4.5 using default parameter values ​​:) Perhaps this is more useful for educational / speculative purposes than for real use, but I would like to show this:

 /// <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(); } } 
+4
Jan 31 '13 at 13:11
source share



All Articles