自定义类作为Iterator中的返回

时间:2019-02-22 16:14:00

标签: c#

我有一个扩展方法,该方法用DateTimes遍历POCO类的列表。然后,此方法组合了彼此重叠或落入公差时间范围内的计划。效果很好!

但是,我的部分需求是同时获取(合并自数据库的)ID列表,以供以后参考。我尝试将扩展方法转换为返回自定义类,但出现以下错误:

Error   CS1624  The body of 'Extensions.Combine(CombinedSchedules)' cannot be an iterator block because 'CombinedSchedules' is not an iterator interface type

这是我的扩展方法:

        public static CombinedSchedules Combine(this CombinedSchedules items)
    {
        using (IEnumerator<ScheduleDto> enumerator = items.Schedules.GetEnumerator())
        {
            if (!enumerator.MoveNext())
            {
                yield break;
            }

            var previous = enumerator.Current;
            while (enumerator.MoveNext())
            {
                var next = enumerator.Current;

                if (TryCombine(previous, next, out var combined))
                {
                    items.IncludedSchedules.TryAdd(previous.Id);
                    previous = combined;
                    continue;
                }

                yield return previous;
                previous = next;

            }

            yield return previous;
        }
    }

这是CombinedSchedules类:

public class CombinedSchedules : IEnumerable<ScheduleDto>, IDisposable
{
    bool disposed = false;
    // Instantiate a SafeHandle instance.
    SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);

    public List<ScheduleDto> Schedules { get; set; }
    public List<int> IncludedSchedules { get; set; }



    public IEnumerator GetEnumerator()
    {
        return Schedules.GetEnumerator();
    }

    IEnumerator<ScheduleDto> IEnumerable<ScheduleDto>.GetEnumerator()
    {
        return Schedules.GetEnumerator();
    }

    #region IDisposable Support
    private bool disposedValue = false; // To detect redundant calls

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                // TODO: dispose managed state (managed objects).
            }

            // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
            // TODO: set large fields to null.

            disposedValue = true;
        }
    }

    // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
    // ~CombinedSchedules() {
    //   // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
    //   Dispose(false);
    // }

    // This code added to correctly implement the disposable pattern.
    public void Dispose()
    {
        // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
        Dispose(true);
        // TODO: uncomment the following line if the finalizer is overridden above.
        // GC.SuppressFinalize(this);
    }
    #endregion
}

我该如何取回我需要的所有东西,以便以后参考组合时间表的列表?预先感谢!

2 个答案:

答案 0 :(得分:4)

在方法中使用yield returnyield break时,它必须返回IEnumerableIEnumerator或通用版本。这里发生的是C#编译器将您的方法重建为实现MoveNextIEnumerator的对象的IEnumerable方法。

编译器不知道如何构建列表,字典,字符串或任何其他也实现IEnumerable的类型。

现在,您想要的功能是 possible ,并且我们知道这是可能的,因为C#7对async方法使用了该功能。在以前的版本中,出于相同的原因,async方法只能返回TaskvoidTask<T>,但是在C#7中,编译器团队添加了提供自己的功能。 “任务构建器”类。

理论上,编译器团队可以对迭代器块执行相同的操作,但总体感觉是对该功能的需求不大。

您最好的选择是选择以下两种策略之一:

  • 将产生的方法粘在上面,然后编写另一个扩展方法以将序列转换为数据类型。请参阅ToListToDictionarystring.Join或采用序列并返回从该序列派生的完全不同的数据结构的任何其他方法。如果您经常需要从序列中构造摘要对象,则此技术很有用。呼叫站点将是combined.GetSchedules().Combine(),其中GetSchedules()接受CombinedSchedule并返回IEnumerable<Schedule>,而Combine()接受IEnumerable<Schedule>并返回CombinedSchedule。 / li>
  • 停止屈服;创建一个CombinedScheduleBuilder对象,然后在您将产生的所有地方创建对象,而是在构建器上调用Add方法。 (产量中断变成回报。)您方法的最后一件事是返回构建器构建的东西。这对于仅将一个对象转换为另一个对象的情况很有用,这似乎很适合您的方案。 (这基本上是elgonzo的答案所提出的想法。)

答案 1 :(得分:2)

如果Combine方法应返回一个新的CombinedSchedules对象,我建议将其从迭代器方法转换为“常规”扩展方法,或多或少遵循以下大致概述的逻辑: / p>

扩展方法将枚举并合并提供的原始源 CombinedSchedules 对象中的 ScheduleDto 实例,并同时构建一个新的 IncludedSchedules 集合方式。使用合并的 ScheduleDto 和新的 IncludedSchedules 集合,将创建并返回一个新的 CombinedSchedules 对象。