16

Considering the following code:

IQueryable<T> queryable;

// something to instantiate queryable

var enumerable = (IEnumerable<T>) queryable;

var filtered = enumerable.Where(i => i > 3);

In the final line, which extension method gets called?

Is it IEnumerable<T>.Where(...)? Or will IQueryable<T>.Where(...) be called because the actual implementation is still obviously a queryable?

Presumably the ideal would be for the IQueryable version to be called, in the same way that normal polymorphism will always use the more specific override.

In Visual Studio though when I right-click on the Where method and "Go to Definition" I'm taken to the IEnumerable version, which kind of makes sense from a visual point-of-view.

My main concern is that if somewhere in my app I use Linq to NHibernate to get a Queryable, but I pass it around using an interface that uses the more general IEnumerable signature, I'll lose the wonders of deferred database execution!


Edit: It turns out that, as Iridium has pointed out, it's the Enumerable version that gets called

public class Program
    {
        static void Main(string[] args)
        {
            var myString2 = new MyString2();
            var myString = (MyString)myString2;
            Console.WriteLine(myString.Method());
            Console.ReadLine();
        }
    }

    public class MyString {}

    public class MyString2 : MyString {}

    public static class ExtensionMethods
    {
        public static string Method(this MyString instance)
        {
            return "MyString method";
        }

        public static string Method(this MyString2 instance)
        {
            return "MyString2 method";
        }
    }

The output is "MyString method".

James Morcom
  • 1,749
  • 14
  • 17
  • Forgot to vote up the question... it was interesting to find out :). – Tomas Jansson Jan 07 '11 at 13:26
  • http://stackoverflow.com/questions/474074/overriding-extension-methods – Jahan Zinedine Jan 07 '11 at 13:27
  • 3
    Remember, extension method syntax is just a syntactic sugar for *calling a static method* and static methods by definition *use static analysis to determine what method to call*. If you want *runtime* analysis, you'll need to write the code yourself to do that. Moreover: it is *not* considered desirable for an IQ that is statically an IE to unexpectedly morph back to the IQ code path; presumably you cast it to an IE because you *don't* want to treat it as an IQ! If you want to treat it as an IQ then *don't cast it away*. The Groucho rule: if it hurts when you do that, *don't do that*. – Eric Lippert Jan 07 '11 at 17:28

2 Answers2

10

The currently accepted answer deals with virtual methods, not extension methods.

If you cast an IQueryable to an IEnumerable and then call one of the extension methods (e.g. Where(...) in your question), then the one on Enumerable version will be called, not the Queryable one.

Iridium
  • 23,323
  • 6
  • 52
  • 74
3

When you run this simple program it explains how overloading works :). The answer is that the underlying type's method will be called. And it should work the same way for IQueryable and IEnumerable; that is, the original object's method will be called since even though we cast it something else the actual implementation is of the original type. But when there is an extension method the extension method for the object which is referenced is used, so in that case it depends on the logic in the extension method. The extension method could have come clever logit to see if the object is of an actual type and then cast it back before it executes the method.

class Program
{
    static void Main(string[] args)
    {
        MyString2 myString2 = new MyString2();
        var myString = (MyString)myString2;
        Console.WriteLine(myString); // prints "ToString of MyString2"
        Console.WriteLine(myString.GiveMeTheString()); // prints "GiveMeTheString of MyString2"
        Console.ReadLine();
    }
}

public class MyString
{
    public string MyProperty { get; set; }
    public override string ToString()
    {
        return "ToString of MyString";
    }
}
public class MyString2 : MyString
{
    public string MyProperty { get; set; }
    public override string ToString()
    {
        return "ToString of MyString2";
    }
}

public static class Extensions
{
    public static string GiveMeTheString(this MyString myString)
    {
        return "GiveMeTheString of MyString";
    }
    public static string GiveMeTheString(this MyString2 myString)
    {
        return "GiveMeTheString of MyString2";
    }
}
Tomas Jansson
  • 22,767
  • 13
  • 83
  • 137
  • Thank you Tomas! I knew I should be able to work this out by coding but my brain wasn't working! At least now though someone else searching for the solution hopefully will come across this post. – James Morcom Jan 07 '11 at 12:52
  • I should add - the answer is that the more specific (IQueryable) extension method will be called. – James Morcom Jan 07 '11 at 12:53
  • Happens to the best of us. Remember to vote and accept answer so other people can find the answer more easily; that is, if it is correct. And I'll add that part somewhere in my answer. – Tomas Jansson Jan 07 '11 at 12:53
  • This answer is not correct. For calling overridden virtual methods (as your example does) then yes, this is correct, but this is specifically for extension methods where the rules are different. – Iridium Jan 07 '11 at 13:07
  • @Iridium: It thought that it might exist some exceptions from this, but can you give a counter example? – Tomas Jansson Jan 07 '11 at 13:10
  • 3
    Your example is for virtual methods, the question deals with extension methods, and so your answer does not apply. – Iridium Jan 07 '11 at 13:12
  • 1
    Iridium is correct, sorry Tomas! I've updated my original post with proof. I'll also mark Iridium's answer as correct but Tomas still gets +1 for helping start the code! – James Morcom Jan 07 '11 at 13:17
  • And you both are correct... and I uppdated the example to show the difference just now. – Tomas Jansson Jan 07 '11 at 13:19
  • When I test your example `Console.WriteLine(myString.GiveMeTheString());` prints "GiveMeTheString of MyString"` so @Iridium should be right about virtual vs extension methods; but +1 for great testing example. – Mojtaba Rezaeian Jan 17 '16 at 09:30