gpt4 book ai didi

c# - 使用 Union 和 Select 时,LINQ 没有在我的 IEnumerator 上调用 Dispose,这是预期的行为还是错误?

转载 作者:太空狗 更新时间:2023-10-29 18:30:41 26 4
gpt4 key购买 nike

我的理解是,LINQ IEnumerable 扩展应该在 IEnumerable 生成的 IEnumerator 上调用 Dispose。参见 this answer on SO .但是,我在实践中没有看到这一点。其他 SO 答案不正确,还是我在 LINQ 中发现了错误?

这是一个最小的 reproduction使用 Mono 的问题。它还在 Dotnet Core 2.1 和 2.2 中重现。

using System;
using System.Threading;
using System.Linq;
using System.Collections.Generic;
using System.Collections;


namespace union
{
class MyEnumerable : IEnumerable<long>
{
public IEnumerator<long> GetEnumerator()
{
return new MyEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

class MyEnumerator : IEnumerator<long>
{
public long Current
{
get
{
return 0;
}
}

object IEnumerator.Current
{
get
{
return Current;
}
}

public bool MoveNext()
{
return false;
}

public void Reset()
{
return;
}

public void Dispose()
{
Console.WriteLine("I got disposed");
}
}

class Program
{
static void Main(string[] args)
{
var enum1 = new MyEnumerable();
var enum2 = new MyEnumerable();

enum1.Union(enum2).Select(x => x + 1).ToList();
Console.WriteLine("All done!");
}
}
}

如果 Dispose 被调用,您会在控制台上看到 I got disposed 两次。相反,您不会得到任何 I got disposed。需要 UnionSelect 才能重现问题。

有哪位 C# 高手知道我是否遇到了错误?

更新:

我认为这是一个错误,我已提交:https://github.com/dotnet/corefx/issues/40384 .

最佳答案

我在 .NET Fiddle 中测试过,它允许您使用三个编译器之一,结果证实了您的发现:

似乎 Mono 也没有。

但我想我缩小了范围:当第一次调用 MoveNext() 返回 false 时,它​​不会调用 Dispose() (即集合中没有项目)。

你会看到这个,例如,如果你用这个替换你的 MoveNext() 代码,它只在第一次返回 truefalse 此后(即模拟包含 1 个项目的集合):

private bool happenedOnce = false;
public bool MoveNext()
{
if (happenedOnce) return false;
happenedOnce = true;
return true;
}

这是 Mono 中的示例和 .NET Core 2.2 .

我想有人会说这不是错误,而是一项功能。如果集合中没有任何内容,那有什么可以处理的? (从头开始 - 文件等......)

但话又说回来,这是不一致的。例如,这将为您提供完全相同的结果,但它处理掉,即使集合中没有任何内容也是如此:

enum1.Select(x => x + 1).Union(enum2.Select(x => x + 1)).ToList();

但这只是强制它使用 Select() 而不是 Union() 来迭代集合。所以这听起来像是 Union() 实现中的错误。

我今天有时间(家人外出拜访 friend )所以我花时间去了debug the .NET Core source .我相信问题是对的here :

if (enumerator.MoveNext())
{
SetEnumerator(enumerator);
StoreFirst();
return true;
}

SetEnumerator() 将枚举器存储在一个实例变量中。您会看到它只有在 MoveNext() 返回 true 时才会被调用。后来,在Dispose() ,如果迭代器不是 null,它只会调用迭代器上的 Dispose()。但由于它从未被设置,它确实是 null

将其与 Select() 的实现进行对比,它在 调用 MoveNext() 之前设置其 _enumerator 变量。

.NET Framework 仅依赖于 foreach,如您所见 here .奇怪的是,so does Mono ,所以我不知道上面是什么。

关于c# - 使用 Union 和 Select 时,LINQ 没有在我的 IEnumerator 上调用 Dispose,这是预期的行为还是错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57532426/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com