Showing posts with label Lambda. Show all posts
Showing posts with label Lambda. Show all posts

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

Code Generation - yield and Lambdas

So, while I don't profess to be an expert on Code Generation, I do hope that some of the tips that I put up here may save someone else the time it took me to figure out how to accomplish what appear to be simple steps.

At the time of writing this post, and having spent several hours scouring the internet, I couldn't for the life of me find a way to get a yield return statement.

The reason for this will be explained in another post (TODO:addlink) but the basic premise was to have the following line in the generated class:

   01 yield return () => SomeStringProperty;

All the properties returned strings, so this was fine.

Trying to generate this was proving a problem, until I came across this...hmm...workaround (don't want to use the word hack here)
While it ties the generated code to be specifically C#, this, again was not a problem for the code base that we were working against.

Here is the code that produced the line:

  01 generatedProperty.GetStatements.Add(new CodeSnippetExpression(string.Format("yield return () => {0}", existingProperty.Name)));

The genereatedProperty is of type CodeMemberProperty as is the existingProperty.

I wouldn't recommend this for all solutions, but it worked in this case where we were not trying to add to a generic code generator.

Submit this story to DotNetKicks Shout it

Wednesday, 3 June 2009

Performance - Lambdas and closures

Whilst performance tuning a recent application, I noticed that the logging was taking an awfully long time, now, whilst this may not come as a surprise in some cases, it came as a surprise being as logging was turned off. Let me explain...

In the application, all BOs use reflection to create a string version of themselves. We were using log4net to do our logging and we were even checking that specific levels of logging were turned on before performing the logging using the simple checks:

    1 private static void WriteToLog(LogLevel logLevel, ILog log, string msg, Exception e)
    2 {
    3     switch (logLevel)
    4     {
    5         case LogLevel.All:
    6         case LogLevel.Trace:
    7             if (log.IsTraceEnabled)
    8             {
    9                 if (e == null) log.Trace(msg);
   10                 else log.Trace(msg, e);
   11             }
   12             break;
   13         case LogLevel.Debug:
   14             if (log.IsDebugEnabled)
   15             {
   16                 if (e == null) log.Debug(msg);
   17                 else log.Debug(msg, e);
   18             }
   19             break;
   20         case LogLevel.Error:
   21             if (log.IsErrorEnabled)
   22             {
   23                 if (e == null) log.Error(msg);
   24                 else log.Error(msg, e);
   25             }
   26             break;
   27         case LogLevel.Fatal:
   28             if (log.IsFatalEnabled)
   29             {
   30                 if (e == null) log.Fatal(msg);
   31                 else log.Fatal(msg, e);
   32             }
   33             break;
   34         case LogLevel.Info:
   35             if (log.IsInfoEnabled)
   36             {
   37                 if (e == null) log.Info(msg);
   38                 else log.Info(msg, e);
   39             }
   40             break;
   41         case LogLevel.Warn:
   42             if (log.IsWarnEnabled)
   43             {
   44                 if (e == null) log.Warn(msg);
   45                 else log.Warn(msg, e);
   46             }
   47             break;
   48         case LogLevel.Off:
   49         default:
   50             break;
   51     }
   52 }

So we were taking all the precautions except the most obvious one: we were creating the msg to log even if we didn't log the msg.

The problem that I was now facing was that we had a generic method for writting the log but the methods that wanted to be called on demand didn't. This is where lambdas and closures came in nice and handy. By creating a lambda and passing in the arguments for creating the message in, this defered the execution of the creation of the message until it was time to write the message:

    1 private static void WriteToLog(LogLevel logLevel, ILog log, Func<string> getMessage, Exception e)
    2 {
    3     switch (logLevel)
    4     {
    5         case LogLevel.All:
    6         case LogLevel.Trace:
    7             if (log.IsTraceEnabled)
    8             {
    9                 if (e == null) log.Trace(getMessage());
   10                 else log.Trace(getMessage(), e);
   11             }
   12             break;
   13         case LogLevel.Debug:
   14             if (log.IsDebugEnabled)
   15             {
   16                 if (e == null) log.Debug(getMessage());
   17                 else log.Debug(getMessage(), e);
   18             }
   19             break;
   20         case LogLevel.Error:
   21             if (log.IsErrorEnabled)
   22             {
   23                 if (e == null) log.Error(getMessage());
   24                 else log.Error(getMessage(), e);
   25             }
   26             break;
   27         case LogLevel.Fatal:
   28             if (log.IsFatalEnabled)
   29             {
   30                 if (e == null) log.Fatal(getMessage());
   31                 else log.Fatal(getMessage(), e);
   32             }
   33             break;
   34         case LogLevel.Info:
   35             if (log.IsInfoEnabled)
   36             {
   37                 if (e == null) log.Info(getMessage());
   38                 else log.Info(getMessage(), e);
   39             }
   40             break;
   41         case LogLevel.Warn:
   42             if (log.IsWarnEnabled)
   43             {
   44                 if (e == null) log.Warn(getMessage());
   45                 else log.Warn(getMessage(), e);
   46             }
   47             break;
   48         case LogLevel.Off:
   49         default:
   50             break;
   51     }
   52 }


And then the calls to the logging could be called as such:

    1 try
    2 {
    3     WriteToLog(LogLevel, log, () => GetEntryMessage(invocation, uniqueIdentifier), null);
    4     returnValue = invocation.Proceed();
    5 }
    6 catch (ThreadAbortException)
    7 {

   10     exitThroughException = false;
   11     throw;
   12 }
   13 catch (Exception e)
   14 {
   15     TimeSpan executionTimeSpan = DateTime.Now - startTime;
   16     WriteToLog(LogLevel, log, () => GetExceptionMessage(invocation, e, executionTimeSpan, uniqueIdentifier), e);
   17     exitThroughException = true;
   18     throw;
   19 }
   20 finally
   21 {
   22     if (!exitThroughException)
   23     {
   24         TimeSpan executionTimeSpan = DateTime.Now - startTime;
   25         WriteToLog(LogLevel, log, () => GetExitMessage(invocation, returnValue, executionTimeSpan, uniqueIdentifier), null);
   26     }
   27     ClearLogContext();
   28 }
   29 
   30 return returnValue;

No need to worry about creating overrides of the logging function to take in the different parameters or creating a master version which handled null arguments.

Submit this story to DotNetKicks Shout it