I am a huge proponent of extension methods in .NET (and other languages). I believe they are a gateway to making complex code easier to follow and understand, also helps to maintain the object oriented paradigm that .NET is built on. They are also key to the notion of Fluent APIs which has been permeating throughout the programming space for the past year or so.
The idea of a Fluent API is an API that reads very much like an English sentence and is considered a tremendous advancement in readable code.
string value = "500"; int myValue = value.AsInt();
This is a sample code snippet that you see a lot in my projects as I tend to work a library I have created which contains many extension methods for formatting and parsing of values. Notice how as you read the code it flows and its concise with the parsing logic centralized. Traditionally you might have seen code such as this:
string value = "a500"; int myValue = int.MinValue; int myValue = int.MinValue; int.TryParse(value, out myValue); myValue = Convert.ToInt32(value);
Using TryParse, which is a standard method common to all primitive value types in .NET, this code does not throw an exception and myValue has a value of zero at operations end. I dont like this because I would expect TryParse to leave the value of myValue unchanged if the parse fails, it does not. As for the use of Convert it basically emulates what would happen if you called int.Parse on this value, it throws a FormatException.
In addition to the obvious problems of repeating this code anywhere you need to parse an int from a string, I find this to be very procedural. Now there is no question that all programming, no matter how hard we try is going to be at some level procedural, however, my goal is always to minimize such code and keep things as Object Oriented as possible, after all that is what OO languages are designed for and, not surprisingly, very good at.
Extension methods are also exceptionally useful for custom formatting and outputting for certain types. For example, lets say you wanted to output a DateTime object is standard notation for the United States, mm/dd/yyyy. Well simply calling ToString(“d”) easily accomplishes this. But littering your code with this code could turn into a potentially horrendous maintenance problem if your boss decides it ought to be dd/mm/yyyy (which is common in Europe), but use an extension method and your code could look like this:
DateTime dt = new DateTime(1983, 1, 13);
Console.WriteLine(dt.AsStandardFormatString());
Using this approach you can have a standard way out outputting a DateTime, perhaps stored somewhere in a different project, that you can easily update, perhaps make culture aware, all without your application ever being the wiser.
So, while Extension Methods are very useful for type conversion libraries and provides standardization out formats and outputs for types, they have another great use. Provides reusable ways to affect changes on objects, collections in particular, while maintaining a very clean interfaces and emphasizing “changes as a unit of work”. I am sure someone has published something relating to the notion of change as a unit of work, but let me elaborate a little further.
The idea of change as a unit of work derives from the Single Responsibility Principle and Open Closed Principle laid out by Robert Martin. The idea is that everything has one responsibility and it should carry out that responsibility effectively and thoroughly, from the construction of methods to the definition of classes, they amalgamation of these constructs comprises the application and only by working together can they do such. Consider the following pieces of code that comes from some code that I wrote this week for a rather complex process that I am working on at work.
var scheduleList = dayList.GroupByShiperId().ConvertToSchedule();
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
(For those concerned, yes I did mutate this code so that it does not reflect what was actually written).
The idea here is to take a very complicated process and break it down into smaller units and then create functions to carry out those units of work. Hence any changes to the information is a unit of work. Easily testable and thanks to extension methods, very clean from an API standpoint. But extension methods are only a small part of this process, understanding the interfaces and classes you have available to you is critical as well as it can help you move code to where it should be. The method in question could easily have been a 200 line function that would have been a nightmare for anyone to maintain. Instead its five functions, all with less then 12 lines working together and producing a result.
Using this approach you can be less procedural in your code and leverage the object oriented paradigm of the .NET framework and help people who will come after you understand what is happening. Thus together with good function naming, you can create similar clean reusable APIs for your projects and further leverage the concept of change as a unit of work.
I tend to discourage the creation of extension methods for common primitive types to avoid “IntelliSense pollution”. You might add a small blurb about organizing your extension methods into different namespaces. That way, your extensions aren't imported unless they're needed, and you can import your collection extensions without importing your string extensions, etc.
LikeLike
hi everybody
I just wanted to introduce myself to everyone!
Can't wait to get to know you all better!
-Marshall
Thanks again!
LikeLike
hey
Just saying hello while I read through the posts
hopefully this is just what im looking for looks like i have a lot to read.
LikeLike
Shalom
It is my first time here. I just wanted to say hi!
LikeLike