Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chaining Extension methods in C#

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.

like image 699
Mike_G Avatar asked Jul 09 '10 19:07

Mike_G


People also ask

What is extension method with example?

Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. Extension methods are static methods, but they're called as if they were instance methods on the extended type.

What is extension method in computer?

In object-oriented computer programming, an extension method is a method added to an object after the original object was compiled. The modified object is often a class, a prototype or a type. Extension methods are features of some object-oriented programming languages.

Why are extension methods static?

Extension methods are static because they get the instance passed in via the first parameter, and they don't act on the actual instance of their declaring class. Also, they're just a syntactic sugar. CLR doesn't support such a thing.

What is extension method in MVC?

What is extension method? Extension methods in C# are methods applied to some existing class and they look like regular instance methods. This way we can "extend" existing classes we cannot change. Perhaps the best example of extension methods are HtmlHelper extensions used in ASP.NET MVC.


4 Answers

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

like image 102
Stephen Cleary Avatar answered Oct 01 '22 21:10

Stephen Cleary


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;
}
like image 31
Adam Robinson Avatar answered Oct 01 '22 22:10

Adam Robinson


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();
like image 41
Samuel Meacham Avatar answered Oct 01 '22 23:10

Samuel Meacham


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.

like image 33
John Rasch Avatar answered Oct 01 '22 23:10

John Rasch