Posts Tagged null

Returning null from a class constructor?!

Just as an exercise, I thought I’d examine common creation patterns in C# and how operator overloading can expand these.

Typically, if the parameters passed into a class constructor are not valid and thus cannot create an instance of itself, an Exception is thrown. ArgumentNullException is most common when a parameter is null and shouldn’t be. Also common are ArgumentExceptions and FormatExceptions, especially when a constructor takes in a string that must abide by a syntax and doesn’t.

public class Expression
{
	public Expression(string pattern)
	{
		if (Expression.IsInvalidPattern(pattern))
		{
			throw new ArgumentException("pattern is invalid");
		}
		
		// Do something
	}
}

Then a try/catch block is used to catch Exceptions when instantiating the type:

Expression expression;
try
{
	expression = new Expression(pattern);
}
catch(ArgumentException)
{
	// Abort
}

An alternative would be to simply indicate that the instantiated class is in an invalid state:

public class Expression
{
	public readonly bool IsInvalid = false;
	
	public Expression(string pattern)
	{
		if (Expression.IsInvalidPattern(pattern))
		{
			this.IsInvalid = true;
			return;
		}
		// Do something
	}
}

The invalid state is readonly so that it can only be set by the constructor, and is then checked for before performing any operations with the new Expression:

Expression expression = new Expression(pattern);

if (expression.IsInvalid)
{
	// Abort
}

The Parse/TryParse pattern is also common. Instead of an Exception, readonly field, or get-only property, a static TryParse method is used that returns true or false, depending on whether the type can be created:

public class Expression
{
	public static bool TryParse(string pattern,
		out Expression expression)
	{
		if (Expression.IsInvalidPattern(pattern))
		{
			expression = null;
			return false;
		}
		
		expression = new Expression(pattern);
		
		return true;
	}
}

TryParse() is then used as in this example:

Expression expression;

if (!Expression.TryParse(pattern, out expression))
{
	// Abort
}

Another pattern which I haven’t seen used allows an invalid object to emulate a null reference. There are better solutions than this, which is why it isn’t used, but by using operator overloading, it can be accomplished nonetheless.

public abstract class Errorable
{
	protected bool isError = false;

	public static bool operator ==(Errorable left, object right)
	{
		if (right == null)
		{
			return left.isError;
		}
		
		return (left.GetHashCode() == right.GetHashCode());
	}

	public static bool operator !=(Errorable left, object right)
	{
		if (right == null)
		{
			return !left.isError;
		}
		
		return (left.GetHashCode() != right.GetHashCode());
	}
} 

public class Expression : Errorable
{
	public Expression(string pattern)
	{
		if (Expression.IsInvalidPattern(pattern))
		{
			base.isError = true;
			return;
		}
		// Do something
    }
} 

An Expression can then be evaluated to test for null, although we know that “expression” really isn’t a null referenced, which is one reason why this pattern is probably a bad idea.

Expression expression = new Expression(pattern);

if (expression == null)
{
	// Abort
}

Lastly, overloading the ! operator allows us to evaluate an object as fasly, which other languages (e.g. JavaScript) do by default:

public abstract class Errorable
{
	protected bool isError = false;

	public static bool operator !(Errorable errorable)
	{
		if (errorable == null || errorable.isError);
	}
}

public class Expression : Errorable
{
	public Expression(string pattern)
	{
		if (Expression.IsInvalidPattern(pattern))
		{
			base.isError = true;
			return;
		}
		// Do something
    }
}

To test an Expression as invalid, the ! operator is used:

Expression expression = new Expression(pattern);

if (!expression)
{
	// Abort
}

The first two patterns are common, and probably why the last two aren’t used. Generally, if there’s already a good, well-established pattern, there’s no reason to try something else. The alternative shown creation patterns were simply an exercise in operator overloading.

kick it on DotNetKicks.com

Comments (4)