【问题标题】:StringBuilder extension method for appending a collection in C#用于在 C# 中附加集合的 StringBuilder 扩展方法
【发布时间】:2008-12-09 19:03:54
【问题描述】:

在 C# 中,我正在尝试为 StringBuilder 构建一个名为 AppendCollection() 的扩展方法,它可以让我这样做:

var sb1 = new StringBuilder();
var sb2 = new StringBuilder();
var people = new List<Person>() { ...init people here... };
var orders = new List<Orders>() { ...init orders here... };

sb1.AppendCollection(people, p => p.ToString());
sb2.AppendCollection(orders, o => o.ToString());

string stringPeople = sb1.ToString();
string stringOrders = sb2.ToString();

stringPeople 最终会为列表中的每个人提供一行。每一行都是 p.ToString() 的结果。对于 stringOrders 也是如此。我不太确定如何编写代码以使 lambda 与泛型一起使用。

【问题讨论】:

  • 有什么理由不想为此使用 String.Join()?
  • 我希望能够传入一个 lambda 作为格式化程序,这样您就可以执行 sb1.AppendCollection(people, => p.FirstName + " " + p.LastName);

标签: c# generics lambda stringbuilder


【解决方案1】:

使用Func&lt;T,string&gt; 委托。

public static void AppendCollection<T>(this StringBuilder sb, 
                                       IEnumerable<T> collection, Func<T, string> method) {
   foreach(T x in collection) 
       sb.AppendLine(method(x));
}

【讨论】:

  • 我不喜欢这个,因为它打破了 StringBuilder 的范式。 StringBuilder 上的方法应该继续添加到内部缓冲区,直到在构建器上调用 ToString。这结合了 append/tostring 步骤,与 StringBuilder 上的其他 append 方法不同。
  • 当然,我更新了答案以提及我对此的看法,但问题中特别提出了这一点。
  • 我完全同意。我输入的示例代码有点太快了。我已经更新了问题。
【解决方案2】:
 public static void AppendCollection<T>(this StringBuilder builder, IEnumerable<T> list, Func<T,string> func)
        {
            foreach (var item in list)
            {
                builder.AppendLine(func(item));
            }
        }

我不会返回一个字符串,我只是将它附加到传入的原始 Stringbuilder 中。

【讨论】:

    【解决方案3】:

    我不确定你是否需要那么努力:

     public static void AppendCollection( this StringBuilder builder,
                                          ICollection collection )
     {
         foreach (var item in collection)
         {
            builder.AppendLine( Convert.ToString( item ) );
         }
     }
    

    用作

     List<Person> people = ...
    
     StringBuilder builder = new StringBuilder();
     builder.AppendCollection( people );
     var s = builder.ToString();
    

    当然,Person 需要重写 ToString() 来为 Person 对象生成正确的输出。

    【讨论】:

    • 通过使用 lambda,您可以随意格式化集合中的项目。
    • 当然,但你只是在调用 ToString()
    • 我可能应该在示例中编写类似 sb1.AppendCollection(p.FirstName + " " + p.LastName) 的内容。这就是我喜欢这个功能的灵活性。
    • Lance,在这种情况下,我宁愿为AppendCollection 提供重载,让您指定格式,然后为您的类型编写自定义格式器。这样,您可以使用已建立的接口并促进代码重用,而不是创建一个全新的破坏接口的 API。
    【解决方案4】:

    类似:

      public static void AppendCollection<TItem>(this StringBuilder builder, IEnumerable<TItem> items, Func<TItem, string> valueSelector)
      {
           foreach(TItem item in items)
           {  
                builder.Append(valueSelector(item));
           }
      }
    

    我会添加一个有用的默认值以在 90% 的情况下保存指定 lambda...

       public static void AppendCollection<TItem>(this StringBuilder builder, IEnumerable<TItem> items)
      {
          AppendCollection(builder, items, x=>x.ToString());
       }
    

    【讨论】:

      【解决方案5】:

      我的版本:

          public static string AppendCollection<T>(this StringBuilder sb, IEnumerable<T> enumerable, Func<T, string> method)
          {
              List<T> l = new List<T>(enumerable);
              l.ForEach(item => sb.AppendLine(method(item)));
              return sb.ToString();
          }
      

      但是在这种情况下你不应该返回一个字符串。我更喜欢以下内容:

          public static void AppendCollection<T>(this StringBuilder sb, IEnumerable<T> enumerable, Func<T, string> method)
          {
              List<T> l = new List<T>(enumerable);
              l.ForEach(item => sb.AppendLine(method(item)));
          }
      

      使用如下:

              sb.AppendCollection(people, p => p.ToString());
              sb.AppendCollection(orders, o => o.ToString());
              Console.WriteLine(sb.ToString());
      

      【讨论】:

      • 我同意我不应该返回一个字符串。我已经更新了问题。
      【解决方案6】:
      static class SBExtention
      {
        static string AppendCollection<T>(this StringBuilder sb, 
                                          IEnumerable<T> coll, 
                                          Func<T,string> action)
        {
             foreach(T t in coll)
             {
                sb.Append(action(t));
                sb.Append("\n");
             }
             return sb.ToString();
      
        }
      }
      

      但是,我认为让它返回 StringBuilder 会更好。这样你就可以链接它:

        static StringBuilder AppendCollection<T>(this StringBuilder sb, 
                                          IEnumerable<T> coll, 
                                          Func<T,string> action)
        {
             // same
             return sb;
      
        }
      

      字符串 peopleAndOrders = sb.AppendCollection(人,p => p.ToString()) .AppendCollection(orders, o => o.ToString()).ToString();

      我同意 Jennifer 关于默认情况的观点:

         public static StringBuilder AppendCollection<TItem>(
                        this StringBuilder builder, 
                        IEnumerable<TItem> items)
        {
            return AppendCollection(builder, items, x=>x.ToString());
         }
      

      字符串 peopleAndOrders = sb.AppendCollection(people).AppendCollection(orders).ToString();

      【讨论】:

      • 链接的东西很好,但我通常更喜欢我的扩展方法与类中的其他方法基本上以相同的方式工作。更改其工作方式的基本模式会使其更难理解。
      【解决方案7】:

      这个方法应该返回什么?我可以看到一个字符串,但是如果您要附加到 StringBuilder,为什么?

      你想要做的事情很简单,但你需要准确地解释你想要什么。

      更新:

      这是我的看法。如果您只是要传入一个新的 StringBuilder 并返回一个字符串,那么为此使用扩展方法是愚蠢且毫无意义的。

      更新 2:

      现在我看到了这种用法,你所做的是不好的做法。理想情况下,您应该做的是:

      public static string Print<T>(this IEnumerable<T> col, Func<T,string> printer)
      {
        var sb = new StringBuilder();
        foreach (T t in col)
        {
          sb.AppendLine(printer(t));
        }
        return sb.ToString();
      }
      
      string[] col = { "Foo" , "Bar" };
      string lines = col.Print( s => s);
      

      更新 3:

      经过进一步澄清:

      public static void AppendCollection<T>(this StringBuilder sb, 
         List<T> col, Func<T,string> printer)
      {
        col.ForEach( o => sb.AppendLine(printer(o)));
      }
      

      (和布鲁诺·康德说的一样)

      现在你真的不再需要它了:)

      【讨论】:

      • 所以我被否决了,因为我要求澄清?这就是精神...
      • 我认为关键是您的帖子不是答案,而是问题。我没有投票给你。
      • 您可以通过在评论而不是答案中要求澄清来避免投反对票。我也没有投反对票。我这样做是为了避免那些认为任何不是答案的东西都没有帮助的人。
      • 但我不想这样。一旦我得到澄清,我将发布答案......
      • 该方法应该返回 void 而不是我一开始的字符串。它应该像 AppendLine() 一样运行,但允许你传递一个集合和格式化函数。
      猜你喜欢
      • 2014-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-03
      • 2014-03-06
      • 2017-01-02
      • 2011-03-07
      • 2011-03-04
      相关资源
      最近更新 更多