65.9K
CodeProject is changing. Read more.
Home

I Love C# Extension Methods

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.22/5 (7 votes)

Oct 20, 2011

CPOL

3 min read

viewsIcon

57034

downloadIcon

358

Some useful C# Extension Methods

avoid this

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:

  • structs cannot be null. So GetProperty only makes sense with classes.
  • You have to add the using NamespaceOfExtension; in each file you want to use it.