Class decorator to declare static member (e.g., for log4net)?
I'm using log4net, and we have a lot of this in our code:
public class Foo {
private static readonly ILog log = LogManager.GetLogger(typeof(Foo));
....
}
One downside is that it means we're pasting this 10-word section all over, and every now and then somebody forgets to change the class name. The log4net FAQ also mentions this alternative possibility, which is even more verbose:
public class Foo {
private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
...
}
Is it possible to write a decorator to define this? I'd really like to say simply:
[LogMe] // or perhaps: [LogMe("log")]
public class Foo {
...
}
I've done similar things in other languages, but never a statically-compiled language like C#. Can I define class members from a decorator?
Edit: Heh. I'm a Lisp programmer. I开发者_运维问答 appreciate the suggestions to switch languages, but really, if I was going to switch languages for better metaprogramming capabilities, I'd go all the way to Lisp instead of being half-assed about it. Unfortunately using a different language isn't an option on this project.
This is exactly a task for AOP - Aspect Oriented Programming. Have a look at PostSharp, it's a .NET AOP framework, it will allow you to do exactly what you want.
This works by modifying (or weaving) IL code in post-compilation, and adding the logging aspect to the decorated method.
EDIT: It appears that PostSharp now is a commercial product. If you're looking for an open-source (free) solution, I suggest LinFu AOP.
We use something almost identical:
private static readonly ILog Logger = LogManager.GetLogger();
This method is implemented as follows:
[MethodImpl(MethodImplOptions.NoInlining)]
public static ILog GetLogger()
{
Type t;
try { t = new StackFrame(1, false).GetMethod().DeclaringType; }
catch { t = typeof(UnknownObject); }
return GetLogger(t);
}
private static class UnknownObject { }
It still proves to be more of a pain that I'm willing to go through. I much prefer a static object with static methods for logging. Obtaining the calling method is not that expensive provided your not getting file info. Compared to the expense of just calling Log4Net obtaining the calling type is nothing (depending some on the loggers used).
Attributes in .NET are not decorators / active components that influence the class/member they're attached to. Attributes are meta-data that can be retrived with reflection. There is no decorator-like facility in C#, although there are AOP solutions that extend .NET with what you want. The simplest solution is probably to just copy that line to each class though.
We wrapped log4net so we could switch it out with ease. It is something that we could very possibly change our mind on in the future.
While we are not doing this, and you will probably take a performance hit to do so....and I'm really hesitant to even suggest it because I'm not sure it is a good idea.....you could...if you felt devilish enough....wrap log4net and do something like this in your wrapper.
var callingType = new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().DeclaringType
If you're smart about how you're logging you could only incur that cost when you need the log message.
Man this would be a breeze in python. I would look into C# Attributes.
If this is ASP.NET or Windows Forms, you can just create a base Page or base Form that all your Forms/Pages derive from. You can declare this member at that level and probably achieve what you desire.
You could probably implement a solution for this with the help of PostSharp, or another Aspect Oriented Programming tool.
It would be pretty trivial using a macro in Boo, but that wouldn't help your situation much.
Perhaps an easy way would be to write an extension method on an Interface, then your class just need to "implement" (but not really because the extension does the impl) the interface
public class Foo : IGetLog<Foo>
{
}
The extension something like....
public interface IGetLog<T> { }
public static class IGetLogExtension
{
public static ILog GetLogger<T>(this IGetLog<T> getLog)
{
return LogManager.GetLogger(typeof(T));
}
}
精彩评论