5

我正在使用以下查询

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();
}

到这里为止,这个查询很快就会运行。但正如我添加的

string[] str =d.ToArray();
strFileName = string.Join(",", str);

运行大约需要 4-5 秒。是什么让它添加这么慢.ToArray()

4

3 回答 3

15

到这里为止,这个查询很快就会运行。

到目前为止,它实际上并没有任何事情,只是构建了一个代表待处理查询的延迟执行模型。MoveNext()在您调用迭代器(即 via foreach,在您的情况下为 via )之前,它不会开始迭代.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");
}

这输出:

start
got data
a
0
b
1
c
2
d
end

请注意,这项工作不会同时发生 - 它既是延迟的(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;
    }
}

...

和一个新的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();
    }
}

并使用:

string s = d.Join(",");
于 2013-05-10T12:00:25.280 回答
12

因为在您迭代它之前,查询什么都不做,.ToArray()确实如此。

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

所以如果你这样做了:

d.First()

r2 in dtFileRowForNode.AsEnumerable()序列将被完全迭代(并在内存中缓冲),但只会r1 in dtRowForNode.AsEnumerable()评估第一个元素。

出于这个原因,如果连接中的一个序列比另一个大得多,则将大序列放在连接的左侧会更有效(内存方面)。连接右侧的整个序列将缓冲在内存中。

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

于 2013-05-10T11:59:43.493 回答
2

您需要阅读对linq语句的延迟评估。除非您明确调用结果,否则查询不会完成 - 例如迭代、调用foreach、、ToArray或其他评估查询的方法之一。ToListSumFirst

因此,您的查询需要花费大量时间才能完成,而不是ToArray调用。

于 2013-05-10T12:02:09.753 回答