C# 3.0 has gotten some great new features that are making the language so much more expressive. Two of my favorite are extension methods and lambda expressions. So now, people have started creating all kinds of crazy extension methods to the built-in type String and every other CLR class.
Last week I was working on this Java project and doing some logging as part of my work, and was getting rather annoyed by the idea of having to check the logging level just before every darn log statement. And then I remembered that the same was true for the .NET equivalent of log4j, but in C# you could do so much better. So I decided to sit down and get some C# lovin'. First, just to be sure that you dear reader understand what the issue really is, here is how typical logging is happening using log4net:
1 private static void LegacyStyleOfLogging()
2 {
3 ILog logger = LogManager.GetLogger(typeof (Program));
4 if (logger.IsDebugEnabled)
5 logger.Debug(String.Format("The result of the method is {0}", RetrievesSlowData()));
6 }
Now, that thing on line 4, that is the nasty check I was talking about earlier. It is there purely as an optimization in case the debugging level is set to something higher then "DEBUG" in which case, since the log statement will not be rendered anyway, the logging string will not be evaluated (you do not know how slow that RetrieveSlowData method is, looks pretty slow to me ;) ). So all in all, I understand the reasoning, it's just that I do not want to do it ;).
Enter extension methods! Without further ado here is my extension method for log4net:
1 public static class LogExtension
2 {
3 public delegate string RenderDelegate();
4 public static void Debug(this ILog log, RenderDelegate renderer)
5 {
6 if (log.IsDebugEnabled) log.Debug(renderer());
7 }
8 }
So not a lot of code for the extension, basically just an overload of the Debug method which accepts a delegate as its only parameter. The new overload checks for the DebugLevel and only then invokes the delegate which is supposed to evaluate the log message.So let's see how it is being used:
1 private static void NewShinyStyleOfLogging()
2 {
3 ILog logger = LogManager.GetLogger(typeof (Program));
4 logger.Debug(() => String.Format("The result of the method is {0}", RetrievesSlowData()));
5 }
So now looking at line 4 you can see there is no checking whether the debug level is "in scope". What you can also see in line 4 is the other new element introduced in C# 3 that I mentioned earlier - lambda expressions. They can be automatically converted to delegates and are extra terse and I think that's what makes this approach feasible. This one has no parameters, thus the strange looking opening and closing braces before the "=>" signature.
With a little bit more effort one could easily add an exception parameter and implement all this for the other levels of logging. And all this without modifying the existing (dare I say ancient) log4net.
You might be wondering if I just put too much effort into saving me a line of code. For those of you who do, I just have to point out the following benefits:
- this line shows up way too often
- there is less noise in my code
- now I can free my mind from having to remember to put in this check
- it is always there, guaranteed
So, I wonder how long we will have to wait for JSR-666 that will introduce C# for the JVM ;)
26.09.2008
|
Flokkur: Forritun
Höfundur: Petar Shomov