方法重载解析如何工作(LINQ Where扩展方法)?

如果我有一个IQueryable<T>类型的变量IQueryable<T>我可以使用命名空间Systm.Linq Where四个扩展方法:

public static IQueryable<T> Where<T>(this IQueryable<T> source,
    Expression<Func<T, bool>> predicate);
public static IQueryable<T> Where<T>(this IQueryable<T> source,
    Expression<Func<T, int, bool>> predicate);
public static IEnumerable<T> Where<T>(this IEnumerable<T> source,
    Func<T, bool> predicate);
public static IEnumerable<T> Where<T>(this IEnumerable<T> source,
    Func<T, int, bool> predicate);

(后两个是因为IQueryable<T>IEnumerable<T>继承。)

如果我使用类型的变量ObjectQuery<T>命名空间System.Data.Objects )我有五个重载Where可用时,即上述的四(因为ObjectQuery<T>实现IQueryable<T>IEnumerable<T>其他中接口)以及此类的实例方法:

public ObjectQuery<T> Where(string predicate,
    params ObjectParameter[] parameters);

如果我在使用IQueryable<T>ObjectQuery<T>执行相同的编程错误,我会得到非常不同的编译器错误。 下面是一个示例程序(将VS2010 SP1 + System.Data.Entity.dll程序集中的标准C#控制台应用程序模板添加到项目引用中,编译器错误在下面的四个示例中注释):

using System.Data.Objects;
using System.Linq;

namespace OverloadTest
{
    public class Test
    {
        public int Id { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            IQueryable<Test> queryable = null;
            ObjectQuery<Test> objectQuery = null;

            var query1 = queryable.Where(t => t.Name == "XYZ");
            // no definition for "Name" in class OverloadTest.Test

            var query2 = queryable.Where(t => bla == blabla);
            // "bla", "blabla" do not exist in current context

            var query3 = objectQuery.Where(t => t.Name == "XYZ");
            // Delegate System.Func<Overload.Test,int,bool>
            // does not take 1 argument

            var query4 = objectQuery.Where(t => bla == blabla);
            // Delegate System.Func<Overload.Test,int,bool> 
            // does not take 1 argument
        }
    }
}

编译器中的“Squiggles”看起来也不一样:

在这里输入图像描述

我了解前两个错误。 但为什么编译器显然希望在最后两个例子中使用重载数字4(使用Func<T, int, bool> predicate ),并且没有告诉我“Name”没有在Test类中定义,当前情况下不存在“bla”和“blabla”?

我期望编译器可以安全地排除过载编号5(我不传入string作为参数)和过载编号2和4(我没有传入带有两个参数(t,i) => ...的lambda表达式(t,i) => ... )但我的期望似乎并不正确。

作为一个便笺:当我看到这个问题时,我遇到了这个问题。 提问者在那里说,问题中的第四个查询没有编译(它在上面的例子3和4中确实是编译器错误),但是这个查询恰好是他的问题的解决方案,对我来说似乎是某个变量或属性名称?)在查询中写入错误(但他并未确认这一点),但此编译器错误没有提供有用的指示。

编辑

请参阅下面的Martin Harris的非常有用的评论:

在示例query4 ,错误“Delegate System.Func不带1参数”是当我将鼠标悬停在squiggle行上时,工具提示窗口中显示的错误。 在编译器输出窗口中,实际上有四个错误按顺序排列:

  • 代表System.Func不占用1个参数
  • “lambda表达式”不能转换为“字符串”,因为“字符串”不是委托类型
  • 名称“bla”在当前上下文中不存在
  • 名称“blabla”在当前上下文中不存在
  • 但为什么编译器不会抱怨使用IQueryable<T>的前两个示例的第一个错误?


    请阅读,直到结束。

    其实这是因为你的代码有编译器时间错误。

    编译器通过查看代码来检测正确的扩展方法。 在这种情况下,它应该采取一个Test参数并返回bool参数。 由于无法编译linq表达式,因此无法检测到正确的扩展方法,并且编译器会假定它找到的第一个扩展方法是您想要的。

    顺便说一句,如果你修复错误

    var query3 = objectQuery.Where(t => t.Id == 1)
    

    编译器会使用

    public static IQueryable<T> Where<T>(
           this IQueryable<T> source,
           Expression<Func<T, bool>> predicate
    );
    

    现在你应该想知道为什么它会在Enumerable上跳过方法。 这是因为ObjectQuery<T>类直接实现了'IQueryable',但是由于IQueryable<T>实现了IEnumerable<T> IQueryable<T>

    你可以在下面看到对象层次结构

    链接地址: http://www.djcxy.com/p/62101.html

    上一篇: How does method overload resolution work (LINQ Where extension method)?

    下一篇: LLMNR on Windows vs. Zeroconf vs. Bonjour