Add Logic to Interfaces using Extension Methods
Ok, a little back story for this one:
I have a class called "Farm" defined in an assembly. It's a business class, if you will, that represents a farm in our company.
In the same assembly I have an interface that I call "IFarmsProvider". It defines all the things that you can do with farms. Methods like "GetFarmsBySite" and "SaveFarm".
Obviously my IFarmsProvider interface doesn't contain any logic - interfaces aren't allowed to. All it does is tells me what methods I need to implement when I start defining (in a new assembly) the implementing provider class for farms. For example, a FarmsSqlProvider class which knows how to get farms from a SQL Server database.
The Farm class has an "ID" parameter (a nullable int) which really should be read-only to the outside world. After all - once you've retrieved a farm from the database, it makes no sense to change its ID: That's the thing that the provider will use to match that object up with its in-database counterpart when saving.
So that's fine - I can make Farm.ID a read-only property and add a constructor that accepts an integer. Now I can construct Farm objects straight out of the database. If the user creates a Farm using the default (parameterless) constructor, then the ID is null and the provider knows that this is a new farm that needs to be created in the database when it's saved.
Here's the rub: When I add new farms to the database, I want to set that farm's ID to the new farm row's ID. It's an identity int in SQL Server so it gets the next available value. But ... Farm.ID is read-only! How the heck am I supposed to change it?
Right now we've taken the easy way out and made Farm.ID a read/write property. It lets us set the ID in the providers we implement, and we just have to hope that other developers don't go changing the value before they save existing farms. Not ideal.
So today I played a bit in beta 2 of Visual Studio 2008, and tried something out.
First, I redefined Farm.ID like this:
public int ID { get; internal set; }
So only classes within the assembly can set the ID of a farm.
Next, I defined a little extension method for my IFarmsProvider interface, like this:
public static class FarmsProviderExtensions
{
public static void SetFarmID(this IFarmsProvider provider, Farm farm, int id)
{
farm.ID = id;
}
}
Now any class that implements IFarmsProvider has a method called SetFarmID! That method has access to the internal setter on the Farm.ID property!
Strictly speaking, this doesn't stop other developers from calling the SetFarmID property if they really want to change an existing farm's ID. However, it does stop them from absent-mindedly typing "myFarm.ID = 3" somewhere in the middle of a method, since Farm.ID is not settable from outside its assembly.
Extension methods in .NET 3.5 are a great way to add "logic" to interfaces. It's almost like having multiple inheritance! Boo ya!
# Trackback from Jason Haley on 11/10/2007 12:17 AM
Comments
# Omer van Kloeten
11/10/2007 12:36 AM
Great idea!
> It's almost like having multiple inheritance! Boo ya!
Took the words right out of my mouth :)