gpt4 book ai didi

c# - 使用通用值类型枚举器跳过 foreach IDisposable 检查

转载 作者:行者123 更新时间:2023-12-05 04:37:17 24 4
gpt4 key购买 nike

我对我试图理解的泛型和约束有一个令人困惑的结果。撇开这是否是个好主意不谈,这段代码:

using System;
using System.Collections.Generic;

public interface IValueEnumerator<T, EnumeratorType> where EnumeratorType : struct
{
T Current { get; }
bool MoveNext();
EnumeratorType GetEnumerator();
}

public struct MyEnumerable : IValueEnumerator<int, MyEnumerable>
{
private int index;
private int count;
public MyEnumerable(int count)
{
this.index = -1;
this.count = count;
}

public bool MoveNext() => ++index < count;
public MyEnumerable GetEnumerator() => this;
public int Current => index;
}

public class Program
{
public static int Iterate<T>(T enumerable) where T : struct, IValueEnumerator<int, T>
{
int z = 0;
foreach (int y in enumerable)
z += y;
return z;
}

public static void Main()
{
var prevMem = System.GC.GetAllocatedBytesForCurrentThread();
MyEnumerable z = new MyEnumerable();
int result = Iterate(z);
long diff = GC.GetAllocatedBytesForCurrentThread() - prevMem;
Console.WriteLine($"Result was {result}");
Console.WriteLine($"{diff} extra bytes allocated");
}
}

产生这个输出:

Result was 0
24 extra bytes allocated

内部Iterate foreach 循环正在生成代码来装箱枚举器并检查它是否实现了 IDisposable .装箱导致分配。

如果我更改 Iterate 的类型约束成为where T : struct, IValueEnumerator<int, MyEnumerable>编译器似乎认识到迭代器无法实现 IDisposable 并且它跳过装箱并且我没有分配:

Result was 0
0 extra bytes allocated

我试过各种编译器,它们都有相同的行为。

这是设计使然吗?我认为原始约束子句足以让编译器不生成 IDisposable查看。是否有我可以添加的额外约束,以便它仍然是通用的,但编译器不实现 IDisposable检查 foreach,或者至少不进行装箱?

最佳答案

好的,我已经弄明白了,包括它发生的原因和修复方法。

首先,这只会发生在调试中,不会发生在发布中。在发行版中,编译器似乎足够聪明,可以避免额外的 IDisposable检查。

接下来,where T : struct, IValueEnumerator<int, MyEnumerable>约束告诉编译器 T 将是一个值类型,但没有告诉它 T 不会IDisposasble .在调试中,似乎相同的通用函数必须能够在约束允许的任何类型上运行,并且其中一些类型可能会实现 IDisposable有些人可能不会。对编译器来说阻力最小的方法是将值装箱并检查它是否是 IDisposable。 .

不幸的是没有什么比where T : not IDisposable向编译器保证它不必处理枚举器。然而,我们可以走另一条路,让接口(interface)实现 IDisposable .然后编译器知道 T 是一次性的,它可以调用 Dispose()在它上面而不先装箱。

也就是说,如果界面如下所示,它不会在调试中导致任何装箱或额外分配:

public interface IValueEnumerator<T, EnumeratorType> : IDisposable
where EnumeratorType : struct, IDisposable
{
T Current { get; }
bool MoveNext();
EnumeratorType GetEnumerator();
}

实现结构必须添加一个空的 Dispose() foreach 将在其迭代后调用的方法,这比编译器知道枚举器肯定不实现时效率低 IDisposable并且可以跳过所有这些。但它确实避免了任何装箱并且没有进行任何分配。

关于c# - 使用通用值类型枚举器跳过 foreach IDisposable 检查,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70717309/

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