[英]Is it possible to have a 'finally' code-block for yield-return methods?
背景
嗨,大家好。 我有一個稱為BaseRecordFetcher<TEntity>
的抽象類,它有一個方法從子類中獲取read / sort / translate / move-next方法,並且yield以模型實體的形式返回結果。
當我讀取多個數據行時,它會正確地為每個實體yield return
,然后在do...while
之后到達Trace消息, do...while
不會出現任何問題。
問題
但是,我注意到,當我在集合上使用IEnumerable.FirstOrDefault()
時,系統假定不再需要任何收益率回報,並且它不會完成該方法的執行 !
除了返回多少記錄的跟蹤輸出外,我對這種行為沒有太大的疑問,但是這讓我想到:“如果我確實需要一些……讓我們將其finally
稱為代碼,該怎么辦? ”
題
是否有辦法始終確保yield return
后系統運行一些后處理代碼?
碼
/// <summary>
/// Retrieve translated entities from the database. The methods used to do
/// this are specified from the child class as parameters (i.e. Action or
/// Func delegates).
/// </summary>
/// <param name="loadSubsetFunc">
/// Specify how to load a set of database records. Return boolean
/// confirmation that records were found.
/// </param>
/// <param name="preIterationAction">
/// Specify what should happen to sort the results.
/// </param>
/// <param name="translateRowFunc">
/// Specify how a database record should translate to a model entity.
/// Return the new entity.
/// </param>
/// <param name="moveNextFunc">
/// Specify how the database row pointer should move on. Return FALSE on a
/// call to the final row.
/// </param>
/// <returns>
/// A set of translated entities from the database.
/// </returns>
/// <example><code>
///
/// return base.FetchRecords(
/// _dOOdad.LoadFacilitySites,
/// () => _dOOdad.Sort = _dOOdad.GetAutoKeyColumn(),
/// () =>
/// {
/// var entity = new FacilitySite();
/// return entity.PopulateLookupEntity(
/// _dOOdad.CurrentRow.ItemArray);
/// },
/// _dOOdad.MoveNext);
///
/// </code></example>
protected virtual IEnumerable<TEntity> FetchRecords(
Func<bool> loadSubsetFunc, Action preIterationAction,
Func<TEntity> translateRowFunc, Func<bool> moveNextFunc)
{
// If records are found, sort them and return set of entities
if (loadSubsetFunc())
{
Trace.WriteLine(string.Format(
"# FOUND one or more records: Returning {0}(s) as a set.",
typeof(TEntity).Name));
int recordCount = 0;
preIterationAction();
do
{
recordCount++;
var entity = translateRowFunc();
yield return entity;
}
while (moveNextFunc());
// This code never gets reached if FirstOrDefault() is used on the set,
// because the system will assume no more enities need to be returned
Trace.WriteLine(string.Format(
"# FINISHED returning records: {0} {1}(s) returned as a set.",
recordCount, typeof(TEntity).Name));
}
else
{
Trace.WriteLine(string.Format(
"# ZERO records found: Returning an empty set of {0}.",
typeof(TEntity).Name));
}
}
編輯(添加解決方案;謝謝@Servy和@BenRobinson):
try
{
do
{
recordCount++;
var entity = translateRowFunc();
yield return entity;
}
while (moveNextFunc());
}
finally
{
// This code always executes, even when you use FirstOrDefault() on the set.
Trace.WriteLine(string.Format(
"# FINISHED returning records: {0} {1}(s) returned as a set.",
recordCount, typeof(TEntity).Name));
}
是的,您可以在迭代器塊中提供finally
塊,是的,它們旨在處理這種確切的情況。
IEnumerator<T>
實現IDisposable
。 當編譯器將迭代器塊轉換為實現時,枚舉器的Dispose
方法將基於應處理該枚舉器的當前位置來執行應執行的所有finally塊。
這意味着只要迭代IEnumerator
確保始終處置其枚舉數,就可以確保您的finally
塊將運行。
還要注意, using
塊將被轉換為try/finally
塊,因此基本上可以像迭代器塊中希望的那樣工作。 當需要處理迭代器時,它們將清理給定的資源。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.