有没有可能为yield-return方法添加“finally”代码块?

3

背景

大家好。我有一个抽象类叫做BaseRecordFetcher<TEntity>,它有一个方法从子类中获取读取/排序/翻译/移动下一步方法,并将结果作为模型实体进行返回。

当我读取多个数据行时,它会正确地对每个实体执行 yield return,然后在do...while之后到达 Trace 消息而没有任何问题。

问题

然而,我注意到当我在集合上使用 IEnumerable.FirstOrDefault() 时,系统会假定不再需要更多的 yield returns,并且它不会完成此方法的执行

除了追踪输出返回的记录数量以外,我对这种行为并没有太大的问题,但是它让我想到:“如果我确实需要一些...让我们称之为 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));
}

2
为什么不试一试,看看会发生什么? - undefined
1
为什么不试一下,看看会发生什么? - undefined
1个回答

6
是的,您可以在迭代器块内提供finally块,并且是专门设计用于处理这种情况的。 IEnumerator<T>实现了IDisposable。 当编译器将迭代器块转换为实现时,枚举器的Dispose方法将执行应根据枚举器在被处理掉时所处的位置而执行的任何finally块。
这意味着只要正在迭代IEnumerator的人始终确保处置他们的枚举器,就可以确定您的finally块将运行。
另请注意,using块将转换为try/finally块,因此将在迭代器块内按照所希望的方式工作。 在处理程序被处理时,如果需要,它们将清除给定资源。

哇,谁能想到呢 :-) 我猜对了关于"finally"的事情。谢谢! - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接