Question

In C#, I'm trying to build an extension method for StringBuilder called AppendCollection() that would let me do this:

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 would end up with a line for each person in the list. Each line would be the result of p.ToString(). Likewise for stringOrders. I'm not quite sure how to write the code to make the lambdas work with generics.

Was it helpful?

Solution

Use the Func<T,string> delegate.

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

OTHER TIPS

 public static void AppendCollection<T>(this StringBuilder builder, IEnumerable<T> list, Func<T,string> func)
        {
            foreach (var item in list)
            {
                builder.AppendLine(func(item));
            }
        }

I wouldn't return a string, I would just append it to the original Stringbuilder that was passed in.

I'm not sure you need to work that hard:

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

Used as

 List<Person> people = ...

 StringBuilder builder = new StringBuilder();
 builder.AppendCollection( people );
 var s = builder.ToString();

Of course, Person needs to override ToString() to produce the correct output for a Person object.

Something like:

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

I would add in a useful default to save specifiying the lambda in 90% of cases...

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

My version:

    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();
    }

but you shouldn't return a string in this case. I would prefer the following:

    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)));
    }

to be used like:

        sb.AppendCollection(people, p => p.ToString());
        sb.AppendCollection(orders, o => o.ToString());
        Console.WriteLine(sb.ToString());
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();

  }
}

However, I think you'll be better off having it return the StringBuilder. That way you could chain it:

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

  }

string peopleAndOrders = sb.AppendCollection(people, p => p.ToString()) .AppendCollection(orders, o => o.ToString()).ToString();

And I agree with Jennifer about the default case:

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

string peopleAndOrders = sb.AppendCollection(people).AppendCollection(orders).ToString();

What is this method suppose to return? I can see a string, but why, if you are appending to a StringBuilder?

What you are trying to do is rather easy, but you need to explain exactly what you want.

Update:

Here's my take. Using an extension method for this is stupid and pointless if you are just going to pass in a new StringBuilder and return a string.

Update 2:

Now that I see that usage, what you are doing is bad practice. What you should ideally be doing is something like:

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);

Update 3:

After more clarification:

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

(which is the same as bruno conde said)

And now you dont really need it anymore :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top