I Love C# Extension Methods
Some useful C# Extension Methods

Introduction
I found a nice way to avoid nested "if
"s or nested property checks against null
:
var root = new Node(args);
if (root.Left != null && root.Left.Left != null) {
// do something
}
var l = root.Left;
if (l != null) {
var ll = l.Left;
if (ll != null) {
// do something
}
}
// or with GetProperty
var ll=root.GetProperty(_ => _.Left).GetProperty(_ => _.Left);
if (ll != null){
// do something
}
Background
Extension methods can extend any type in the .NET world. This is a sample from Microsoft's MSDN from here.
public static class Sample {
// from Extension Methods (C# Programming Guide
public static int WordCount(this String str) {
return str.Split(new char[] { ' ', '.', '?' },
StringSplitOptions.RemoveEmptyEntries).Length;
}
}
With this, you can call the extension method as if the type (string
here) had implemented it.
int x="1 2 3 4".WordCount();
This is only syntax sugar. You can also write:
int x=Sample.WordCount("1 2 3 4");
It's a static
method - and the compiler allows to write the call in both syntax-versions - that's all.
and...?
I love this sugar because I can check the parameter 'str
':
public static class Sample {
public static int MyWordCount(this String str) {
if (string.IsNullOrEmpty(str)) {
return 0;
}
return str.Split(new char[] { ' ', '.', '?' },
StringSplitOptions.RemoveEmptyEntries).Length;
}
}
This a good thing since I can call:
string s=null; int x=s.MyWordCount();
and everything works fine - no NullReferenceException
.
Combine this with lambda expression and see there GetProperty
is born.
Using the Extension Method GetProperty
In the following code, I use no additional classes, and the samples become a little bit silly, but I hope you see how GetProperty
works.
static void Main(string[] args) {
// get the 9th parameters's length or 0 (= default(int))
var usedefault = args.Skip(8).FirstOrDefault().GetProperty(_ => _.Length);
System.Console.Out.WriteLine("1) usedefault
: " + usedefault.ToString());
// get the 9th paramters's length or -1
var withdefaultValue = args.Skip(8).FirstOrDefault().GetProperty
(_ => _.Length, -1);
System.Console.Out.WriteLine("2) withdefaultValue
: " + withdefaultValue.ToString());
// get the 9th parameter in upper case or the %COMPUTERNAME%
var withdefaultValueFunction1 = args.Skip(8).FirstOrDefault().GetProperty(
_ => _.ToUpperInvariant(),
() => System.Environment.MachineName);
System.Console.Out.WriteLine("3) withdefaultValueFunction1
: " + withdefaultValueFunction1.ToString());
// get the 9th parameter's Length or a expensive computing
// (the length of the first file in the current directory)
var withdefaultValueFunction2 = args.Skip(8).FirstOrDefault().GetProperty(
_ => _.Length,
() => (new System.IO.DirectoryInfo(@".")).GetFiles
("*.*", System.IO.SearchOption.TopDirectoryOnly)
.FirstOrDefault()
.GetProperty(_ => _.Length));
System.Console.Out.WriteLine("4) withdefaultValueFunction2 : " +
withdefaultValueFunction2.ToString());
}
and the results are:
1) usedefault : 0
2) withdefaultValue : -1
3) withdefaultValueFunction1 : myhostname
4) withdefaultValueFunction2 : 10752
Okay - I think example 4 makes absolutely no sense ( except here;-). But if you call the program with enough parameters, the Length
property will be used. When you invoke the program without parameters, the lambda expression is called. I don't use an if
nor a ?:
to check if the property can be accessed or rather the expression can be called.
The Implementation of GetProperty
The implementation is small - and if you really like generic programming - nice, if not ... I did this for you - you can just use it.
public static TP GetProperty<tc>(this TC that, Func<tc> func)
where TC : class {
if (object.ReferenceEquals(that, null)) {
return default(TP);
}
return func(that);
}
public static TP GetProperty<tc>(this TC that, Func<tc> func, TP defaultValue)
where TC : class {
if (object.ReferenceEquals(that, null)) {
return defaultValue;
}
return func(that);
}
public static TP GetProperty<tc>(this TC that, Func<tc> func, Func<tp /> funcDefault)
where TC : class {
if (object.ReferenceEquals(that, null)) {
return funcDefault();
}
return func(that);
}
Using the Extension Method GetProperty II
For the next sample, I create this simple (Tree-)Node
class.
public class Node {
public Node(string[] args) {
this.Name = "root";
var node = this;
foreach (var arg in args) {
if ((arg.Length % 2) == 0) {
node = node.Left = new Node(arg);
} else {
node = node.Right = new Node(arg);
}
}
}
public Node(string name) {
this.Name = name;
}
public readonly string Name;
public Node Left { get; set; }
public Node Right { get; set; }
}
//
static void Main(string[] args) {
if (args.Length == 0) {
args = new string[]
{ "left", "right", "Left", "Right", "LEFT", "RIGHT" };
}
var root = new Node(args);
// before GetProperty
if (root.Left != null && root.Left.Left != null) {
var ll1 =root.Left.Left;
// ...
}
// or with GetProperty
var ll2=root.GetProperty(_ => _.Left).GetProperty(_ => _.Left);
if (ll2 != null){
//...
}
}
The "var ll2" will be null
or not - but it doesn't raise an NullReferenceException
.
My point is if you can easily write...
if (root.Left != null && root.Left.Left.Left != null)
{ var name =root.Left.Left.Left.Name;}
...and you have forgotten one Left
- or -
you check for...
root.Left.Left.Left != null
...and use later
var node=root.Left.Right.Left;.
If you're using GetProperty
, you write the one expression once - and no copy & paste and modify / no try - debug - modify - try - debug - modify - will bring this one expression into two different expressions.
In the next samples, I'll hope I can show you how nice this can be: (btw _ underscore is a valid identifier).
static void Main(string[] args) {
if (args.Length == 0) {
args = new string[]
{ "left", "right", "Left", "Right", "LEFT", "RIGHT" };
}
var root = new Node(args);
try {
var lr = root.Left.Right;
// root.Left can be null so root.Left.Right might fail
// (not with the default args)
System.Console.Out.WriteLine("5) lr.Name = " + lr.Name);
} catch (NullReferenceException) {
System.Console.Out.WriteLine("5) lr NullReferenceException.");
}
try {
var ll = root.Left.Left;
// root.Left can be null so root.Left.Left might fail
// (with the default args it will)
System.Console.Out.WriteLine("6) ll.Name = " + ll.Name);
} catch (NullReferenceException) {
System.Console.Out.WriteLine("6) ll NullReferenceException");
}
try {
var lr = root.GetProperty(_ => _.Left).GetProperty(_ => _.Right);
System.Console.Out.WriteLine("7) lr.Name = " + lr.Name);
// lr can be null so lr.Name might fail (not with the default args)
} catch (NullReferenceException) {
System.Console.Out.WriteLine("7) lr NullReferenceException");
}
try {
var ll = root.GetProperty(_ => _.Left).GetProperty(_ => _.Left);
System.Console.Out.WriteLine("8) ll.Name = " +
ll.GetProperty(_ => _.Name, "ll is null"));
} catch (NullReferenceException) {
System.Console.Out.WriteLine("8) NullReferenceException");
}
}
and the results are:
5) lr.Name = right
6) ll NullReferenceException
7) lr.Name = right
8) ll.Name = ll is null
The last sample (8) you never get an error here - even with other parameter.
Of course, you can create special Extension methods for Left
and Right
- I'll do this for a few properties I really often use and the objects may be null
- I do this if the code becomes more readable - but I do this only for a small number of properties. GetProperty
works for all.
public static class NodeExtension {
public static Node GoLeft(this Node that) {
if (that == null) {
return null;
} else {
return that.Left;
}
}
public static Node GoRight(this Node that) {
if (that == null) {
return null;
} else {
return that.Right;
}
}
}
...
static void Main(string[] args) {
...
var lrlr = root.GoLeft().GoRight().GoLeft().GoRight();
System.Console.Out.WriteLine("9) lrlr.Name = " +
lrlr.GetProperty(_ => _.Name, "lrlr is null"));
}
...
It often makes no difference if the one property is null
or the other property is null
, the interesting object cannot be reached.
Often a method checks this, checks the other thing and after all, it just calls the method that really does something - or does nothing useful (or throws an exception) because of missing information.
Two things at last:
struct
s cannot benull
. SoGetProperty
only makes sense with classes.- You have to add the
using NamespaceOfExtension;
in each file you want to use it.