在一般情况下可以工作,但 LastIndex 不存在
不,但您可以通过以下方式找到它:
var lastIndex = sequence
.Select((x, i) => new {El = x, Idx = i})
.Where(x => x.El != 0)
.Select(x => x.Idx).Last();
如果您需要与IQueryable<T> 合作,那将是您所能得到的。
它有一些问题。一方面,它扫描序列两次,谁说序列甚至允许这样做。我们可以做得更好,但我们必须缓冲,虽然不一定缓冲整个事情:
public static IEnumerable<T> BeforeLastMatch<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (predicate == null) throw new ArgumentNullException(nameof(predicate));
return BeforeLastMatchImpl(source, predicate);
}
public static IEnumerable<T> BeforeLastMatchImpl<T>(IEnumerable<T> source, Func<T, bool> predicate)
{
var buffer = new List<T>();
foreach(T item in source)
{
if (predicate(item) && buffer.Count != 0)
{
foreach(T allowed in buffer)
{
yield return allowed;
}
buffer.Clear();
}
buffer.Add(item);
}
}
致电sequence.BeforeLastMatch(x => x != 0),您会收到5, 0, 0, 0, 0, 4, 0, 0, 0, 2, 0
如果你真的需要它同时使用 IEnumerable 和 IQueryable 也可以处理,但它有点复杂。如果您知道您只会拥有内存中的IEnumerable,请不要打扰。 (另外一些供应商对不同的特性有不同的支持,所以你可能不得不做上面的内存版本):
private class ElementAndIndex<T>
{
public T Element { get; set; }
public int Index { get; set; }
}
public static IQueryable<T> BeforeLastMatch<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (predicate == null) throw new ArgumentNullException(nameof(predicate));
// If source is actually an in-memory enumerable, the other method will be more efficient,
// so use it instead.
var asEnum = source as EnumerableQuery<T>;
if (asEnum != null && asEnum.Expression.NodeType == ExpressionType.Constant)
{
// On any other IQueryable calling `AsEnumerable()` will force it
// to be loaded into memory, but on an EnumerableQuery it just
// unwraps the wrapped enumerable this will chain back to the
// contained GetEnumerator.
return BeforeLastMatchImpl(source.AsEnumerable(), predicate.Compile()).AsQueryable();
}
// We have a lambda from (T x) => bool, and we need one from
// (ElementAndIndex<T> x) => bool, so build it here.
var param = Expression.Parameter(typeof(ElementAndIndex<T>));
var indexingPredicate = Expression.Lambda<Func<ElementAndIndex<T>, bool>>(
Expression.Invoke(predicate, Expression.Property(param, "Element")),
param
);
return source.Take( // We're going to Take based on the last index this finds.
source
// Elements and indices together
.Select((x, i) => new ElementAndIndex<T>{ Element = x, Index = i})
// The new predicate we created from that passed to us.
.Where(indexingPredicate)
// The last matching element.
.Select(x => x.Index).Last());
}