如果我将 IQueryable 转换为 IEnumerable 然后调用 Linq 扩展方法,将调用哪个实现?

发布于 2024-10-11 00:58:42 字数 1370 浏览 3 评论 0原文

考虑以下代码:

IQueryable<T> queryable;

// something to instantiate queryable

var enumerable = (IEnumerable<T>) queryable;

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

在最后一行中,哪个扩展方法被调用?

IEnumerable.Where(...) 吗?或者是否会调用 IQueryable.Where(...) 因为实际实现显然仍然是可查询的?

据推测,理想的情况是调用 IQueryable 版本,就像正常的多态性将始终使用更具体的覆盖一样。

但在 Visual Studio 中,当我右键单击Where 方法并“转到定义”时,我会进入 IEnumerable 版本,从视觉角度来看,这是有意义的。

我主要担心的是,如果在我的应用程序中的某个地方我使用 Linq to NHibernate 来获取 Queryable,但我使用使用更通用的 IEnumerable 签名的接口来传递它,我将失去延迟数据库执行的奇迹!


编辑:事实证明,正如 Iridium 所指出的那样,它是被调用的 Enumerable 版本,

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";
        }
    }

输出是“MyString 方法”。

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".

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

薄荷港 2024-10-18 00:58:42

当前接受的答案涉及虚拟方法,而不是扩展方法。

如果您将 IQueryable 转换为 IEnumerable,然后调用扩展方法之一(例如问题中的Where(...)),则将调用 Enumerable 版本上的扩展方法,而不是 Queryable 版本。

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.

烈酒灼喉 2024-10-18 00:58:42

当您运行这个简单的程序时,它会解释重载是如何工作的:)。答案是底层类型的方法将被调用。对于 IQueryableIEnumerable 应该以相同的方式工作;也就是说,原始对象的方法将被调用,因为即使我们将其转换为其他内容,实际实现也是原始类型。但是,当存在扩展方法时,将使用所引用对象的扩展方法,因此在这种情况下,它取决于扩展方法中的逻辑。扩展方法可以聪明地 logit 来查看对象是否属于实际类型,然后在执行该方法之前将其强制转换回来。

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";
    }
}

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";
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文