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

如果我有一个类型为IQueryable的变量,我有四个扩展方法,用于命名空间Systm.Linq Where

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

(最后两个因为IQueryableinheritance自IEnumerable 。)

如果我使用ObjectQuery类型的变量(在命名空间System.Data.Objects ),我有五个可用的重载,即上面的四个(因为ObjectQuery实现了IQueryableIEnumerable等等接口)以及此类的实例方法:

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

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

 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 queryable = null; ObjectQuery 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 // does not take 1 argument var query4 = objectQuery.Where(t => bla == blabla); // Delegate System.Func // does not take 1 argument } } } 

“Squiggles”在编译器中看起来也不同:

在此处输入图像描述

我理解前两个错误。 但是为什么编译器显然想要在最后两个例子中使用重载号4(带有Func predicate )并且没有告诉我在类Test没有定义“Name”并且“bla”和“blabla”在目前的背景下是不存在的?

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

作为旁注:我在看这个问题时遇到了这个问题 。 提问者说,问题中的第四个查询没有编译(它在上面的例子3和4中确切地存在编译器错误),但是这个查询正是他的问题的解决方案,对我来说似乎是某个东西(一个变量)或者属性名称?)在查询中写错(虽然他没有确认)但是这个编译器错误并没有给出有用的指示。

编辑

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

在示例query4 ,当我将鼠标hover在波浪线上时,错误“ Delegate System.Func不接受1参数 ”是工具提示窗口中显示的错误。 在编译器输出窗口中,实际上有四个错误:

  • 委托System.Func不带1个参数
  • “lambda expression”无法转换为“string”,因为“string”不是委托类型
  • 当前上下文中不存在名称“bla”
  • 在当前上下文中不存在名称“blabla”

但是为什么编译器没有抱怨使用IQueryable的前两个例子的第一个错误?

请阅读直到结束。

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

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

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

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

编译器将使用

 public static IQueryable Where( this IQueryable source, Expression> predicate ); 

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

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