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

如果我有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&gt ;.) 如果我使用ObjectQuery< T>类型的变量(在命名空间System.Data.Objects中)我有五个可用的重载,即上面的四个(因为ObjectQuery< T>实现IQueryable< T>和其他接口中的IEnumerable< T>)以及此类的实例方法:

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

如果我在使用IQueryable< T>时使用相同的编程错误或ObjectQuery< T>我得到了非常不同的编译错误.这是一个示例程序(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<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>谓词)并且不告诉我在类Test中没有定义“Name” “bla”和“blabla”在目前的背景下是不存在的?

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

作为旁注:我在查看this question时遇到了这个问题.提问者说,问题中的第四个查询没有编译(它在上面的例子3和4中确实存在编译器错误),但这个查询正好他的问题和我的解决方案似乎在查询中写了一些东西(变量或属性名?)(但他没有证实这一点),但是这个编译器错误没有给出有用的指示什么是错误的.

编辑

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

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

> Delegate System.Func不带1个参数
>“lambda expression”无法转换为“string”,因为“string”不是委托类型
>当前上下文中不存在名称“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> class直接实现’IQueryable’,但实现IEnumerable< T>因为IQueryable< T>.

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

相关文章
相关标签/搜索