扩展 ienumerable 方法 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]);
        }
    }
}
你需要

调用为

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

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

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

你应该分配结果,而不仅仅是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);

其他回复已经回答了您的问题,但更有效的实现不是这样吗(它不涉及制作数组的两个副本以将其反转两次)。不过,它确实迭代了集合两次(或者更确切地说,一次,然后 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的类型(例如数组或列表),则只会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;
    }
}

将代码更改为,

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

原因有三,

  1. SkipLast函数返回突变序列,它不会直接更改它。
  2. 将索引器与IEnumerable一起使用有什么意义?它强加了一个不必要的计数。
  3. 此代码易于阅读、更易于键入并显示意图。

有关更有效的泛型SkipLast请参阅带有枚举器的 Matthew 缓冲区。


您的示例可以使用更专业的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];
    }
}

最新更新