.Take() 的行为如何根据我在左侧使用的接口参考而变化。 IQueryable 与 IEnumerable

How does the behavior of .Take() changes based on the interface reference I'm using on left. IQueryable vs IEnumerable

假设我有这些示例代码

IQueryable<Employee> data = context.Employees.Where(x => x.FullName != "Shekar Reddy");                    
    var topEmp = data.Take(1);
      foreach (var item in topEmp)
       {
         Console.WriteLine(item.FullName);
       }

IEnumerable<Employee> data = context.Employees.Where(x => x.FullName != "Shekar Reddy");                    
   var topEmp = data.Take(1);
   foreach (var item in topEmp)
      {
         Console.WriteLine(item.FullName);
      }

它们之间的唯一区别是我使用的是 IQueryable 与 IEnumerable 的引用。 第一个片段正在生成 sql 查询以获取与过滤条件匹配的前 1 项,而后一个片段正在生成没有顶部过滤器的 sql 查询。

在这两种情况下,data 中的对象都是类型 Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable 但是第一个场景中的 topEmp 是类型:Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable 和 第二个是 System.Linq.Enumerable.EnumerablePartition

.Take 方法的行为如何根据左侧的接口引用而改变。我看到“数据”变量在这两种情况下都是相同的类型。根据我的理解,如果左侧引用是 class 那么 .Take of that class 可以被调用;但它们是接口。我确定我在这里遗漏了 C# 的基本概念。

你是对的,你错过了 C# 的一个基本概念,这个概念是扩展方法

我们没有调用 Enumerable 对象实现的抽象 Take 方法。

相反,我们正在调用一个独立于对象存在的函数:Take 扩展方法。根据对象的类型有不同的方法。

在 System.Core 库中存在这些静态 类.

public static class Queryable 
{
    public static IQueryable<TSource> Take<TSource>(
        this IQueryable<TSource> source, 
        int count
    ); 
    // Many other methods...
}


public static class Enumerable
{
    public static IEnumerable<TSource> Take<TSource>(
       this IEnumerable<TSource> source, 
       int count
     );
     // Many other methods...
}

扩展方法在编译时解析,而不是运行时。

您的 data 变量恰好实现了 IQueryable<T>。但是,一旦您将其转换为 IEnumerable<T>,编译器必须选择 IEnumerable<T> 扩展方法。

您没有调用相同的 .Take() 方法。

Enumerable.Take returns 一个新的 IEnumerable 将枚举枚举中的项目并生成它们。

Queryable.Take returns 一个新的 IQueryable,代表一棵 Expression 树。当您开始枚举该可查询时,该表达式将被编译为 SQL.