Reputation: 24523
When logging, you always get entangled in string literals.
I solved that nicely for properties, fields and variables by passing an Expression<Func<T>> expression
(as explained here), so you can do things like this:
public void Demo(string someArgument)
{
LogFrameWork.LogLine("Demo"); // goal is to get rid of these string literals
LogFramework.Log(() => someArgument);
}
I want to do something similar for the method Demo
itself:
public void Demo(string someArgument)
{
LogFramework.Log(this.Demo);
}
I tried things like this:
public static void Log(Delegate method)
{
string methodName = method.Method.Name;
LogLine(methodName);
}
and this:
public static void Log(Action method)
{
string methodName = method.Method.Name;
LogLine(methodName);
}
But I get compiler errors like these:
Argument 1: cannot convert from 'method group' to 'System.Delegate'
Argument 1: cannot convert from 'method group' to 'System.Action'
I could introduce a bunch of overloads using Func<...> and Action<...>, but that sounds overly complex.
Is there a way to cover this for any method with any number of parameters and an optional result?
--jeroen
PS: I think this question might have some relevance here, but no answers that got me a 'aha' feeling :-)
Upvotes: 6
Views: 6084
Reputation: 25563
This is much harder than it looks. I think you might be best with the generic Func and Action overloads, but there is a way to do this with expression trees. Here's an example in LINQPad:
public static void Log(Expression<Action> expr)
{
Console.WriteLine(((MethodCallExpression)expr.Body).Method.Name);
}
void Main()
{
Log(() => DoIt());
Log(() => DoIt2(null));
Log(() => DoIt3());
}
public void DoIt()
{
Console.WriteLine ("Do It!");
}
public void DoIt2(string s)
{
Console.WriteLine ("Do It 2!" + s);
}
public int DoIt3()
{
Console.WriteLine ("Do It 3!");
return 3;
}
This outputs:
DoIt DoIt2 DoIt3
Note that I had to use lambdas and specify dummy arguments when calling the Log method.
This is based on Fyodor Soikin's excellent answer.
Upvotes: 5
Reputation: 38385
Instead of trying to pass the method in as a parameter to your logger, look at it from the perspective of having the logger identify the calling method.
Here's an (pseudo) example:
Logger Class
public void Debug( string message )
{
message = string.Format( "{0}: {1}", GetCallingMethodInfo(), message );
// logging stuff
}
/// <summary>
/// Gets the application name and method that called the logger.
/// </summary>
/// <returns></returns>
private static string GetCallingMethodInfo()
{
// we should be looking at the stack 2 frames in the past:
// 1. for the calling method in this class
// 2. for the calling method that called the method in this class
MethodBase method = new StackFrame( 2 ).GetMethod();
string name = method.Name;
string type = method.DeclaringType.Name;
return string.Format( "{0}.{1}", type, name );
}
Anywhere that uses the logger:
// resides in class Foo
public void SomeMethod()
{
logger.Debug("Start");
}
The output from the logger will then be: Foo.SomeMethod: Start
Upvotes: 3
Reputation: 2562
Try this:
/// <summary>
/// Trace data event handler delegate.
/// </summary>
/// <returns>The data to write to the trace listeners</returns>
public delegate object TraceDataEventHandler();
public static class Tracing
{
/// Trace a verbose message using an undefined event identifier and message.
/// </summary>
/// <param name="message">The delegate to call for the trace message if this event should be traced.</param>
[Conditional("TRACE")]
public static void TraceVerbose(TraceMessageEventHandler message)
{
... your logic here
}
}
Then you can do...
Tracing.TraceVerbose(() => String.Format(...));
I hope I have understood your question correctly... does this do what you want?
Upvotes: 0
Reputation: 14256
You can also achieve this without using ExpressionTrees through System.Diagnostics.StackTrace
.
StackTrace trace = new StackTrace();
And then:
trace.GetFrame(0).GetMethod().Name
To get the MethodInfo and then name of the current method, or:
trace.GetFrame(1).GetMethod().Name
To get the calling method.
Upvotes: 5
Reputation: 25694
You can define a delegate, then accept that delegate as a parameter.
public delegate void DemoDelegate(string arg);
public void MyMethod(DemoDelegate delegate)
{
// Call the delegate
delegate("some string");
}
You can call MyMethod like this:
MyMethod(delegate(string arg)
{
// do something
});
or
void MethodThatTakesAString(string value)
{
// do something
}
MyMethod(MethodThatTakesAString);
See this link for more information:
http://msdn.microsoft.com/en-us/library/aa288459(v=vs.71).aspx
Upvotes: 0