Tuesday 9 June 2009

Accessing Properties without reflection and magic strings

Whilst some great refactoring tools will take care of magic strings for you, the use of them still grates on me whenever I am forced into using them and whilst using reflection can be very powerful it can also have a performance hit when used frequently.

Now, what do I mean by accessing properties?

What if you need to tell some object to use a property and its value, but it may need to access this property several times and the value might change. The last is the crux of the problem here.

If you pass the property in using standard property accessors, you will get the value as it stands at that time. If the property is mutable then this maybe ok, but what if it's not or the mutable object is replaced?

Well, you could pass the object and a magic string and use reflection to access the property when the new objects needs access, but there is always a penalty to using this approach.

However, using Lambdas and the power of closures, you now have an alternative to this approach:

Take a look at the following code:


using System;
using System.Diagnostics;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var vio = new SomeVeryImportantObject { VeryImportantProperty = "Top Secret" };
var moreImportantObject = new NeedInfoToWork();

moreImportantObject.SetAnothersPropertyToUseLaterUsingDelegates(() => vio.VeryImportantProperty);
moreImportantObject.SetAnothersPropertyToUseLaterUsingReflection(vio, "VeryImportantProperty");


vio.VeryImportantProperty = "Ok, not so important";

Console.WriteLine(moreImportantObject.GetValueNowUsingReflection());
Console.WriteLine(moreImportantObject.GetValueUsingClousers());

const int timesToAccess = 1000000;

var sw = Stopwatch.StartNew();

for (int i = 0; i < timesToAccess; i++)
{
var value = moreImportantObject.GetValueNowUsingReflection();
}

sw.Stop();
Console.WriteLine("Time Taken using reflection {0}ms", sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();

for (int i = 0; i < timesToAccess; i++)
{
var value = moreImportantObject.GetValueUsingClousers();
}

sw.Stop();
Console.WriteLine("Time Taken using closures {0}ms", sw.ElapsedMilliseconds);

Console.ReadKey();
}
}

public class SomeVeryImportantObject
{
public string VeryImportantProperty { get; set; }
}

public class NeedInfoToWork
{
private Func _propertyAccess;
private object _objectToAccess;
private string _propertyName;

public void SetAnothersPropertyToUseLaterUsingDelegates(Func propertyAccess)
{
_propertyAccess = propertyAccess;
}

public void SetAnothersPropertyToUseLaterUsingReflection(Object objectToGetPropertyValueFrom, string propertyName)
{
_objectToAccess = objectToGetPropertyValueFrom;
_propertyName = propertyName;
}

public string GetValueNowUsingReflection()
{
return _objectToAccess.GetType()
.GetProperty(_propertyName)
.GetValue(_objectToAccess, null)
.ToString();
}

public string GetValueUsingClousers()
{
return _propertyAccess();
}
}
}


This produced the following results on my computer:

> Ok, not so important
> Ok, not so important
> Time Taken using reflection 2076ms
> Take Taken using closures 56ms

Enough said.

We have improved performance and removed the use of magic strings.

Submit this story to DotNetKicks Shout it

No comments:

Post a Comment