如果枚举包含任何项,则枚举枚举的功能方法

时间:2018-05-23 18:01:48

标签: c# linq functional-programming ienumerable

我只是在寻找

的替代品
if (enumerable.Any())
{
   // Code to Map to new type.
}

我知道linq中的foreach方法会这样做,但它会返回void,我需要一些可以将集合传递给新类型的东西。

我试图避免的问题是两次枚举集合并减少圈复杂度。

我想知道linq库中是否存在允许这样的现有方法。

3 个答案:

答案 0 :(得分:0)

您可以将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}
 */

答案 1 :(得分:0)

前段时间我遇到了类似的问题......然后我创建了这个:

// 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>。然后你&#34;打包&#34;这部分消耗IEnumerator<T>IEnumerable<T>&#34;正确&#34;已构建(示例的enu2)...当下次使用此IEnumerable<T>时,它将返回部分消耗的IEnumerator<T>,并使用一些技巧使其看起来不是&#39真的消耗了。

答案 2 :(得分:0)

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());
}