Skip to content

Instantly share code, notes, and snippets.

@ValdemarOrn
Last active December 18, 2015 02:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ValdemarOrn/5709965 to your computer and use it in GitHub Desktop.
Save ValdemarOrn/5709965 to your computer and use it in GitHub Desktop.
public static class EvalExtensions
{
/// <summary>
/// Evaluates an expression with the object as a parameter.
/// If the expression can be successfully evaluated, it returns the result.
/// If an exception occurs, e.g. NullReferenceException or other,
/// it returns a default value.
/// </summary>
/// <typeparam name="TArg">Type of the argument object</typeparam>
/// <typeparam name="TRes">Type of result value</typeparam>
/// <param name="arg">Input argument</param>
/// <param name="expression">Expression to evaluate</param>
/// <param name="defaultValue">Default value in case the expression cannot be evaluated</param>
/// <returns></returns>
public static TRes Eval<TArg, TRes>(this TArg arg, Func<TArg, TRes> expression,
TRes defaultValue = default(TRes))
{
try
{
if (((object)arg) == null)
{
return defaultValue;
}
return expression(arg);
}
catch (Exception)
{
return defaultValue;
}
}
/// <summary>
/// Evaluates an expression with the object as a parameter.
/// If the expression can be successfully evaluated, it returns the result.
/// If an exception occurs, e.g. NullReferenceException or other,
/// it returns a Nullable&lt;&gt; default value.
/// </summary>
/// <typeparam name="TArg">Type of the argument object</typeparam>
/// <typeparam name="TRes">Type of the Nullable&lt;&gt; result value</typeparam>
/// <param name="arg">Input argument</param>
/// <param name="expression">Expression to evaluate</param>
/// <param name="defaultValue">Default value in case the expression cannot be evaluated</param>
/// <returns></returns>
public static Nullable<TRes> Eval<TArg, TRes>(this TArg arg, Func<TArg, TRes> expression,
Nullable<TRes> defaultValue) where TRes : struct
{
try
{
if (((object)arg) == null)
{
return defaultValue;
}
return expression(arg);
}
catch (Exception)
{
return defaultValue;
}
}
}
class Person
{
public DateTime? Birthday;
}
public static class Program
{
public static void Main(string[] args)
{
var list = new List<Person>();
var p = new Person();
p.Birthday = DateTime.Now;
list.Add(p);
int? year = null;
// make null checks for every object in the chain... not very pleasant to read
if(list != null && list.Count > 0 && list[0] != null && list[0].Birthday != null)
{
year = list[0].Birthday.Value.Year;
}
// use Eval, with null as the default argument
int? year2 = list.Eval(x => x[0].Birthday.Value.Year, null);
}
}

Evaluating expression chains of nullable objects in C#

Evaluating deeply nested properties can be tedious if you want your code to be safe from the dreaded NullReferenceException. Although sometimes considered a bad habit, occasionally you just have to dive into an object hierarchy to fish out that one value you're interested in.

In these cases, you must check every object in the hierarchy, usually falling back to a default value in case the value you're looking for isn't there. Even so, you often end up with null references in places you never thought to anticipate.

Using these simple extension methods can make it a lot easier (and prettier) to access deeply nested values. It also lets you set an explicit default value in case your expression fails.

A word of caution

Since the purpose of these functions is to provide a logical fallback value in case an expression cannot be evaluated, it can potentially mask an underlying bug in your code. Don't make your expressions too complicated, because when they fail, all you'll see is that pretty default value.

Use responsibly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment