【问题标题】:Is there a Linq method to add a single item to an IEnumerable<T>?是否有 Linq 方法可以将单个项目添加到 IEnumerable<T>?
【发布时间】:2011-06-20 21:41:41
【问题描述】:

基本上我正在尝试做这样的事情:

image.Layers

它为除Parent 层之外的所有层返回一个 IEnumerable,但在某些情况下,我只想这样做:

image.Layers.With(image.ParentLayer);

因为它只在少数地方使用,而 image.Layers 满足的通常使用的 100 多个。这就是为什么我不想创建另一个也返回 Parent 层的属性。

【问题讨论】:

    标签: c# .net ienumerable


    【解决方案1】:

    一种方法是从项目(例如数组)中创建一个单例序列,然后将Concat 放在原始序列上:

    image.Layers.Concat(new[] { image.ParentLayer } )
    

    如果您经常这样做,请考虑编写一个Append(或类似的)扩展方法,例如listed here,它可以让您这样做:

    image.Layers.Append(image.ParentLayer)
    

    .NET Core 更新 (per the "best" answer below):

    AppendPrepend 现在已添加到 .NET Standard 框架中,因此您不再需要自己编写。只需这样做:

    image.Layers.Append(image.ParentLayer)
    

    【讨论】:

      【解决方案2】:

      AppendPrepend 现在已添加到 .NET Standard 框架中,因此您不再需要自己编写。只需这样做:

      image.Layers.Append(image.ParentLayer)
      

      请参阅What are the 43 APIs that are in .Net Standard 2.0 but not in .Net Framework 4.6.1? 了解新功能的详细列表。

      【讨论】:

      • 这种方法与使用.Concat(new[] { ... }) 相比有一个缺点:如果要添加的项目也实现IEnumerable&lt;T&gt;,那么Append 方法将默认添加IEnumerable&lt;T&gt;只是打算添加T
      • 回想一下,它不能在原版上运行。您需要image.Layers = image.Layers.Append(image.ParentLayer)(可能还有image.Layers = image.Layers.Append(image.ParentLayer).ToArray() 或类似名称)才能获得预期的效果。
      【解决方案3】:

      已经给出了许多实现。 我的看起来有点不同(但性能一样好)

      另外,我发现控制 ORDER 也是可行的。因此,我通常也有一个 ConcatTo 方法,将新元素放在前面。

      public static class Utility
      {
          /// <summary>
          /// Adds the specified element at the end of the IEnummerable.
          /// </summary>
          /// <typeparam name="T">The type of elements the IEnumerable contans.</typeparam>
          /// <param name="target">The target.</param>
          /// <param name="item">The item to be concatenated.</param>
          /// <returns>An IEnumerable, enumerating first the items in the existing enumerable</returns>
          public static IEnumerable<T> ConcatItem<T>(this IEnumerable<T> target, T item)
          {
              if (null == target) throw new ArgumentException(nameof(target));
              foreach (T t in target) yield return t;
              yield return item;
          }
      
          /// <summary>
          /// Inserts the specified element at the start of the IEnumerable.
          /// </summary>
          /// <typeparam name="T">The type of elements the IEnumerable contans.</typeparam>
          /// <param name="target">The IEnummerable.</param>
          /// <param name="item">The item to be concatenated.</param>
          /// <returns>An IEnumerable, enumerating first the target elements, and then the new element.</returns>
          public static IEnumerable<T> ConcatTo<T>(this IEnumerable<T> target, T item)
          {
              if (null == target) throw new ArgumentException(nameof(target));
              yield return item;
              foreach (T t in target) yield return t;
          }
      }
      

      或者,使用隐式创建的数组。 (使用 params 关键字),以便您可以调用该方法一次添加一个或多个项目:

      public static class Utility
      {
          /// <summary>
          /// Adds the specified element at the end of the IEnummerable.
          /// </summary>
          /// <typeparam name="T">The type of elements the IEnumerable contans.</typeparam>
          /// <param name="target">The target.</param>
          /// <param name="items">The items to be concatenated.</param>
          /// <returns>An IEnumerable, enumerating first the items in the existing enumerable</returns>
          public static IEnumerable<T> ConcatItems<T>(this IEnumerable<T> target, params T[] items) =>
              (target ?? throw new ArgumentException(nameof(target))).Concat(items);
      
          /// <summary>
          /// Inserts the specified element at the start of the IEnumerable.
          /// </summary>
          /// <typeparam name="T">The type of elements the IEnumerable contans.</typeparam>
          /// <param name="target">The IEnummerable.</param>
          /// <param name="items">The items to be concatenated.</param>
          /// <returns>An IEnumerable, enumerating first the target elements, and then the new elements.</returns>
          public static IEnumerable<T> ConcatTo<T>(this IEnumerable<T> target, params T[] items) =>
              items.Concat(target ?? throw new ArgumentException(nameof(target)));
      

      【讨论】:

      • .NET 可能会将 yield 语句编译为一个匿名对象,该对象被实例化。
      • @Jorrit:实际上,编译器创建了一个(“匿名”)迭代器,它也有一些开销。因此编译后的代码更大,但在运行时分配的内存是几个字节而不是列表的完整副本。
      【解决方案4】:

      没有一种方法可以做到这一点。最接近的是Enumerable.Concat 方法,但它试图将IEnumerable&lt;T&gt; 与另一个IEnumerable&lt;T&gt; 结合起来。您可以使用以下内容使其与单个元素一起使用

      image.Layers.Concat(new [] { image.ParentLayer });
      

      或者只是添加一个新的扩展方法

      public static IEnumerable<T> ConcatSingle<T>(this IEnumerable<T> enumerable, T value) {
        return enumerable.Concat(new [] { value });
      }
      

      【讨论】:

        【解决方案5】:

        你可以使用Enumerable.Concat:

        var allLayers = image.Layers.Concat(new[] {image.ParentLayer});
        

        【讨论】:

          【解决方案6】:

          你可以这样做:

          image.Layers.Concat(new[] { image.ParentLayer });
          

          将枚举与包含您要添加的内容的单元素数组连接

          【讨论】:

            【解决方案7】:

            我曾经为此做了一个不错的小功能:

            public static class CoreUtil
            {    
                public static IEnumerable<T> AsEnumerable<T>(params T[] items)
                {
                    return items;
                }
            }
            

            现在这是可能的:

            image.Layers.Append(CoreUtil.AsEnumerable(image.ParentLayer, image.AnotherLayer))
            

            【讨论】:

            • AsEnumerable 现在是 a built-in extension method,截至 .Net 3.5,因此可能需要使用不同的名称。此外,AsX 暗示它是一种扩展方法(方法不应该是动词吗?),所以我认为它首先不是一个好名字。我建议Enumerate
            • 哈!很久以前。是的,同时我也得出了同样的结论。我仍然使用这个小东西,但现在它是ToEnumerableEnumerate 也不错。
            • 我仍然看到 ToEnumerable 建议扩展方法。 CreateEnumerable 怎么样? :)
            【解决方案8】:

            如果您喜欢 .With 的语法,请将其写为扩展方法。 IEnumerable 不会注意到另一个。

            【讨论】:

              【解决方案9】:

              我使用以下扩展方法来避免创建无用的Array

              public static IEnumerable<T> ConcatSingle<T>(this IEnumerable<T> enumerable, T value) {
                 return enumerable.Concat(value.Yield());
              }
              
              public static IEnumerable<T> Yield<T>(this T item) {
                  yield return item;
              }
              

              【讨论】:

              • +1 不错的实现,但为什么要避免使用数组?我知道创建数组感觉不对,但它真的比 C# 为这些迭代器所做的所有隐藏工作效率低吗?
              • Yield 将分配并返回一个匿名的 IEnumerable,其中包含对您的项目的引用,因此这可能比单个项目数组需要更多的内存和时间。
              【解决方案10】:

              Concat 方法可以连接两个序列。

              【讨论】:

              • 第二个元素不是序列……这就是问题的重点。
              【解决方案11】:
              /// <summary>Concatenates elements to a sequence.</summary>
              /// <typeparam name="T">The type of the elements of the input sequences.</typeparam>
              /// <param name="target">The sequence to concatenate.</param>
              /// <param name="items">The items to concatenate to the sequence.</param>
              public static IEnumerable<T> ConcatItems<T>(this IEnumerable<T> target, params T[] items)
              {
                  if (items == null)
                      items = new [] { default(T) };
                  return target.Concat(items);
              }
              

              此解决方案基于realbart's answer。我对其进行了调整以允许使用单个 null 值作为参数:

              var newCollection = collection.ConcatItems(null)
              

              【讨论】:

                猜你喜欢
                • 2013-08-22
                • 2013-02-21
                • 2010-11-15
                • 1970-01-01
                • 1970-01-01
                • 2011-06-28
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多