Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extension method selection using generic types and Expressions

I have some extension methods that use an Expression parameter to pull in a property member and act on it, and I have an overload for the specific case where the member is an IEnumerable<>. However, it doesn't seem to match the expected method overload when called from inside a generic class (for r4 below). Outside of the class the correct method is selected.

What's going on here? Will this 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";
    }
}
like image 879
Daniel Curtis Avatar asked Sep 03 '13 07:09

Daniel Curtis


People also ask

What is extension method with example?

Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. Extension methods are static methods, but they're called as if they were instance methods on the extended type.

What are the types of extension methods in C#?

The most common extension methods are the LINQ standard query operators that add query functionality to the existing System. Collections. IEnumerable and System. Collections.

What is extension method in MVC?

What is extension method? Extension methods in C# are methods applied to some existing class and they look like regular instance methods. This way we can "extend" existing classes we cannot change. Perhaps the best example of extension methods are HtmlHelper extensions used in ASP.NET MVC.

What is extension method in LINQ?

LinqExtensionMethod.zip. Linq provides standard query operators like filtering, sorting, grouping, aggregation, and concatenations, and it has many operators to achive many types of functionalities, which are called extension methods, in LINQ.


1 Answers

Generics are not dynamic typing. Which overload to call is frozen at compile-time. When the program is later run, even if the variable happens to hold a more specific run-time type, that doesn't matter because the overload was fixed at compile-time.

Your method:

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

has to bind at compile-time to one specific overload of Ext. Since all we know about T (the type of a.Content) in the class MyClass<T> is that it is convertible to object, there is really only one overload to choose from, so this is easy for the compiler.

From then on, the TestExt method body is hard-coded to calling that specific overload of Ext.


EDIT: Here's 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 by now, r becomes "Object".

(If you constrained T somehow, for example where T : IEnumerable<object>, things would be different).

Also this is the same for operators. For example the == operator is overloaded in the sense that it works in one way for general reference types, and in another for strings. So in the example below, operator == takes the role of Overloaded from before:

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.

like image 192
Jeppe Stig Nielsen Avatar answered Oct 16 '22 15:10

Jeppe Stig Nielsen