如何处理不知道结果是否产生的 IDisposable 对象的 IEnumerable?
How to handle an IEnumerable of IDisposable objects not knowing if the results are yield or not?
我正在寻找处理这种情况的最佳实践/标准。
我们的代码 (MyClass) 使用另一个 class (ItemGenerator)。
ItemGenerator 对我们来说是一个黑盒子,所以我们不知道实现(我们知道但我们不想依赖它,因为它可能会从下面改变)。
ItemGenerator 有一个方法 GetItems(),它 return 是 Item 的一个 IEnumerable。
项目 class 实现了 IDisposable,因此我们应该在完成后释放该对象。
当我们(MyClass)遍历项目列表时,如果发生异常(任何异常),我们希望停止处理并释放控制(将异常冒泡)。
我的问题是:
我们是否应该不断迭代这些项目以处理所有项目?这可能看起来很愚蠢,但如果不处理其余物品会怎样?
同时,根据下面的代码,我们绝对不应该迭代其余项目,因为它们是 yield return。那么为什么要生成它们以便我们可以处理它们(它可能会显着影响性能)。
问题是我们不知道 GetItems() return 是否是按需(产量)项目。而且我认为我们不应该关心,对吗?
那么我们应该如何处理链表中间出现异常的情况(举例)?
下面是说明其要点的代码示例。
这是我们的代码:
public class MyClass
{
public void VerifyAllItems()
{
ItemGenerator generator = new ItemGenerator();
foreach (Item item in generator.GetItems())
{
try
{
// Do some work with "item" here. Though an exception could occur.
// If an exception occurs, we don't care about processing the rest of the items and just want to bubble up the exception
}
finally
{
// Always dispose of the
item?.Dispose();
}
}
}
}
这是黑盒代码
public class ItemGenerator
{
private long _itemsToGenerate = 0;
public ItemGenerator()
{
_itemsToGenerate = new Random().Next(10, 100);
}
public IEnumerable<Item> GetItems()
{
while (_itemsToGenerate > 0)
{
yield return HeavyWork();
_itemsToGenerate--;
}
}
private Item HeavyWork()
{
// Doing a lot of work here
return new Item();
}
}
public class Item : IDisposable
{
private bool _isDisposed = false;
public virtual void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool isDisposing)
{
if (!_isDisposed)
{
if (isDisposing)
{
// Dispose of any resources
}
_isDisposed = true;
}
}
}
问题比你说的还要严重。不仅不能确定是否枚举集合,根本就不能确定是否要处理掉 any 的项目。仅仅因为某些东西实现了 IDisposable
并不意味着你应该处理它,例如如果您的工厂总是 returns 相同的实例。
这种问题正是 分配某些东西的代码通常负责释放它的原因。在这种情况下,ItemGenerator 会创建项目,因此它应该处理它们。
class ItemGenerator : IDiposable
{
protected readonly List<Item> _instances = new List<Item>();
IEnumerable<Item> GetItems()
{
for ( some; condition; here; )
{
var item = new Item();
_instances.Add(item);
yield return item;
}
}
public void Dispose()
{
foreach (var item in _instances) item.Dispose();
}
}
现在您只需将 ItemGenerator 放入 using 块中即可。
public void VerifyAllItems()
{
using (ItemGenerator generator = new ItemGenerator())
{
foreach (Item item in generator.GetItems())
{
try
{
// Do some work with "item" here. Though an exception could occur.
// If an exception occurs, we don't care about processing the rest of the items and just want to bubble up the exception
}
finally
{
//Don't need to dispose anything here
}
}
} //Disposal happens here because of the using statement
}
使用此模式,当您退出 using 块时,由 ItemGenerator 分配的任何项目都将被释放。
现在调用者根本不需要关心项目生成器的实现,或者担心处置生成器本身以外的任何东西。当然,如果您是分配生成器的人,您应该处理掉生成器。
有点奇怪,但是...
internal class DisposingEnumerator : IEnumerator<Item>
{
private readonly List<IDisposable> deallocationQueue = new List<IDisposable>();
private readonly IEnumerable<Item> source;
private IEnumerator<Item> sourceEnumerator;
public DisposingEnumerator(IEnumerable<Item> source)
{
this.source = source;
}
public bool MoveNext()
{
if (sourceEnumerator == null)
{
sourceEnumerator = source.GetEnumerator();
}
bool hasNext = sourceEnumerator.MoveNext();
if (hasNext)
{
deallocationQueue.Add(Current);
}
return hasNext;
}
public Item Current => sourceEnumerator.Current;
object IEnumerator.Current => Current;
public void Reset()
{
throw new NotSupportedException();
}
// Will be called within "foreach" statement
// You can implement IDisposable in ItemCollection as well
public void Dispose()
{
foreach (var item in deallocationQueue)
{
item.Dispose();
}
}
}
public class ItemCollection : IEnumerable<Item>
{
private IEnumerator<Item> enumerator;
public ItemCollection(IEnumerable<Item> source)
{
this.enumerator = new DisposingEnumerator(source);
}
public IEnumerator<Item> GetEnumerator()
{
return enumerator;
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
用法:
var items = generator.GetItems();
// Equivalent of using statement
foreach (var item in new ItemCollection(items))
{
}
我正在寻找处理这种情况的最佳实践/标准。
我们的代码 (MyClass) 使用另一个 class (ItemGenerator)。 ItemGenerator 对我们来说是一个黑盒子,所以我们不知道实现(我们知道但我们不想依赖它,因为它可能会从下面改变)。
ItemGenerator 有一个方法 GetItems(),它 return 是 Item 的一个 IEnumerable。 项目 class 实现了 IDisposable,因此我们应该在完成后释放该对象。
当我们(MyClass)遍历项目列表时,如果发生异常(任何异常),我们希望停止处理并释放控制(将异常冒泡)。
我的问题是:
我们是否应该不断迭代这些项目以处理所有项目?这可能看起来很愚蠢,但如果不处理其余物品会怎样?
同时,根据下面的代码,我们绝对不应该迭代其余项目,因为它们是 yield return。那么为什么要生成它们以便我们可以处理它们(它可能会显着影响性能)。
问题是我们不知道 GetItems() return 是否是按需(产量)项目。而且我认为我们不应该关心,对吗?
那么我们应该如何处理链表中间出现异常的情况(举例)?
下面是说明其要点的代码示例。
这是我们的代码:
public class MyClass
{
public void VerifyAllItems()
{
ItemGenerator generator = new ItemGenerator();
foreach (Item item in generator.GetItems())
{
try
{
// Do some work with "item" here. Though an exception could occur.
// If an exception occurs, we don't care about processing the rest of the items and just want to bubble up the exception
}
finally
{
// Always dispose of the
item?.Dispose();
}
}
}
}
这是黑盒代码
public class ItemGenerator
{
private long _itemsToGenerate = 0;
public ItemGenerator()
{
_itemsToGenerate = new Random().Next(10, 100);
}
public IEnumerable<Item> GetItems()
{
while (_itemsToGenerate > 0)
{
yield return HeavyWork();
_itemsToGenerate--;
}
}
private Item HeavyWork()
{
// Doing a lot of work here
return new Item();
}
}
public class Item : IDisposable
{
private bool _isDisposed = false;
public virtual void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool isDisposing)
{
if (!_isDisposed)
{
if (isDisposing)
{
// Dispose of any resources
}
_isDisposed = true;
}
}
}
问题比你说的还要严重。不仅不能确定是否枚举集合,根本就不能确定是否要处理掉 any 的项目。仅仅因为某些东西实现了 IDisposable
并不意味着你应该处理它,例如如果您的工厂总是 returns 相同的实例。
这种问题正是 分配某些东西的代码通常负责释放它的原因。在这种情况下,ItemGenerator 会创建项目,因此它应该处理它们。
class ItemGenerator : IDiposable
{
protected readonly List<Item> _instances = new List<Item>();
IEnumerable<Item> GetItems()
{
for ( some; condition; here; )
{
var item = new Item();
_instances.Add(item);
yield return item;
}
}
public void Dispose()
{
foreach (var item in _instances) item.Dispose();
}
}
现在您只需将 ItemGenerator 放入 using 块中即可。
public void VerifyAllItems()
{
using (ItemGenerator generator = new ItemGenerator())
{
foreach (Item item in generator.GetItems())
{
try
{
// Do some work with "item" here. Though an exception could occur.
// If an exception occurs, we don't care about processing the rest of the items and just want to bubble up the exception
}
finally
{
//Don't need to dispose anything here
}
}
} //Disposal happens here because of the using statement
}
使用此模式,当您退出 using 块时,由 ItemGenerator 分配的任何项目都将被释放。
现在调用者根本不需要关心项目生成器的实现,或者担心处置生成器本身以外的任何东西。当然,如果您是分配生成器的人,您应该处理掉生成器。
有点奇怪,但是...
internal class DisposingEnumerator : IEnumerator<Item>
{
private readonly List<IDisposable> deallocationQueue = new List<IDisposable>();
private readonly IEnumerable<Item> source;
private IEnumerator<Item> sourceEnumerator;
public DisposingEnumerator(IEnumerable<Item> source)
{
this.source = source;
}
public bool MoveNext()
{
if (sourceEnumerator == null)
{
sourceEnumerator = source.GetEnumerator();
}
bool hasNext = sourceEnumerator.MoveNext();
if (hasNext)
{
deallocationQueue.Add(Current);
}
return hasNext;
}
public Item Current => sourceEnumerator.Current;
object IEnumerator.Current => Current;
public void Reset()
{
throw new NotSupportedException();
}
// Will be called within "foreach" statement
// You can implement IDisposable in ItemCollection as well
public void Dispose()
{
foreach (var item in deallocationQueue)
{
item.Dispose();
}
}
}
public class ItemCollection : IEnumerable<Item>
{
private IEnumerator<Item> enumerator;
public ItemCollection(IEnumerable<Item> source)
{
this.enumerator = new DisposingEnumerator(source);
}
public IEnumerator<Item> GetEnumerator()
{
return enumerator;
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
用法:
var items = generator.GetItems();
// Equivalent of using statement
foreach (var item in new ItemCollection(items))
{
}