Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/330.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 带有where子句的LINQ语句使执行变慢_C#_Linq - Fatal编程技术网

C# 带有where子句的LINQ语句使执行变慢

C# 带有where子句的LINQ语句使执行变慢,c#,linq,C#,Linq,我有一个关于LINQ和where语句的问题。我有以下代码示例(这是我在应用程序中使用的代码的简化版本): //从数据源获取图像。 var images=GetImages();//返回IEnumerable //我将继续处理图像,直到所有内容都已处理完毕。 while(images.Any()) { //我要确定它是什么样的图像,并用它做一些动作。 var image=images.First(); //突然,在我的过程中,我将向我的图像集合添加一个where语句,以获取所有符合指定条件的图像。

我有一个关于LINQ和where语句的问题。我有以下代码示例(这是我在应用程序中使用的代码的简化版本):

//从数据源获取图像。
var images=GetImages();//返回IEnumerable
//我将继续处理图像,直到所有内容都已处理完毕。
while(images.Any())
{
//我要确定它是什么样的图像,并用它做一些动作。
var image=images.First();
//突然,在我的过程中,我将向我的图像集合添加一个where语句,以获取所有符合指定条件的图像。
//如果我的图像集合不是空的,则可能会再次对图像集合执行相同的where语句。
//这也是问题所在,当我不添加ToList()扩展方法时,我的linq语句变得非常慢。
//当我添加ToList()扩展方法时,为什么我的linq语句又运行得很快?
var saveImages=images.Where();/.ToList()这是使我的LINQ查询再次运行所必需的。
//我将对这些保存图像执行一些操作,然后我将从当前图像集合中删除这些保存图像,因为我不再需要使用以下语句执行这些操作。
图像=图像。除了(保存图像);
}
正如代码示例所解释的,当我添加ToList()扩展方法时,为什么我的LINQ语句再次变快。为什么不能只因为返回IEnumerable集合就使用Where语句


我真的很困惑,我希望有人能给我解释:)。

当你通过循环时,你的
图像首先变成了这个

images.Except(firstSetOfExclusions)
那么这个

images.Except(firstSetOfExclusions).Except(secondSetOfExclusions)
images.Except(firstSetOfExclusions).Except(secondSetOfExclusions).Except(thirdSetOfExclusions)
那么这个

images.Except(firstSetOfExclusions).Except(secondSetOfExclusions)
images.Except(firstSetOfExclusions).Except(secondSetOfExclusions).Except(thirdSetOfExclusions)
等等。慢的原因是,除非调用
ToList
,否则每个排除集都必须执行新的查询。随着循环的每次迭代,这会变得越来越慢,因为它会一次又一次地执行相同的查询
ToList
通过在内存中“具体化”查询来修复此问题

请注意,此问题的另一个解决方案是“具体化”新的图像子集,如下所示:

images = images.Except(saveImages).ToList();

这将避免链接“except”,因此您不必在
saveImages

上调用
ToList
,如果我们将LINQ重新实现到对象以显示方法,可能会更有意义;这是我们的
主要内容

static void Main()
{
    Log();
    IEnumerable<int> data = GetData();

    while (data.Any())
    {
        var value = data.First();
        Console.WriteLine("\t\tFound:{0}", value);
        var found = data.Where(i => i == value);
        data = data.Except(found);
    }
}
static IEnumerable<int> GetData()
{
    Log();
    return new[] { 1, 2, 3, 4, 5 };
}
注意到每个项目之间的复杂性是如何增长的吗

要获得额外积分,请将
GetData
设为迭代器块-查看执行了多少次
GetData

static IEnumerable<int> GetData()
{
    Log();
    yield return 1;
    yield return 2;
    yield return 3;
    yield return 4;
    yield return 5;
}
静态IEnumerable GetData()
{
Log();
收益率1;
收益率2;
收益率3;
收益率4;
收益率5;
}
我做了94次(而不是原来版本中的一次)。很有趣吧

这不是LINQ的错——这是因为您使用LINQ非常奇怪。对于您正在做的事情,最好使用平面集合(
List
),根据需要添加和删除项目

这是LINQ:

public static bool Any<T>(this IEnumerable<T> data)
{
    Log();
    using (var iter = data.GetEnumerator())
    {
        return iter.MoveNext();
    }
}
static void Log([CallerMemberName] string name = null)
{
    Console.WriteLine(name);
}
public static T First<T>(this IEnumerable<T> data)
{
    Log();
    using (var iter = data.GetEnumerator())
    {
        if (iter.MoveNext()) return iter.Current;
        throw new InvalidOperationException();
    }
}
public static IEnumerable<T> Where<T>(this IEnumerable<T> data, Func<T,bool> predicate)
{
    Log();
    foreach (var item in data) if (predicate(item)) yield return item;
}
public static IEnumerable<T> Except<T>(this IEnumerable<T> data, IEnumerable<T> except)
{
    Log();
    var exclude = new HashSet<T>(except);
    foreach (var item in data)
    {
        if (!exclude.Contains(item)) yield return item;
    }
}
公共静态bool Any(此IEnumerable数据)
{
Log();
使用(var iter=data.GetEnumerator())
{
返回iter.MoveNext();
}
}
静态无效日志([CallerMemberName]字符串名称=null)
{
Console.WriteLine(名称);
}
公共静态T优先(此IEnumerable数据)
{
Log();
使用(var iter=data.GetEnumerator())
{
if(iter.MoveNext())返回iter.Current;
抛出新的InvalidOperationException();
}
}
公共静态IEnumerable,其中(此IEnumerable数据,Func谓词)
{
Log();
foreach(数据中的var项)if(谓词(项))产生返回项;
}
公共静态IEnumerable Exception(此IEnumerable数据,IEnumerable Exception)
{
Log();
var exclude=新哈希集(除外);
foreach(数据中的var项)
{
如果(!exclude.Contains(item))生成退货项目;
}
}

您正在逐渐使
图像
投影变得越来越复杂。我只想循环一次。。。另外:每次您迭代它时,它可能会迭代任何由
GetImages
组成的内容。。。这可能是一项非常重要的工作,
ToList
只是将可枚举项解析为一个列表。如果对可枚举项进行多次枚举,如果可枚举项本身具有昂贵的迭代(例如数据库查询),这可能是一个好处。
GetImages
中的底层可枚举项是什么?您做了一件非常奇怪的事情,似乎忘记了LINQ使用后期绑定。@MarcGravel我很确定GetImages方法只返回集合一次,因为图像是通过API下载的。所以一切都已经在记忆中了。在GetImages方法中,我准备了一个新的图像列表,并将下载的图像添加到其中(以及我在过程中需要的任何附加数据)。你能给我解释一下为什么我让图像投影变得越来越复杂吗?@MichielPeeters多次迭代序列(而不是数组/列表)已经很“奇怪”,因为它甚至不能保证工作。使用
while(Any)
First
而不是
foreach
是很奇怪的。使用连续嵌套的
多次调整该查询。除了
/
。其中
很奇怪。这里有多个奇怪的级别。是否有可能查看LINQ语句中实际发生的情况?我已经查看/调试了集合,但我找不到在哪里可以真正看到表达式树。@MichielPeeters我不知道它是否可行-我认为很多都是实现LINQ扩展方法的类的私有,和/或嵌入在方法内部本地创建的lambdas中,这使得很难找到。真遗憾。如果我能看到表达式树,也许我会更了解LINQ实际上是如何处理的