Recently over on the alt.net list there has been alot (read: way too much) talk about extension methods. The running consensus seems to be that extension methods are great for extending someone else's API but have no place in code that you control.
This view point of many was expressed well by Peter Richie as
I see no need to release a framework that includes extension methods for classes, public or not. Releasing updates to a framework that include extension methods for classes is a different story. The ability to maintain an established level of quality on existing binaries while still offering the ability to add behaviour to the classes in those binaries is very compelling. But, I agree with Chad that they be avoided on classes whenever possible. With .NET 3.5 you have to reference new assemblies to bring in extension methods, they don’t magically appear in the existing assemblies.
and Chad Myers as
I know I'm doom and gloom, but it seems that ext methods should be avoided where ever possible and treated as smells when unavoidable. They're great in Linq, so the smell is justified, but beware of patchwork ext method frameworks.
I hope to show some code that benefits from extension methods even on classes that I completely control.
Fluent Interfaces
Now I am sure you are thinking "Hey he is going to show how to use native types and extension methods". I won't do this, I think enough people already have and it wouldn't serve my case well for using extension methods on classes that I control.
I like fluent interfaces, especially for value objects. I have used a builder pattern for some time to create these. If people want I can put up a post explaining more on the relationship between value objects and builders but here is some quick example code.
public struct CostBuilder
{
private readonly int Shares;
private readonly Money Price;
public CostBuilder Bought(int _Shares)
{
return new CostBuilder(_Shares, this.Price);
}
public CostBuilder Sold(int _Shares)
{
decimal alteredprice = this.Price;
if (alteredprice > 0)
{
alteredprice = 0 - alteredprice;
}
return new CostBuilder(-_Shares, alteredprice);
}
public CostBuilder For(Money _Price)
{
if (this.Shares > 0)
{
return new CostBuilder(this.Shares, _Price);
}
else
{
return new CostBuilder(this.Shares, 0 - _Price);
}
}
public CostBuilder(int _Shares, Money _Price)
{
this.Shares = _Shares;
this.Price = _Price;
}
public static implicit operator Cost(CostBuilder Builder)
{
return new Cost(Builder.Shares, Builder.Price);
}
}
|
| Listing 1: A simple Builder |
Aaron over at Elutian wrote a post Fluent Fixtures which uses a similar pattern (it is in general similar to the builder pattern). We would then take our builder and put a static instance onto a class named New like this...
static class New
{
public static CostBuilder Cost;
}
|
| Listing 2: New class which holds builder |
Finally we can use the above example in a manner similar to
Cost Purchased = New.Cost.Bought(100).For(
New.Money.InCanada(20000)
);
|
| Listing 3: Using code |
Now enough about the how to and back to the point of the post Extension Methods... As said earlier we could make this interface nicer possibly by decorating native types with extension methods but the challenge is to show a case where we control the class and we receive benefit from using extension methods.
We have a problem here productivity wise, every item we do this with lives on the New class. In a large domain this quickly adds up. The easy answer is to start putting New classes into various namespaces. Let me just say that this is not only bad but its really annoying. Enter Extension Methods
Context Sensitivity
Extension methods allow me to only have a method visible if you are currently referencing my namespace. This can be used to tweak intellisense a bit so I can only show you what is relevant in your current context when you view the New class unfortunately static methods aren't supported but we can get around that using a static instance.
public class Create
{
public static Builder New = new Builder();
}
|
| Listing 4: Create and Builder |
Now that I have these I can decorate the Builder with extension methods like:
public static CostBuilder Cost(this Builder s)
{
return new CostBuilder();
}
|
| Listing 5: Extension Method for Builder |
Now when I go to use it, Create.New will ONLY show me the Cost() method if Cost is in my currently referenced namespaces (as opposed to showing me every object I could every construct). I can do this with all of my objects (I would imagine in a big domain or a generic domain of value objects you may have hundreds of these).
This makes my finding of these builders more efficient as it will automatically ignore the ones that have no place in my current context.
Other Uses
There are lots of things that could use a similar pattern to this. I am simply using it to provide me context sensitive information that I would not otherwise see in a static state. Others that come to mind might be.
IMyService Service = Instance.For.IMyService().WithContext(Context);
or to layer something like this over an IOC container so I only see the interfaces that are currently in my context listed as available options.
ISomeService MyService = Container.For.ISomeService;
Is this good? I'm not sure but the ability to use things in a context sensitive fashion like this certainly seems to offer some interesting possibilities. I can come up with lots of fun ways of abusing using this :)
Posted
Wed, Dec 5 2007 2:19 AM
by
Greg