Tuesday 8 April 2014

C# Property by reference

I wanted to write a generic function to test a validation method of my object, but I wanted the function to be able to manipulate specific properties, like turning boolean values on or off.

Now one possibility is to pass the value by reference which is often something I don't like very much, but won't get into that too much right now, but since we are dealing with properties here the usual more elegant way is to create a wrapper or lambda function of some kind.

Now natrually before coding anything a quick google/SO search reveals some basic solutions and as one might expect involves some reflection as one CodeProject article here.

However I usually use dynamic reflection as a last resort as there is too much room for error here and referring to fields as strings just doesn't seem like the cleanest way. Also a super simple solution is by an SO post made here  (scroll down a bit) which simply just uses a small wrapper class you specifiy the getter and setter using 2 lambda expressions. This is certainly clean and simple, which I like, however I tried to create a function even simpler and more concise which also uses the compiler to ensure correctness, so I came up with this.

So much like the first version to use some reflection except no strings but using expressions. First extracting the property info from the expression, a nice clean wrapper object to help with this from this SO post:

public static class PropertyHelper<T>
 public static PropertyInfo GetProperty<TValue>(
  Expression<Func<T, TValue>> selector)
  {
  Expression body = selector;
  if (body is LambdaExpression)
  {
   body = ((LambdaExpression)body).Body;
  }
  
  switch (body.NodeType)
  {
   case ExpressionType.MemberAccess:
    return (PropertyInfo)((MemberExpression)body).Member;
    break;
   default:
    throw new InvalidOperationException();
  }
  }
}

So taking that a little further, creating a helper class to generate a lambda getter and setter

public static class PropertyHelper
{
 public static Func<TValue> GetPropertyGetter<T, TValue>(T value, Expression<Func<T, TValue>> selector)
 {
  var propInfo = (PropertyInfo)((MemberExpression)(selector).Body).Member;
  return () => (TValue)propInfo.GetValue(value, null);
 }

 public static Action<TValue> GetPropertySetter<T, TValue>(T value, Expression<Func<T, TValue>> selector)
 {
  var propInfo = (PropertyInfo)((MemberExpression)(selector).Body).Member;
  return v => propInfo.SetValue(value, v, null);
 }
}

Small note: This requires you to use a lambda expression for the property any other expression will break it, I didn't add all the checks to make it more terse. So Then the usage would simply be:
var getter = PropertyHelper.GetPropertyGetter(world, w => w.Hello);
var setter = PropertyHelper.GetPropertySetter(world, w => w.Hello);

And then you can get the property by calling:

getter();
setter("Hello");

Extending that a little further by adding a nice wrapper class:

public class PropertyWrapper<TValue> : IPropertyWrapper<TValue>
{
 protected PropertyInfo _propInfo;
 protected object _instance;

 public PropertyWrapper(PropertyInfo propinfo, object instance)
 {
  _instance = instance;
  _propInfo = propinfo;
 }

 public object Instance
 {
  get { return _instance; }
 }

 public TValue Value
 {
  get { return (TValue)_propInfo.GetValue(_instance, null); }

  set
  {
   _propInfo.SetValue(_instance, value, null);
  }
 }
}

Then you can pass around the wrappers:
var wrapper = PropertyHelper.GetPropertyWrapper(world, w => w.Hello)
Now you can easily use this in your generic method and manipulate any property:
  
private static void TestCondition(IPropertyWrapper condition)
{
 condition.Value = false;
 Console.WriteLine(condition.Instance.Validate());
 condition.Value = true;
 Console.WriteLine(condition.Instance.Validate());
}
Dynamic objects are of course another way to do this however you then still are missing out on the compile time checking, so I haven't coded out this solution, but I think I'd still rather use this approach then reflection by string name method. So there you have it, this seems like a nice clean way to create property references, you can take it one more step further by creating an object extension method for this, but I usually try to avoid this as it can clutter up the code completion just a little bit too much. But if you can think of an even better cleaner way in C#