views:

153

answers:

7

The StringBuilder class allows you, in what I consider to be a very intuitive way, to chain method calls to .Append(), .AppendFormat() and some others like so:

StringBuilder sb = new StringBuilder();
sb.Append("first string")
  .Append("second string);

The List class' .Add() method, on the other hand, returns void - so chaining calls doesn't work. This, in my opinion and the immortal words of Jayne Cobb "just don' make no kinda sense".

I admit that my understanding of Generics is very basic, but I would like to overload the .Add() method (and others) so that they return the original object, and allow chaining. Any and all assistance will be rewarded with further Firefly quotes.

+2  A: 
public static IList<T> Anything-not-Add*<T>(this IList<T> list, T item)
{
    list.Add(item);
    return list;
}

* AddItem, Append, AppendList, etc. (see comments below)

The same idea came to my mind like other guys' too, independently:

public static TList Anything<TList, TItem>(this TList list, TItem item)
    where TList : IList<TItem>
{
    list.Add(item);
    return list;

}

And Thomas is right: as far as IList<T> inherits ICollection<T> you should use ICollection.

abatishchev
Extension methods for the win! This will only work in C# 3 or later (so VS 2008 or 2010).
Jason Thompson
this will not work, because during overload resolution compiler will prefer instance method to extension
desco
This is an extension method, right? How will the compiler know to use this version of the method instead of the original (@desco seems to be saying it won't)
ScottSEA
@ScottSEA, yes @desco is right.
Jon Hanna
@desco: Thanks, you're right
abatishchev
+4  A: 

use can create extension method

public static class ListExtensions
{
    public static List<T> AddItem<T>(this List<T> self, T item)
    {
        self.Add(item);
        return self;
    }
}

var l = new List<int>();
l.AddItem(1).AddItem(2);

EDIT

we can also make this method generic over collection parameter

public static class ListExtensions
{   
    public static TC AddItem<TC, T>(this TC self, T item)
        where TC : ICollection<T>
    {
        self.Add(item);
        return self;
    }
}

var c1 = new Collection<int>();
c1.AddItem(1).AddItem(2);

var c2 = new List<int>();
c2.AddItem(10).AddItem(20);

EDIT 2: Maybe someone will find this trick useful, it is possible to utilize nested object initializer and collection initializer for setting properties and adding values into existing instances.

using System;
using System.Collections.Generic;
using System.Linq;

struct I<T>
{
    public readonly T V;
    public I(T v)
    {
        V = v;
    }
}

class Obj
{
    public int A { get; set; }
    public string B { get; set; }

    public override string ToString()
    {
        return string.Format("A={0}, B={1}", A, B);
    }
}


class Program
{
    static void Main()
    {
        var list = new List<int> { 100 };
        new I<List<int>>(list)
            {
                V = { 1, 2, 3, 4, 5, 6 }
            };

        Console.WriteLine(string.Join(" ", list.Select(x => x.ToString()).ToArray())); // 100 1 2 3 4 5 6 

        var obj = new Obj { A = 10, B = "!!!" };
        Console.WriteLine(obj); // A=10, B=!!!
        new I<Obj>(obj)
            {
                V = { B = "Changed!" }
            };
        Console.WriteLine(obj); // A=10, B=Changed!
    }
}
desco
Is there no way to do this without changing the name of the method? I'd like to minimize possible confusion in other developers
ScottSEA
You should declare the parameter as ICollection<T> rather than List<T>
Thomas Levesque
@Thomas: `Add()` is method of both IList and ICollection
abatishchev
@desco that's true of IList<T> too, which is why I changed my answer to work on, and return, List<T>. @ScottSEA if you could change the mechanism of a method but not its name, then that would **really** confuse other developers. As it is, I would already avoid the whole chaining approach as its not as idiomatic in C# as some other languages and hence liable to cause confusion as it is (indeed, as it is some people argue against chaining because it can trip people up as to where it is and isn't allowed).
Jon Hanna
if parameter is ICollection then we will lose original type after chaning method calls.
desco
@abatishchev, yes, but IList<T> inherits ICollection<T>. You should always use the less derived type when possible
Thomas Levesque
@desco, indeed. See my answer for a workaround
Thomas Levesque
+1  A: 

Have an extension method off:

public static List<T> Append(this List<T> list, T item)
{
  list.Add(item);
  return self;
}

Note that we have to create it with a new name, as if an instance member matches the signature (the 'Add' you are already complaining about) then the extension method won't be called.

In all though, I'd recommend against this. While I like chaining myself, it's being rare in C# libraries means it's not as idiomatic as it is in other languages where it's more common (no technical reason for this, though some differences in how properties work encourages it a bit more in some other languages, just the way things are in terms of what is common). Because of this, the constructs it enables aren't as familiar in C# as elsewhere, and your code is more likely to be misread by another dev.

Jon Hanna
+12  A: 

If you want to keep the same name for the Add method, you could hide the method from the base class:

public class MyList<T> : List<T>
{
    public new MyList<T> Add(T item)
    {
        base.Add(item);
        return this;
    }
}

However, this will only work if you're manipulating the list with a variable explicitly typed as MyList<T> (i.e. it won't work if your variable is declared as IList<T> for instance). So I think the solutions involving an extension method are better, even if that means changing the name of the method.

Although others have already posted solutions with extension methods, here's another one, that has the advantage of conserving the actual type of the collection:

public static class ExtensionMethods
{
    public static TCollection Append<TCollection, TItem>(this TCollection collection, TItem item)
        where TCollection : ICollection<TItem>
    {
        collection.Add(item);
        return collection;
    }
}

Use it like that:

var list = new List<string>();
list.Append("Hello").Append("World");
Thomas Levesque
This was the most complete answer, that was posted first - they're all very similar, so it was difficult to choose which one as Answer. Thanks to all for the discussion: I learned a lot.
ScottSEA
A: 

I like the extension approach that others have mentioned as that seems to answer the question well (although you would have to give it a different method signature than the existing Add()). Also, it does seem like there's some inconsistency about object returns on calls like this (I thought it was a mutability issue, but the stringbuilder is mutable isn't it?), so you raise an interesting question.

I'm curious, though, if the AddRange method would not work as an out-of-the-box solution? Is there a particular reason you want to chain the commands instead of passing everything in as a an array?

Would do something like this not accomplish what you need?

List<string> list = new List<string>();
list.AddRange(new string[]{
    "first string", 
    "second string",
});
Steven
+1  A: 

You could use an extension method with a different name:

public static T Put<T, U>(this T collection, U item) where T : ICollection<U> {
  collection.Add(item);
  return collection;
}

To create code like this:

var list = new List<int>();
list.Put(1).Put(2).Put(3);

To retain the name Add, however, you can have a method like this:

public static T Add<T, U>(this T collection, Func<U> itemProducer) 
  where T : ICollection<U> {
  collection.Add(itemProducer());
  return collection;
}

And create code like this:

list.Add(()=>1).Add(()=>2).Add(()=>3);

It doesn't look that good though.

Maybe if we change the type we can have a better syntax.

Given this class:

public class ListBuilder<T> {
  IList<T> _list;
  public ListBuilder(IList<T> list) {
    _list = list;
  }
  public ListBuilder<T> Add(T item) {
    _list.Add(item);
    return this;
  }
}

You can have this method:

public static ListBuilder<T> Edit<T>(this IList<T> list) {
  return new ListBuilder<T>(list);
}

And use code like this:

list.Edit().Add(1).Add(2).Add(3);
Jordão
very nice solution !
Thomas Levesque
+1  A: 

I'm sure you won't appreciate this answer but there's a very good reason that List<>.Add() works this way. It is very fast, it needs to be to be competitive with an array and because it is such a low-level method. It is however just a hair too big to get inlined by the JIT optimizer. It cannot optimize the return statement you'd need to return the list reference.

Writing lst.Add(obj) in your code is for free, the lst reference is available in a CPU register.

A version of Add() that returns the reference makes the code almost 5% slower. It's a lot worse for the proposed extension method, there an entire extra stack frame involved.

Hans Passant
I'm sure you won't appreciate this comment, but perhaps you could explain why I wouldn't appreciate that answer. It was well written, easy to understand and raised some good points on optimization.
ScottSEA