.ToArray()的慢LINQ查询

Raj*_*mar 5 c# linq performance winforms

我正在使用以下查询

foreach (var callDetailsForNode_ReArrange in callDetailsForNodes_ReArrange)
{
    var test = from r1 in dtRowForNode.AsEnumerable()
               join r2 in dtFileRowForNode.AsEnumerable()
               on r1.Field<int>("Lng_Upload_Id") equals r2.Field<int>("Lng_Upload_Id")
               where ((r1.Field<string>("Txt_Called_Number") == callDetailsForNode_ReArrange.caller2.ToString()) || r1.Field<string>("Txt_Calling_Number") == callDetailsForNode_ReArrange.caller2.ToString())
               select r2.Field<string>("Txt_File_Name");

    var d = test.Distinct();
}
Run Code Online (Sandbox Code Playgroud)

此时此查询立即运行.但正如我补充道

string[] str =d.ToArray();
strFileName = string.Join(",", str);
Run Code Online (Sandbox Code Playgroud)

运行大约需要4-5秒.什么使它添加如此缓慢.ToArray()

Mar*_*ell 15

此时此查询立即运行.

到目前为止,除了构建表示挂起查询的延迟执行模型之外,它实际上没有任何事情.在你调用迭代器之前,它不会开始迭代,即在你的情况下通过.MoveNext()foreach.ToArray()

所以:它需要时间,因为它正在做工作.

考虑:

static IEnumerable<int> GetData()
{
    Console.WriteLine("a");
    yield return 0;
    Console.WriteLine("b");
    yield return 1;
    Console.WriteLine("c");
    yield return 2;
    Console.WriteLine("d");
}
static void Main()
{
    Console.WriteLine("start");
    var data = GetData();
    Console.WriteLine("got data");
    foreach (var item in data)
        Console.WriteLine(item);
    Console.WriteLine("end");
}
Run Code Online (Sandbox Code Playgroud)

这输出:

start
got data
a
0
b
1
c
2
d
end
Run Code Online (Sandbox Code Playgroud)

需要注意的是如何工作的不一切发生一次-它既是延迟(a说到后got data)和后台(我们没有得到a,... d,0... 2).


相关:这大致是如何Distinct()工作的,来自评论:

public static IEnumerable<T> Distinct<T>(this IEnumerable<T> source) {
    var seen = new HashSet<T>();
    foreach(var item in source) {
        if(seen.Add(item)) yield return item;
    }
}
Run Code Online (Sandbox Code Playgroud)

...

和一个新的Join操作:

public static string Join(this IEnumerable<string> source, string separator) {
    using(var iter = source.GetEnumerator()) {
        if(!iter.MoveNext()) return "";
        var sb = new StringBuilder(iter.Current);
        while(iter.MoveNext())
            sb.Append(separator).Append(iter.Current);
        return sb.ToString();
    }
}
Run Code Online (Sandbox Code Playgroud)

并使用:

string s = d.Join(",");
Run Code Online (Sandbox Code Playgroud)

  • 我不知道即使是方法`Console.WriteLine("a");`中的第一行也不会在`foreach`之前打印出来.所以我会想到`a`在`获取数据'之前出现.Linq有时候仍然令人惊讶. (2认同)

Mat*_*son 12

因为在你迭代它之前查询没有任何作用,这样.ToArray()做.

需要注意的一点是,r2 in dtFileRowForNode.AsEnumerable()当查询开始迭代时,连接的右侧(在您的示例中)将完全枚举,即使只访问结果的第一个元素 - 但不是直到那时.

所以,如果你这样做:

d.First()

r2 in dtFileRowForNode.AsEnumerable()序列将完全迭代(和在存储器中缓冲的),但是只有第一个元素r1 in dtRowForNode.AsEnumerable()将被评估.

因此,如果连接中的一个序列比另一个序列大得多,则将大序列放在连接的左侧会更有效(以内存方式).连接右侧的整个序列将缓冲在内存中.

(我应该指出,只适用于Linq-to-objects.Linq-to-SQL将在数据库中运行这些查询,因此它处理缓冲.)