Choosing an Extension Method Using Common Types and Expressions

I have some extension methods that use the Expression parameter to pull out a property element and act on it, and I have an overload for the specific case where the element is an IEnumerable <> . However, it does not correspond to the expected method overload when called from within the general class (for r4 below). Outside the class, the correct method is selected.

What's going on here? Will it ever work or do I need to find a new approach?

(This is in C # 5)

public class Test { public void MyTest() { // returns "Object" var r1 = new MyClass<object>().Ext(a => a.Content); // returns "Enumerable" var r2 = new MyClass<IEnumerable<object>>().Ext(a => a.Content); // returns "Object" var r3 = new MyClass<object>().TestExt(); // returns "Object" (I was expecting "Enumerable") var r4 = new MyClass<IEnumerable<object>>().TestExt(); // returns "Enumerable" var r5 = new MyClass<int>().TestExt2(); } } public class MyClass<T> { public T Content { get; set; } public IEnumerable<object> OtherContent { get; set; } public string TestExt() { return this.Ext(a => a.Content); } public string TestExt2() { return this.Ext(a => a.OtherContent); } } public static class MyExtensions { public static string Ext<T>(this T obj, Expression<Func<T, IEnumerable<object>>> memberExpression) { return "Enumerable"; } public static string Ext<T>(this T obj, Expression<Func<T, object>> memberExpression) { return "Object"; } } 
+6
source share
1 answer

Generics are not dynamic typing. What overload for the call is frozen at compile time. When the program starts later, even if the variable contains a more specific type of runtime, it does not matter, since the overload was fixed at compile time.

Your method:

 public string TestExt() { return this.Ext(a => a.Content); } 

must be bound at compile time to one specific Ext overload. Since all we know about T ( a.Content type) in the MyClass<T> class is that it converts to object , in fact there is only one overload, so this is easy for the compiler.

From now on, the body of the TestExt method TestExt hard-coded to call this particular Ext overload.


EDIT: here is a much simpler example:

 static void Main() { IEnumerable<object> e = new List<object>(); var r = Generic(e); } static string Generic<T>(T x) { return Overloaded(x); } static string Overloaded(IEnumerable<object> x) { return "Enumerable"; } static string Overloaded(object x) { return "Object"; } 

and, as you understand, r becomes "Object" .

(If you are constrained by T some way, for example where T : IEnumerable<object> , everything will be different).

The same goes for operators. For example, the == operator is overloaded in the sense that it works in one case for generic reference types, and in another for strings. So, in the example below, the == operator plays the role of Overloaded from:

 static void Main() { string str1 = "abc"; string str2 = "a"; str2 += "bc"; // makes sure this is a new instance of "abc" bool b1 = str1 == str2; // true bool b2 = Generic(str1, str2); // false } static bool Generic<T>(T x, T y) where T : class { return x == y; } 

where b2 becomes false .

+4
source

Source: https://habr.com/ru/post/953032/


All Articles