Функциональный способ перечисления перечислимых, если он содержит какие-либо элементы - PullRequest
0 голосов
/ 23 мая 2018

Я просто ищу альтернативу

if (enumerable.Any())
{
   // Code to Map to new type.
}

Я знаю, что метод foreach в linq делает это, но возвращает void, и мне требуется что-то, что может передать коллекцию новому типу.

Проблема, которую я пытаюсь избежать, состоит в том, чтобы дважды перечислить коллекцию и уменьшить цикломатическую сложность.

Мне было интересно, существуют ли какие-либо методы в библиотеке linq, которые позволили бы это.

Ответы [ 3 ]

0 голосов
/ 23 мая 2018

Некоторое время назад у меня была похожая проблема ... Тогда я создал это:

// Simple IEnumerable<T> that "uses" an IEnumerator<T> that has
// already received a MoveNext(). "eats" the first MoveNext() 
// received, then continues normally. For shortness, both IEnumerable<T>
// and IEnumerator<T> are implemented by the same class. Note that if a
// second call to GetEnumerator() is done, the "real" IEnumerator<T> will
// be returned, not this proxy implementation.
public class EnumerableFromStartedEnumerator<T> : IEnumerable<T>, IEnumerator<T>
{
    public readonly IEnumerator<T> Enumerator;

    public readonly IEnumerable<T> Enumerable;

    // Received by creator. Return value of MoveNext() done by caller
    protected bool FirstMoveNextSuccessful { get; set; }

    // The Enumerator can be "used" only once, then a new enumerator
    // can be requested by Enumerable.GetEnumerator() 
    // (default = false)
    protected bool Used { get; set; }

    // The first MoveNext() has been already done (default = false)
    protected bool DoneMoveNext { get; set; }

    public EnumerableFromStartedEnumerator(IEnumerator<T> enumerator, bool firstMoveNextSuccessful, IEnumerable<T> enumerable)
    {
        Enumerator = enumerator;
        FirstMoveNextSuccessful = firstMoveNextSuccessful;
        Enumerable = enumerable;
    }

    public IEnumerator<T> GetEnumerator()
    {
        if (Used)
        {
            return Enumerable.GetEnumerator();
        }

        Used = true;
        return this;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public T Current
    {
        get
        {
            // There are various school of though on what should
            // happens if called before the first MoveNext() or
            // after a MoveNext() returns false. We follow the 
            // "return default(TInner)" school of thought for the
            // before first MoveNext() and the "whatever the 
            // Enumerator wants" for the after a MoveNext() returns
            // false
            if (!DoneMoveNext)
            {
                return default(T);
            }

            return Enumerator.Current;
        }
    }

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

    object IEnumerator.Current
    {
        get
        {
            return Current;
        }
    }

    public bool MoveNext()
    {
        if (!DoneMoveNext)
        {
            DoneMoveNext = true;
            return FirstMoveNextSuccessful;
        }

        return Enumerator.MoveNext();
    }

    public void Reset()
    {
        // This will 99% throw :-) Not our problem.
        Enumerator.Reset();

        // So it is improbable we will arrive here
        DoneMoveNext = true;
    }
}

и это

public static class EnumerableExtensions
{
    public static bool Any<TSource>(this IEnumerable<TSource> source, out IEnumerable<TSource> newSource)
    {
        var coll = source as ICollection<TSource>;

        if (coll != null)
        {
            newSource = source;
            return coll.Count != 0;
        }

        var coll2 = source as ICollection;

        if (coll2 != null)
        {
            newSource = source;
            return coll2.Count != 0;
        }

        var enu = source.GetEnumerator();

        bool success = enu.MoveNext();

        newSource = new EnumerableFromStartedEnumerator<TSource>(enu, success, source);

        return success;
    }
}

, которые вы используете следующим образом:

var enu = new int[] { 1, 2, 3, 4, 5 }.Where(x => x > 3);

IEnumerable<int> enu2;

Console.WriteLine(enu.Any(out enu2));

foreach (var num in enu2)
{
    Console.WriteLine(num);
}

Основная идея состоит в том, что вы начинаете перечислять IEnumerable<> и перечисляете только первый элемент.Теперь у вас есть частично потребленный IEnumerator<T>.Затем вы «упаковываете» этот частично потребленный IEnumerator<T> в новый IEnumerable<T> «правильно» собранный (пример enu2) ... Когда этот IEnumerable<T> будет использован в следующий раз, он вернет частично израсходованный IEnumerator<T> с некоторыми уловками, чтобы казалось, что это не было на самом деле потреблено.

0 голосов
/ 23 мая 2018

Select кажется лучшим вариантом, но если ваше отображение сложное и / или не генерирует простой тип, то вы должны перебирать коллекцию в теле if.В этом случае вы можете просто обработать перечисление вручную:

var e = enumerable.GetEnumerator();
if (e.MoveNext()) {
    // Code to Map to new type.
    do {
        // map e.Current to new type
    } while (e.MoveNext());
}
0 голосов
/ 23 мая 2018

Вы можете использовать ConvertAll с методом конвертации, который отображает ваш объект на ваш новый тип.

https://msdn.microsoft.com/en-us/library/73fe8cwf(v=vs.110).aspx

Освобождение от MSDN

using System;
using System.Drawing;
using System.Collections.Generic;

public class Example
{
    public static void Main()
    {
        List<PointF> lpf = new List<PointF>();

        lpf.Add(new PointF(27.8F, 32.62F));
        lpf.Add(new PointF(99.3F, 147.273F));
        lpf.Add(new PointF(7.5F, 1412.2F));

        Console.WriteLine();
        foreach( PointF p in lpf )
        {
            Console.WriteLine(p);
        }

        List<Point> lp = lpf.ConvertAll( 
            new Converter<PointF, Point>(PointFToPoint));

        Console.WriteLine();
        foreach( Point p in lp )
        {
            Console.WriteLine(p);
        }
    }

    public static Point PointFToPoint(PointF pf)
    {
        return new Point(((int) pf.X), ((int) pf.Y));
    }
}

/* This code example produces the following output:

{X=27.8, Y=32.62}
{X=99.3, Y=147.273}
{X=7.5, Y=1412.2}

{X=27,Y=32}
{X=99,Y=147}
{X=7,Y=1412}
 */
...