2011-03-26 57 views
6

当记录时,你总是纠缠在字符串文字中。C#通过任何方法作为参数

我解决了这个很好的属性,字段和变量传递一个Expression<Func<T>> expression(如解释here),所以你可以做这样的事情:

public void Demo(string someArgument) 
{ 
    LogFrameWork.LogLine("Demo"); // goal is to get rid of these string literals 
    LogFramework.Log(() => someArgument); 
} 

我想要做的方法Demo本身类似的东西:

public void Demo(string someArgument) 
{ 
    LogFramework.Log(this.Demo); 
} 

我试过这样的事情:

public static void Log(Delegate method) 
{ 
    string methodName = method.Method.Name; 
    LogLine(methodName); 
} 

这:

public static void Log(Action method) 
{ 
    string methodName = method.Method.Name; 
    LogLine(methodName); 
} 

,但我得到这样的编译器错误:

​​

我介绍了一堆使用Func<...>Action<...>过载,但听起来过于复杂。

有没有办法用任何数量的参数和可选的结果来覆盖这个方法?

--jeroen

PS:我觉得this question可能有一定的相关性在这里,但没有答案,这让我一个“啊哈”的感觉:-)

+1

但是,我理解你的问题,难道你不认为你会更容易和更可扩展的一些AOP框架?您可以创建自定义属性来标记在调用时应该记录的方法。 – empi 2011-03-26 16:21:43

+1

我过去使用过AOP,它导致编译/链接时间猛增。由于.NET已经有了更高的编译/链接时间,我宁愿避免这种情况。 – 2011-03-26 16:29:38

+1

针对我在表达式树中看到的这个常见问题的每个解决方案都使用了多重重载方法,这也有点尴尬,因为您需要包含参数。如果我要编写手动跟踪日志记录,我通常只是将方法名称作为字符串常量传递,并依靠Resharper来提醒我保持同步。 – 2011-03-26 16:33:52

回答

3

不是试图将方法作为参数传递给记录器,而是从使记录器识别调用方法的角度来看待它。

下面是一个(伪)例如:

Logger类

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的:

// resides in class Foo 
public void SomeMethod() 
{ 
    logger.Debug("Start"); 
} 

从记录程序的输出将是:Foo.SomeMethod: Start

+0

+1;感谢详细解释[解决方案](http://stackoverflow.com/questions/5443521/c-pass-any-method作为一个参数/ 5443606#5443606)[Josh G](http://stackoverflow.com/users/64329/josh-g)提出的。我确实知道他的意思,但有一个更详细的例子在手将会帮助未来的用户很多 – 2011-03-26 16:38:57

+1

这是我在几年前使用'MS企业日志库'编写的日志库。这个解决方案几乎解决了你想要达到的目标,我想,你需要记录器知道什么方法称为记录器。关于这一点的好处是使用'DeclaringType',你最终会得到一个完整的名称空间给方法。而且不必担心将方法名称添加为日志消息的一部分。 – 2011-03-26 17:04:21

+1

我接受了你的答案,因为它在呼叫站点创建了最干净的代码。没有令人费解的表情。尼斯。 – 2011-04-13 22:36:22

0

您可以定义一个委托,然后接受该委托作为参数。

public delegate void DemoDelegate(string arg); 

public void MyMethod(DemoDelegate delegate) 
{ 
    // Call the delegate 
    delegate("some string"); 
} 

可以调用的MyMethod这样的:

MyMethod(delegate(string arg) 
{ 
    // do something 
}); 

void MethodThatTakesAString(string value) 
{ 
    // do something 
} 

MyMethod(MethodThatTakesAString); 

请参阅此链接了解更多信息:

http://msdn.microsoft.com/en-us/library/aa288459(v=vs.71).aspx

+1

我了解代表,但没有看到这将如何帮助我的任何方法与任何数量的参数。请详细说明,因为我可能会查看一些非常明显的内容:-) – 2011-03-26 16:31:46

5

你也可以做到这一点而不通过System.Diagnostics.StackTrace使用ExpressionTree。

StackTrace trace = new StackTrace(); 

然后:

trace.GetFrame(0).GetMethod().Name 

要获得的MethodInfo,然后命名当前的方法,或:

trace.GetFrame(1).GetMethod().Name 

获取调用方法。

+0

+1;有趣!将调查这一点。感谢您在箱子外面思考。这很可能也会帮助我以自动方式获取参数。 – 2011-03-26 16:36:22

+1

这尤其是非高性能的,并且存在将帧优化掉的风险。 – 2011-03-26 16:39:09

+0

@Kirk:表达式也被称为非常昂贵,你可以详细说明'正在优化'部分;你的意思是内联吗? – 2011-03-26 16:40:59

0

试试这个:

/// <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 
    } 
} 

然后你就可以做...

Tracing.TraceVerbose(() => String.Format(...)); 

我希望我已经正确理解你的问题......这是否做你想要的?

5

这比看起来要难得多。我认为你最好用通用的Func和Action重载,但是有一种方法可以用表达式树来完成。下面是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; 
} 

此输出:

DoIt 
DoIt2 
DoIt3

注意,我不得不使用lambda表达式并调用日志方法时指定伪参数。

这是基于Fyodor Soikin's excellent answer

+0

感谢您的回答;我担心它会变成这样的东西,但确认它是很好的。 – 2011-03-27 19:48:08