It seems pretty pointless to me.
It's more code that you have to maintain - and in a year's time no one will remember the debates you had and half your code will use string.Format
the other half will use this method.
It's also distracting you from the real task of developing software that solves real problems.
You've probably wasted more money by having these discussions that it would have cost the company to buy ReSharper licenses for everyone.
My observation is that a lot of folk misuse multiple inheritance without working through composition type questions.
The "classic" OO example of a car leads to this problem. For example, we'll say that a car breaks down to the following parts:
- it's a vehicle
- there are tires
- there is an engine
- there is a chassis
- there are seats
- and so on...
so we'll define a class that looks like this:
public class Car : Vehicle, Tires, Engine, Chassis, Seats {}
All well and good, but it presents some significant problems with encapsulation and constructors / destructors. And it doesn't accurately represent what a car looks like in the real world.
If we start asking composition questions then we'll see some nuances here that weren't at first obvious.
- is a car a vehicle? (yes)
- is a car a tire or set of tires? (no, it has a set of tires)
- is a car an engine? (no)
- is a car a seat? (no)
- etc ...
So now our class can look something like this:
public class Car : Vehicle
{
public Tires tires; //ie, myCar _has_ a set of tires.
protected Engine engine;
private Chassis chassis;
protected Seats seats;
}
Which is also all well and good. But now we start scratching our heads and say "you know, I really wish that when I instantiate a car that it has to have tires, an engine, and a chassis."
So now we can turn Tires, Engine, and Chassis into interfaces and we'll have the following class. I'm prefixing the interfaces with "I" to make the distinction more clear.
public class Car : Vehicle, ITires, IEngine, IChassis
{
protected Seats seats;
}
Interfaces are like contracts. When a class implements an interface, it's guaranteeing it will provide certain methods and behaviors.
And now things really are all well and good. Our model of the car accurately reflects the real world representation of the same Object, including some guarantees about the resemblance between the two courtesy of the Interfaces. We have avoided the icky issues that multiple inheritance can create.
That's not to say that multiple inheritance is always bad. However, it's not really needed most of the time which is why languages like Java and C# don't allow for multiple inheritance.
Best Answer
Extension methods offer you an opportunity to reason about problems a totally different way-- functionally. Functional programming is a totally different paradigm from object-oriented and has certain advantages. For example, when you use functional thinking and immutable variables, your program instantly becomes thread-safe without using any locks or concurrency mechanisms at all. In addition, when you write pure functions, unit testing is a snap, and you don't need any mocks or stubs at all. What's more, extension methods allow you to put methods on interfaces-- no class required at all-- and you only need to implement them once and they will operate on any class that implements the interface, even if they don't share a base class.
Here are the rules to make your code work well in this fashion:
Write your extension methods so that they are pure. They should not access any state outside of the method inputs and should not modify any state at all.
Write your extension methods so they always return a new instance, rather than modifying the instance that was handed to it.
Write them generically when possible; this allows type-safe method chaining that can transform types.
If you need the methods to have dependencies, inject them as parameters, e.g.
Where
accepts a delegate which allows the caller to "inject" a logic engine (exposed as aFunc<T,bool>
) that determines whether a row will be included.By the way, all LINQ methods conform to these rules.
So for example, if you have several types of classes that have a "shipped" status, you could implement this:
This will work with any shippable item without requiring any upcasting or downcasting; it is pure, so you don't need to inject any dependencies and there is nothing to mock or isolate; and it will allow type-safe method chaining if you write other extension methods.
This is not a bad way to go at all. The only way it could be bad is if you think you are writing object-oriented code. You're not. You're writing functional code, and that can be a good thing.