views:

130

answers:

5

Is it possible to create an extension method that returns the instance that is invoking the extension method?

I would like to have an extension method for anything that inherits from ICollection<T>, returns the object. Much like how jQuery always returns the jquery object.

public static object AddItem<T>(this ICollection<T> collection, T itemToAdd)
{
    collection.Add(itemToAdd);
    return collection;
{

I imagine something like above, but I am not sure how to get back to the parent to the "this" object type for use of something like this:

List<int> myInts = new List<int>().AddItem(5);

EDIT: Just wanted to be clear that i was hoping for a single generic constraint solution.

+11  A: 

If you need to return the specific type, you can use a generic constraint:

public static TCollection AddItem<TCollection, TElement>(
    this TCollection collection,
    TElement itemToAdd)
    where TCollection : ICollection<TElement>
{
    collection.Add(itemToAdd);
    return collection;
}

I tested this and it works in VS2010.

Update (regarding jQuery):

jQuery chaining works very well because JavaScript uses dynamic typing. C# 4.0 supports dynamic, so you can do this:

public static dynamic AddItem<T>(this ICollection<T> collection, T itemToAdd)
{
    collection.Add(itemToAdd);
    return collection;
}

However, I do recommend the generic constraint version, since it is more type-safe, more efficient, and allows IntelliSense on the returned type. In more complex scenarios, generic constraints aren't always capable of expressing what you need; in those cases, dynamic can be used (though it won't bind to additional extension methods, so it doesn't work well with chaining).

Stephen Cleary
Thanks. I did this at first, but didnt like the 2 generics specification. BUT, i figured out that since the type is implied with TCollection, you actually dont have to specify it in code.
Mike_G
Right. The code to use it is exactly what you had in your question; no explicit type parameters are necessary. I also updated the answer regarding jQuery.
Stephen Cleary
+3  A: 

While I don't have VS open to try this, something along these lines should work:

public static TCollection AddItem<TCollection, TItem>(TCollection collection, 
                                                      TItem itemToAdd) 
where TCollection : ICollection<TItem>
{
    collection.Add(itemToAdd);
    return collection;
}
Adam Robinson
A: 

Just return ICollection<T>instead of object and everything should work like you intended it.

Achim
No, because he's assigning it to a variable of type `List<T>`.
Adam Robinson
Mike_G
Sorry, i was wrong again, Adam is correct.
Mike_G
+1  A: 

You seem to have 2 conflicting goals, and it comes down to what do you want your extension method to return:

  • The instance that invoked the extension method (the collection)
  • OR the item that was added to the collection

From your example usage, quoted here:

List<int> myInts = new List<int>().AddItem(5);

You make it look like you want to return the collection. In any case, that assignment still won't work without a cast, since your extension method would need to have a return type of ICollection, like this:

public static ICollection<T> AddItem<T>(this ICollection<T> collection, T itemToAdd)
{
    collection.Add(itemToAdd);
    return collection;
}

That would allow you to do this:

List<int> myList = (List<int>) new List<int>().AddItem(5);

Now if you'd rather return the object that was added, you still shouldn't have a return type of object. You should take advantage of your generic type parameter, and return T, like this:

public static T AddItem<T>(this ICollection<T> collection, T itemToAdd)
{
    collection.Add(itemToAdd);
    return itemToAdd;
}

However, if you're returning the item that was added, you won't be able to chain like this:

List<int> myList = (List<int>) new List<int>().AddItem(5);

, since the return type of AddItem(5) is not ICollection, but it's T (int, in this case). You can still chain though, just off of the value added, like this:

List<int> myList = new List<int>();
myList.AddItem(5).DoSomethingWithMyInt(); // Not very useful in this case

It seems like the first scenario is more useful (returning the collection), because it does allow you chain, right off of the initial assignment statement. Here's a larger example of that:

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

Or, if you don't want to cast, you can call ToList() on the ICollection that comes back:

List<int> myList = new List<int>().AddItem(1).AddItem(2).ToList();
Samuel Meacham
A: 

EDIT: Just wanted to be clear that i was hoping for a single generic constraint solution.

In this case you're out of luck because return type conversions can be covariant, but not contravariant (i.e. you cannot implicitly convert from ICollection<T> to List<T>), so without a generic return type this cannot be done.

What's wrong with specifying 2 type parameters anyway? They can be inferred by the arguments you provide to the function so you won't even really notice them in your calling code.

John Rasch