【问题标题】:If result of the LINQ has an interface type it means it has a deferred execution如果 LINQ 的结果具有接口类型,则意味着它具有延迟执行
【发布时间】:2018-03-06 11:44:14
【问题描述】:

使用 LINQ 我发现当 LINQ 表达式的结果具有接口类型时,这意味着它具有延迟执行。我说的对吗?

例如:

List<int> numbers = new List<int>() { 1, 2, 3, 4, 5 };

// Deferred execution because res1 has an IEnumerable<int> type
var res1 = numbers.Where(x => x > 2);

// Immediate execution because res2 has an int[] type
var res2 = numbers.Where(x => x > 2).ToArray();

【问题讨论】:

  • 更多的是“立即执行,因为您正在调用ToArray”。 IEnumerable&lt;int&gt; res3 = numbers.ToArray() 将立即执行
  • 这个粗略的假设很有可能是正确的,但它主要是一个实现细节。不要认为这是理所当然的。例如,如果ToList() 返回IList&lt;T&gt;,那么您仍然有一个接口,但它不会被延迟。它甚至并不总是有意义的,例如考虑Empty&lt;T&gt;()
  • @Tim Schmelter 请给我一个例子
  • @HelloWorld:我给了
  • 技术上甚至 var res2 = numbers.Where(x =&gt; x &gt; 2).ToArray(); 使用延迟执行,但它真的无关紧要,因为它只是从 From 调用延迟到 ToArray 调用.延迟执行实际上可以在使用ExpressionDelegate 的任何地方进行。

标签: c# linq


【解决方案1】:

不正确,即使数组实现了IEnumerable&lt;int&gt;。您不知道类型是否使用延迟执行,因为没有IDeferred 接口。

我认为您能做的最好的事情就是尝试将其转换为ICollection&lt;T&gt;ICollection

public static bool IsDeferred<T>(this IEnumerable<T> source) {
    if (source == null) throw new ArgumentNullException(nameof(source));
    ICollection<T> genCollection = source as ICollection<T>;
    if (genCollection != null) return false;
    ICollection collection = source as ICollection;
    if (collection != null) return false;
    return true;
}

var arr = new int[5];
bool deferred = arr.IsDeferred(); // false
IEnumerable<int> seq = arr.Where(i => i != 0);
deferred = seq.IsDeferred();      // true

【讨论】:

    【解决方案2】:

    这是不对的

    如果您有一个 IQueryable,它将创建一个 Expression&lt;Func&lt;T, bool&gt;&gt;,它可以让您延迟执行。你所拥有的是一个表达式。

    如果你有一个 IEnumerable,它可以是一个列表、一个数组或其他任何东西。或者它甚至可以是一个返回 IEnumerable 的函数,你最终会得到一个 Func&lt;T, bool&gt;,它会在你访问它时立即执行。

    如果你有这样的东西,这可能是个陷阱

    private IEnumerable<Test> GetTests()
    { 
       return _context.Set<Test>();  
    }
    

    你做到了

    var tests = GetTests().Take(100);
    

    您实际上在这里有一个Func&lt;T, bool&gt;。您将从 db 中获取所有记录,然后在内存中执行 Take(100)

    如果你这样做

    var tests = GetTests().AsQueryable().Take(100);
    

    现在您有一个延迟执行的Expression&lt;Func&lt;T, bool&gt;&gt;Take(100) 将发生在数据库服务器上

    或者您可以首先返回 IQueryable 而不是 IEnumerable。我只是想说明两者之间的区别。

    【讨论】:

    • "你实际上有一个 Func",在哪里?
    • 测试变量将是Func&lt;Test, bool&gt;,当您执行 Take(100) 时,它将在服务器上执行完全选择,然后执行取入内存。那是因为使用了 IEnumerable 而不是 iQueryable
    • 在您的示例中,testsIEnumerable&lt;Test&gt;
    • 直到你做.Take(100)它没有实现。它仍然是尚未针对服务器执行的Func&lt;T, bool&gt;。当您执行Take() 时,它会执行并获得您的 Enumerable。问题是它会选择所有内容,然后执行 Take,因为您没有表达式。
    • 即使在调用 Take(100) 之后它也没有实现。如果testsFunc&lt;T, bool&gt;,您将无法调用Take,因为Take 在参数中采用IEnumerable&lt;TSource&gt;
    猜你喜欢
    • 2010-12-11
    • 2019-05-15
    • 2015-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多