Сопоставьте тип перечислителя (не перечисляемый) с перечислителем другого типа

Как я могу преобразовать IEnumerator<KeyValuePair<TKey, TValue>> в IEnumerator<TKey>, содержащий ключ из пары ключ-значение?

В более общем смысле, как я могу преобразовать IEnumerator<TSource> в IEnumerator<TTarget>, если есть преобразование из TSource в TTarget?

Обратите внимание, что этот вопрос не ищет информацию о том, как перечислить перечислитель (используйте MoveNext и Current) или как преобразовать перечислитель в перечисляемый (см. это обсуждение), или как преобразовать enumerable в enumerator (просто вызовите GetEnumerator() на IEnumerable<T>), или как для сопоставления перечислимого с другим типом (см. это обсуждение).

Я предоставил свои собственные предложенные ответы в качестве ответов, но, конечно, я рад услышать о других подходах.


person mbabramo    schedule 04.01.2019    source источник


Ответы (2)


Подход, который мне нравится больше, заключается в следующем. (Можно также определить его как struct вместо class, например, чтобы уменьшить выделение кучи.)

public class TransformEnumerator<TSource, TTarget> : IEnumerator<TTarget>
{
    IEnumerator<TSource> SourceEnumerator;
    Func<TSource, TTarget> TransformFunc;

    public TransformEnumerator(IEnumerator<TSource> sourceEnumerator, Func<TSource, TTarget> transformFunc)
    {
        SourceEnumerator = sourceEnumerator;
        TransformFunc = transformFunc;
    }

    public TTarget Current => TransformFunc(SourceEnumerator.Current);

    object IEnumerator.Current => TransformFunc(SourceEnumerator.Current);

    public void Dispose()
    {
        SourceEnumerator.Dispose();
    }

    public bool MoveNext()
    {
        return SourceEnumerator.MoveNext();
    }

    public void Reset()
    {
        SourceEnumerator.Reset();
    }
}
person mbabramo    schedule 04.01.2019

Вот один из подходов с использованием static class:

public static class TransformEnumerator2<TSource, TTarget>
{
    public static IEnumerator<TTarget> GetEnumerator(IEnumerator<TSource> source, Func<TSource, TTarget> transformFunc)
    {
        return (IEnumerator<TTarget>)GetEnumerable(source, transformFunc);
    }

    private static IEnumerable<TTarget> GetEnumerable(IEnumerator<TSource> source, Func<TSource, TTarget> transformFunc)
    {
        while (source.MoveNext())
            yield return transformFunc(source.Current);
    }
}

Обратите внимание, что метод GetEnumerable является приватным. В общедоступном API клиент ожидает, что сможет использовать перечисляемое несколько раз, но здесь перечисляемое можно использовать только один раз. Однако ожидается, что Enumerator будет использоваться только один раз, поскольку поведение Reset не определено, так что это достигает своей цели.

person mbabramo    schedule 04.01.2019