奇怪而又非常具体的问题。现在,我们有一个使用CallerMemberNameAttribute
的日志记录界面,如下所示:
interface ILogger
{
void Info(string message, [CallerMemberName] string memberName = "");
}
这都是伟大而完美的,并且在我们的实施中效果很好。但是,提出了一个要求,我们需要编写一些可以在我们的流程以及其他地方调用的函数,并且这些函数需要使用一个定义为ITracer
的类,如下所示:
interface ITracer
{
void TraceInformation(string message);
}
因此,当在我们的环境中运行时,这些功能应记录到当前的日志记录基础结构中,但是在其他环境中运行时,它具有自己的ITracer
集,可以完成自己的工作。因此,我编写了一个垫片,该垫片在环境中被调用时只会传递消息:
class Logger : ILogger
{
public void Info(string message, [CallerMemberName] string memberName = "") => // log some stuff
public ITracer GetTraceWriter() => return new TraceWriter(this);
}
class TraceWriter : ITracer
{
public TraceWriter(ILogger logger) => this.logger = logger;
public void TraceInformation(string message) => this.logger.Info($"{message}");
}
这可以正常工作,但是memberName
是输出的日志消息的一部分,在此实现中,当TraceWriter开始记录日志时,它始终具有等于{{1}的memberName
}。有什么方法可以通过函数调用将此参数属性传递给吗?这里的主要问题是我无法更改TraceInformation
界面。
想到但无法解决的解决方案:
ITracer
中的TraceInformation
调用以返回对ITracer
的函数调用,该函数调用可以直接从方法中调用(无法执行此操作,因为我无法更改ITracer接口)答案 0 :(得分:0)
您可能会做的,也许是不理想的事情,是使用StackFrame
类在调用函数中查找堆栈。
您可以使用它来遍历堆栈,以查找(或排除)特定类型,或者实现特定接口的类型-或更简单地只是“向上”堆栈特定数量的帧。
如果将此内容包含在void Info(..)
的实现中,则可以按以下方式访问方法名称
# get the name of the current method (i.e. Info)
new StackFrame(0, false).GetMethod().Name
# get the name of the calling method (i.e. TraceInformation)
new StackFrame(1, false).GetMethod().Name
# get the name of the parent calling method - what you are looking for
new StackFrame(2, false).GetMethod().Name
当然,您需要从ITracer
对象调用该方法时以及直接调用该方法时进行协调。您还可以获取调用对象,检查它们实现了哪些接口,并记录适当的方法名称。
这全部使用反射,因此您需要考虑性能影响,但是我希望CallerMemberName
也使用反射/可能会产生类似的影响。