问题描述
我在实现 IEnumerable 接口的对象池中有以下代码.
I have the following code in an object pool that implements the IEnumerable interface.
public IEnumerable<T> ActiveNodes
{
get
{
for (int i = 0; i < _pool.Count; i++)
{
if (_pool[i].AvailableInPool)
{
yield return _pool[i];
}
}
}
}
据我所知(根据 this 问题),这将生成垃圾作为 IEnumerable需要收集对象._pool 中的任何元素都不会被收集,因为池的目的是保留对所有元素的引用以防止垃圾产生.
As far as I know (according to this question), this will generate garbage as the IEnumerable object will need to be collected. None of the elements in _pool will ever be collected, as the purpose of the pool is to keep references to all of them to prevent garbage creation.
任何人都可以提出一种方法来允许在 _pool 上进行迭代,以免产生垃圾吗?
Can anyone suggest a way to allow iteration over _pool so that no garbage is generated?
当迭代池时,池中所有具有 AvailableInPool == true
的项目都应该被迭代.顺序无关紧要.
When iterating over pool, all of the items in pool that have AvailableInPool == true
should be iterated over. Order doesn't matter.
推荐答案
在任何正常"设计中迭代项目通常会导致创建一个新的可枚举对象.创建和处理对象的速度非常快,因此只有在非常特殊的情况下(低延迟是最优先考虑的)垃圾回收才会(我说可能")成为问题.
Iterating items will in any 'normal' design usually result in the creation of a new enumerable object. Creating and disposing objects is very fast, so only in very special scenarios (where low latency is the top most priority) garbage collections could (I say 'could') be a problem.
通过返回不实现IEnumerable
的结构,可以实现没有垃圾的设计.C# 编译器仍然可以迭代此类对象,因为 foreach
语句使用鸭子类型.例如,.NET 的 List<T>
就采用了这种方法.
A design without garbage is possible by returning structures that don't implement IEnumerable
. The C# compiler can still iterate such objects, because the foreach
statement uses duck typing. .NET's List<T>
, for instance, takes this approach.
当在数组和 List<T>
上使用 foreach
时,不会产生垃圾.在数组上使用 foreach
时,C# 会将操作转换为 for
语句,而 List<T>
已经实现了 struct
枚举器,导致 foreach
不产生垃圾.
When using foreach
over both an array and List<T>
, no garbage will be generated. When using foreach
on an array, C# will transform the operation to a for
statement, while List<T>
already implements a struct
enumerator, causing the foreach
to produce no garbage.
这里是一个struct enumerable和struct enumerator.当您返回可枚举时,C# 编译器可以对其进行 foreach:
Here is a struct enumerable and struct enumerator. When you return the enumerable, the C# compiler can foreach over it:
public struct StructEnumerable<T>
{
private readonly List<T> pool;
public StructEnumerable(List<T> pool)
{
this.pool = pool;
}
public StructEnumerator<T> GetEnumerator()
{
return new StructEnumerator<T>(this.pool);
}
}
这里是StructEnumerator
:
public struct StructEnumerator<T>
{
private readonly List<T> pool;
private int index;
public StructEnumerator(List<T> pool)
{
this.pool = pool;
this.index = 0;
}
public T Current
{
get
{
if (this.pool == null || this.index == 0)
throw new InvalidOperationException();
return this.pool[this.index - 1];
}
}
public bool MoveNext()
{
this.index++;
return this.pool != null && this.pool.Count >= this.index;
}
public void Reset()
{
this.index = 0;
}
}
您可以简单地返回 StructEnumerable<T>
,如下所示:
You can simply return the StructEnumerable<T>
as follows:
public StructEnumerable<T> Items
{
get { return new StructEnumerable<T>(this.pool); }
}
而 C# 可以使用普通的 foreach 对其进行迭代:
And C# can iterate over this with a normal foreach:
foreach (var item in pool.Items)
{
Console.WriteLine(item);
}
请注意,您不能使用 System.Linq.Enumerable
对项目进行 LINQ,为此您需要 IEnumerable<T>
接口,这涉及创建枚举器和,因此,垃圾收集.当然,您可以构建自己的 LINQ 扩展方法,但这不太可能有帮助,因为这通常仍会导致创建新对象(当为使用的委托生成闭包时).
Note that you can't LINQ over the item using System.Linq.Enumerable
You need the IEnumerable<T>
interface for that, and that involves creating enumerators and, therefore, garbage collection. You could, of course, build your own LINQ extension methods, but that will unlikely help, because that will often still result in new objects being created (when closures are being generated for used delegates).
这篇关于允许迭代而不产生任何垃圾的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!