Skip to content

Instantly share code, notes, and snippets.

@robertwilczynski
Created January 5, 2010 17:49
Show Gist options
  • Save robertwilczynski/269553 to your computer and use it in GitHub Desktop.
Save robertwilczynski/269553 to your computer and use it in GitHub Desktop.
Evaluating possible null reference expressions
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Linq.Expressions;
using System.Diagnostics;
namespace CallChainMagic
{
public class CallChainMagic
{
private static List<MemberExpression> SplitExpression(Expression<Func<object>> expression)
{
List<MemberExpression> expressions = new List<MemberExpression>();
MemberExpression memberExpression = null;
if (expression.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpression = expression.Body as MemberExpression;
}
expressions.Add(memberExpression);
while (memberExpression.Expression.NodeType == ExpressionType.MemberAccess)
{
memberExpression = memberExpression.Expression as MemberExpression;
expressions.Add(memberExpression);
}
return expressions;
}
public static object TraverseExpression(Expression<Func<object>> expression, Func<MemberExpression, int, object, object> action)
{
List<MemberExpression> expressions = SplitExpression(expression);
object output;
// Evaluating expression root value.
var ex = expressions[expressions.Count - 1];
var del = Expression<Func<object>>.Lambda(ex).Compile();
output = del.DynamicInvoke();
if (output == null)
{
throw new NullReferenceException(
String.Format("Null reference enountered on '{0}'.", ex.Member.Name));
}
for (int i = expressions.Count - 2; i >= 0; i--)
{
//Console.WriteLine(ex.Member.Name);
ex = expressions[i];
output = action(ex, i, output);
}
return output;
}
public static object Eval(Expression<Func<object>> expression)
{
return TraverseExpression(expression, (ex, index, o) =>
{
var output = (ex.Member as PropertyInfo).GetValue(o, new object[] { });
if (output == null && index > 0)
{
throw new NullReferenceException(
String.Format("Null reference enountered on '{0}'.", ex.Member.Name));
}
return output;
});
}
}
class Program
{
private const int Iterations = 1000;
private static void Regular(Order order)
{
if (order == null)
{
throw new NullReferenceException("");
}
if (order.Customer == null)
{
throw new NullReferenceException("");
}
if (order.Customer.ContactInfo == null)
{
throw new NullReferenceException("");
}
if (order.Customer.ContactInfo.Address == null)
{
throw new NullReferenceException("");
}
}
private static void Expressions(Order order)
{
CallChainMagic.Eval(() => order.Customer.ContactInfo.Address.Line1);
}
public static void MeasureTime(string name, Action action, int iterations)
{
Stopwatch watch = new Stopwatch();
watch.Start();
for (int i = 0; i < iterations; i++)
{
try
{
action();
}
catch
{
}
}
watch.Stop();
Console.WriteLine("{0} took {1} (average {2} ms).", name, watch.Elapsed, watch.Elapsed.TotalMilliseconds / (double)iterations);
}
private static void PreformanceTest()
{
Console.WriteLine("Performance test");
Console.WriteLine("============================================================");
Order order = new Order();
Console.WriteLine("order.Customer == null");
MeasureTime("Regular", () => Regular(order), Iterations);
MeasureTime("Expressions", () => Expressions(order), Iterations);
order.Customer = new Customer();
Console.WriteLine("order.Customer.ContactInfo == null");
MeasureTime("Regular", () => Regular(order), Iterations);
MeasureTime("Expressions", () => Expressions(order), Iterations);
order.Customer.ContactInfo = new ContactInfo();
Console.WriteLine("order.Customer.ContactInfo.Address == null");
MeasureTime("Regular", () => Regular(order), Iterations);
MeasureTime("Expressions", () => Expressions(order), Iterations);
order.Customer.ContactInfo.Address = new Address();
Console.WriteLine("order.Customer.ContactInfo.Address != null");
MeasureTime("Regular", () => Regular(order), Iterations);
MeasureTime("Expressions", () => Expressions(order), Iterations);
}
private static void ValueTest()
{
Console.WriteLine("Value test");
Console.WriteLine("============================================================");
Order order = new Order();
order.Customer = new Customer();
order.Customer.ContactInfo = new ContactInfo();
order.Customer.ContactInfo.Address = new Address() { Line1 = "test list" };
var value = CallChainMagic.Eval(() => order.Customer.ContactInfo.Address.Line1);
Console.WriteLine("Expecting '{0}'", order.Customer.ContactInfo.Address.Line1);
Console.WriteLine("Value of 'order.Customer.ContactInfo.Address.Line1' is '{0}'", value);
}
static void Main(string[] args)
{
ValueTest();
Console.WriteLine(); Console.WriteLine();
PreformanceTest();
}
}
public class Order
{
public Customer Customer { get; set; }
}
public class Customer
{
public ContactInfo ContactInfo { get; set; }
}
public class ContactInfo
{
public Address Address { get; set; }
}
public class Address
{
public string Line1 { get; set; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment