我正在尋找有關如何處理這種情況的最佳實踐/標準。
我們的代碼 ( MyClass ) 使用另一個類 ( ItemGenerator )。ItemGenerator 對我們來說是一個黑盒,所以我們不知道實作(我們知道,但我們不想依賴它,因為它可以從下面改變)。
ItemGenerator 有一個方法GetItems(),它回傳一個Item的 IEnumerable 。Item 類實作了 IDisposable,因此我們應該在完成后處理該物件。
當我們(MyClass)遍歷專案串列時,如果發生例外(任何例外),我們希望停止處理并釋放控制權(使例外冒泡)。
我的問題是這樣的:
我們是否應該繼續遍歷專案以處理所有專案?這可能看起來很愚蠢,但如果其他物品沒有被處理掉,它們會怎樣呢?
同時,根據下面的代碼,我們絕對不應該遍歷其余的專案,因為它們是收益回報。那么為什么生成它們只是為了我們可以處理它們(它可能會顯著影響性能)。
問題是我們不知道 GetItems() 是否按需回傳專案(產量)。而且我認為我們不應該關心,對吧?
那么我們應該如何處理串列中間出現例外的情況(例如)呢?
下面是說明其要點的代碼示例。
這是我們的代碼:
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;
}
}
}
uj5u.com熱心網友回復:
問題比你說的還要嚴重。您不僅無法確定是否列舉集合,而且根本無法確定是否處理任何專案。僅僅因為某些東西實作了IDisposable并不意味著你應該處理它,例如,如果你有一個總是回傳相同實體的工廠。
這種問題正是為什么分配某些東西的代碼通常要負責釋放它。在這種情況下,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 分配的任何專案都將被處理。
現在呼叫者根本不需要關心專案生成器的實作,或者擔心處理生成器本身以外的任何東西。當然,如果你是分配它的人,你應該處置發電機。
uj5u.com熱心網友回復:
有點奇怪,但是……
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))
{
}
證明
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/341574.html
上一篇:在Regexc#中拆分字串
