La solution que j'utilise pour ce problème est légèrement plus élaborée.
Ma classe util static contient une méthode d'extension MarkEndqui convertit les T-items en EndMarkedItem<T>-items. Chaque élément est marqué d'un extra int, qui est soit 0 ; ou (au cas où l'on serait particulièrement intéressé par les 3 derniers éléments) -3 , -2 ou -1 pour les 3 derniers éléments.
Cela peut être utile en soi, par exemple lorsque vous souhaitez créer une liste dans une simple foreachboucle avec des virgules après chaque élément sauf les 2 derniers, avec l'avant-dernier élément suivi d'un mot de conjonction (tel que " et " ou " ou »), et le dernier élément suivi d'un point.
Pour générer la liste entière sans les n derniers éléments, la méthode d'extension ButLastitère simplement sur le EndMarkedItem<T>s while EndMark == 0.
Si vous ne spécifiez pas tailLength, seul le dernier élément est marqué (dans MarkEnd()) ou supprimé (dans ButLast()).
Comme les autres solutions, cela fonctionne par mise en mémoire tampon.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Adhemar.Util.Linq {
    public struct EndMarkedItem<T> {
        public T Item { get; private set; }
        public int EndMark { get; private set; }
        public EndMarkedItem(T item, int endMark) : this() {
            Item = item;
            EndMark = endMark;
        }
    }
    public static class TailEnumerables {
        public static IEnumerable<T> ButLast<T>(this IEnumerable<T> ts) {
            return ts.ButLast(1);
        }
        public static IEnumerable<T> ButLast<T>(this IEnumerable<T> ts, int tailLength) {
            return ts.MarkEnd(tailLength).TakeWhile(te => te.EndMark == 0).Select(te => te.Item);
        }
        public static IEnumerable<EndMarkedItem<T>> MarkEnd<T>(this IEnumerable<T> ts) {
            return ts.MarkEnd(1);
        }
        public static IEnumerable<EndMarkedItem<T>> MarkEnd<T>(this IEnumerable<T> ts, int tailLength) {
            if (tailLength < 0) {
                throw new ArgumentOutOfRangeException("tailLength");
            }
            else if (tailLength == 0) {
                foreach (var t in ts) {
                    yield return new EndMarkedItem<T>(t, 0);
                }
            }
            else {
                var buffer = new T[tailLength];
                var index = -buffer.Length;
                foreach (var t in ts) {
                    if (index < 0) {
                        buffer[buffer.Length + index] = t;
                        index++;
                    }
                    else {
                        yield return new EndMarkedItem<T>(buffer[index], 0);
                        buffer[index] = t;
                        index++;
                        if (index == buffer.Length) {
                            index = 0;
                        }
                    }
                }
                if (index >= 0) {
                    for (var i = index; i < buffer.Length; i++) {
                        yield return new EndMarkedItem<T>(buffer[i], i - buffer.Length - index);
                    }
                    for (var j = 0; j < index; j++) {
                        yield return new EndMarkedItem<T>(buffer[j], j - index);
                    }
                }
                else {
                    for (var k = 0; k < buffer.Length + index; k++) {
                        yield return new EndMarkedItem<T>(buffer[k], k - buffer.Length - index);
                    }
                }
            }    
        }
    }
}