Introduction
This is my first CodeProject article, so, do not expect too much of it! Also, English is not my first language, so, excuse me if I have made any mistakes.
Why do we need to log everything? If you need to trace some class activity, you'll need to log its steps. It is very simple to do it from the GUI, but what if I want to re-use my class in another project? Well, I will have to rewrite all logs. "No way, man!" you'll say... "Use a logger class!!"... Yes, but then your class is dependant too... I used the Snippet Compiler for my tests. This is a .NET must-have tool!
The Idea
I needed to simply log a string
message from the inside of my class, without having to depend on an external class. I wanted to do something like this:
class SomeClass
{
...
void DoSomething()
{
...
LogMessage("Something to log");
}
}
Where LogMessage(String)
simply puts that string
out of my class, and makes some other function handle it... hey, that sounds familiar to me... Callbacks! But wait, I'm talking in .NET, I have to say 'Delegate
s'.
The Solution
If you haven't used Delegate
s so far, you don't really know what a delegate
is. Because all well-known 'events', are in fact delegate
s. Delegate
s are very useful when you have to pass a function as a parameter, instead an object. C++ calls this a 'Callback'. Other languages let you pass the function as it is (but we are talking about .NET!). You may find this code in the zip file as LoggerImplementation.cs. I will declare a Delegate
for my logger:
public delegate void LogDelegate(String msg);
And now, I will add some code to my class to handle it:
private LogDelegate _log;
public LogDelegate Logger
{
get { return _log; }
set { _log = value; }
}
private void LogMessage(String msg)
{
if( _log != null )
{
_log(msg);
}
}
Cool! Now I can call LogMessage("Something!")
from any method inside my class without using external objects. We first declared a private LogDelegate
, and then the property to access it. Note that here it is not necessary to have the member and the property, because there is no logic in it. But it is always a good practice to do it. Then, there is a LogMessage(String)
method. This is a pre-call to the Delegate
, because we have to check if someone is consuming the delegate
before launching it. Oh yes, who's showing the messages? I see no print sentences... Well, the one who instantiates is the one who has to tell the object what to do when a LogMessage
is called. This is done like this:
stuffMaker.Log += delegate ( String msg )
{
Console.WriteLine(msg);
};
This will work only in C# 2.0. If you are using previous versions, you cannot declare anonymous methods. So, you'll have to add a new LogDelegate
to Log
, addressing another named method which will handle the delegate
(upgrade yourself, use 2.0). So, you also want to write the message to a file? Or the Windows EventLog
? No problem, delegate
s let you add many different handlers of a single method.
stuffMaker.Log += delegate ( String msg )
{
Console.WriteLine(msg);
};
stuffMaker.Log += delegate ( String msg )
{
Console.WriteLine("Other log: " + msg);
};
stuffMaker.Log += delegate ( String msg )
{
Console.WriteLine("Yes, another log: " + msg);
};
stuffMaker.Log += delegate ( String msg )
{
};
A Better 'Object Oriented' Solution
Well, you may say "But I have to paste this code in every class I want to log ...". Well, yes. You're right... so, why don't we do it the OO way?? Let's define an abstract
class (you may find this code in the zip file as LoggerImplementation Extender.cs).
public delegate void LogDelegate(String msg);
public abstract class Logger
{
private LogDelegate _log;
public LogDelegate Log
{
get { return _log; }
set { _log = value; }
}
protected void LogMessage(String msg)
{
if( _log != null )
{
_log(msg);
}
}
}
And now, the only thing you have to do to add logging capabilities to a class is this:
public class StuffMaker : Logger
Conclusion
This is my first article and I hope you liked it. I've found this very useful in my daily work, and also for testing purposes. You may use this same approach for monitoring class' internal processes too. You may also note that this Pattern doesn't even exist... but it was a nice solution to a common problem, and I gave it a name.
History
- 12th June, 2006: Initial post
Alejandro "nSeeker" Rizzo is an advanced student of informatics engineering in Argentina. He's now working for a known security software company from Spain.
He coded under VB since he was fourteen, but a few years ago, he definitely moved to C#. He works with ASP.Net, Delphi, and C++ too.
Visit http:\\thenseeker.blogspot.com for further info about Alejandro.