扩展ienumerable方法C#

时间:2014-01-14 09:21:54

标签: c#

我写了跳过最后一个方法。当我用int数组调用它时,我希望只返回2个元素,而不是4.

有什么问题?

由于

public static class myclass
{
    public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source, int n)
    {
        return source.Reverse().Skip(n).Reverse();
    }

}

class Program
{
    static void Main(string[] args)
    {
        int [] a = new int[] {5, 6, 7, 8};
        ArrayList a1 = new ArrayList();

        a.SkipLast(2);
        for( int i = 0; i <a.Length; i++)
        {
            Console.Write(a[i]);
        }

    }
}

4 个答案:

答案 0 :(得分:5)

你需要打电话给

var newlist = a.SkipLast(2);
for( int i = 0; i <newlist.Count; i++)
{
    Console.Write(newlist[i]);
}

您的方法返回跳过的列表,但原始列表不会更新

如果要分配或更新相同的列表,可以将返回的列表设置为原始的a = a.SkipLast(2).ToArray();

答案 1 :(得分:3)

你应该分配结果,而不仅仅是a.SkipLast(2)

  a = a.SkipLast(2).ToArray(); // <- if you want to change "a" and loop on a 

  for( int i = 0; i <a.Length; i++) { ...

当您执行a.SkipLast(2)时,它会创建IEnumerable<int>,然后会丢弃它; 最可读的解决方案,恕我直言,使用 foreach ,这对LINQ非常方便:

  ...
  int [] a = new int[] {5, 6, 7, 8};

  foreach(int item in a.SkipLast(2)) 
    Console.Write(item);

答案 2 :(得分:1)

将您的代码更改为

foreach (var r in a.SkipLast(2))
{
    Console.Write(r);
}

有三个原因,

  1. SkipLast函数返回变异序列,它不会直接更改它。
  2. 使用IEnumerable的索引器有什么意义?它强加了不必要的数量。
  3. 此代码易于阅读,更易于输入和显示意图。

  4. 要获得更有效的通用SkipLast,请参阅Matthew's buffer with enumerator.


    您的示例可以使用更专业的SkipLast

    public static IEnumerable<T> SkipLast<T>(this IList<T> source, int n = 1)
    {
        for (var i = 0; i < (source.Count - n); i++)
        {
            yield return source[i];
        }
    }
    

答案 3 :(得分:1)

其他回复已经回答了你的问题,但不会更有效的实现(这不涉及制作数组的两个副本以便将其反转两次)。它确实迭代了集合两次(或者更确切地说,一次,然后是count-n次访问):

public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source, int n)
{
    n = source.Count() - n;
    return source.TakeWhile(_ => n-- > 0);
}

实际上,如果source是一种在没有迭代的情况下实现Count的类型(例如数组或List),那么这只会访问元素count-n次,所以它会非常对这些类型有效。

这是一个更好的解决方案,只迭代序列一次。它的数据要求只需要一个带有n元素的缓冲区,如果n与序列的大小相比较小,则效率非常高:

public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source, int n)
{
    int count = 0;
    T[] buffer = new T[n];

    var iter = source.GetEnumerator();

    while (iter.MoveNext())
    {
        if (count >= n)
            yield return buffer[count%n];

        buffer[count++%n] = iter.Current;
    }
}