在对C#中的第一个和/或最后一个元素进行特殊处理时,如何评估IEnumerable <T>?

彼得·鲁德曼

这是问题的更一般形式,如何对列表的第一个和最后一个元素进行特殊处理?更具体的问题很容易回答。我们知道第一个元素和最后一个元素的索引,因此我们可以直接访问它们或针对这些值测试索引变量。例如:

for (int i = 0; i < values.Count; ++i)
{
  if (i == values.Count - 1)
  {
    // do something with last element
  }
  else
  {
    // do something else
  }
}

但是有时我需要用一个变种IEnumerable<T>例如:

public static Bar TransformFoo(Foo value)
{
  if (isLast /* how do we know this? */)
  {
    // do something with the last element
  }
  else
  {
    // do something else
  }    
}

public static IEnumerable<Bar> TransformFooSequence(IEnumerable<Foo> source)
{
  return source.Select(TransformFoo);
}

由于这是一种常见的模式,所以我想以一种通用的方式解决它(而不是像过去那样为每种情况编写自定义的for循环)。一种选择是使用将序列转换为列表,ToList()使用计数元素Count()两种情况下的问题都是解决方案涉及评估整个序列,这可能会非常昂贵。

所以问题是,如何在对IEnumerable<T>序列的第一个和/或最后一个元素进行特殊处理的同时评估序列,同时又以一般的方式保持对该序列的惰性评估呢?

彼得·鲁德曼

解决此问题的一种方法是为其创建新的扩展方法,IEnumerable<T>方法返回源序列的元素以及有关其位置的语义信息。如果源序列的元素具有type T,则扩展方法将返回type的元组(T, PositionFlags)这是代码:

[Flags]
enum PositionFlags
{
    None = 0,
    First = 1,
    Last = 2
}    

public static IEnumerable<(T value, PositionFlags flags)> WithPositions<T>(
    this IEnumerable<T> source)
{
    using (var enumerator = source.GetEnumerator())
    {
        if (!enumerator.MoveNext())
        {
            yield break;
        }

        T value = enumerator.Current;
        PositionFlags flags = PositionFlags.First;

        while (enumerator.MoveNext())
        {
            yield return (value, flags);

            value = enumerator.Current;
            flags = PositionFlags.None;
        }

        flags |= PositionFlags.Last;

        yield return (value, flags);
    }
}

然后,我们可以传递位置信息以对序列中的第一项和/或最后一项进行特殊处理。例如:

Bar TransformFoo(Foo value, bool isLast)
{
    if (isLast)
    {
        // do something with the last element
     }
    else
    {
        // do something else
    }     
}

IEnumerable<Bar> TransformFooSequence(IEnumerable<Foo> source)
{
  return source
      .WithPositions()
      .Select(entry => TransformFoo(
          entry.value,
          (entry.flags & PositionFlags.Last) == PositionFlags.Last));
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何从.net中的IEnumerable <T>获取第一个元素?

如何将 Observable<T> 的第一个元素添加到每个组?

如何从列表/字典中打印名称,在Python中第一个元素为T,第二个元素为A

“ <T>将来<T> Submit(Callable <T>任务)”中的第一个<T>代表什么?

使用 rstrip() 和 lstrip() 删除字符串中的第一个和最后一个下划线字符“_”会导致 Python 3.7 中的字符“t”丢失

如何检查数组c ++中的第一个元素和最后一个元素

在FormFlow,BotFramework中使用List <T>时缺少第一个选项

从 IQueryable<T> 获取最后一个元素的扩展方法

C#LINQ分组并求和List <T>,然后分组并求和第一个List中的另一个列表

创建一个IEqualityComparer <IEnumerable <T >>

如何在C ++中交换队列的第一个和最后一个元素?

Java 泛型:<T> T[] toArray(T[] a) 中的第一个“T”是什么意思?

.NET(C#)中的反应性扩展-Subject <T>实例仅处理一个订阅

从 T-SQL 中的 json 对象获取第一个值或默认值

Java列表处理第一个和最后一个元素

'tt' 中的第二个字母 't' 比第一个大一点

如何从三个相同的元素中隐藏第一个和最后一个元素

如何获取javascript中特殊字符之间的第一个和最后一个单词或字符?

PHP-在编写固定长度的文件时,PHP在第一个文件之后不将'\ t'作为Tab处理。为什么?

Angular Js:如何在ng-repeat中查找第一个和最后一个元素并为其添加特殊类?

如何定位类的第一个和最后一个元素

如何通过XPath选择第一个和最后一个元素?

如何复制向量的第一个和最后一个元素?

R:识别重复组中的第一个和最后一个元素

删除数组中的第一个和最后一个元素

ul列表中的第一个和最后一个VISIBLE元素

NumPy数组中的第一个和最后一个元素

删除行中第一个和最后一个元素的填充

ActionResult <IEnumerable <T >>必须返回一个List <T>