如何找到当前函数调用者的对象实例的类型?

时间:2008-12-03 21:18:50

标签: c# .net reflection logging

目前我有一个函数CreateLog(),用于在构造实例的类之后创建一个带有名称的log4net日志。 通常用于:

class MessageReceiver
{
     protected ILog Log = Util.CreateLog();
     ...
}

如果我们删除了大量的错误处理,那么实现可以归结为: [编辑:请在本文中进一步阅读更长版本的CreateLog。]

public ILog CreateLog()
{
        System.Diagnostics.StackFrame stackFrame = new System.Diagnostics.StackFrame(1);
        System.Reflection.MethodBase method = stackFrame.GetMethod();
        return CreateLogWithName(method.DeclaringType.FullName);
}

问题是如果我们将MessageReceiver放入子类中,日志仍然会从MessageReceiver中取名,因为这是调用CreateLog的方法(构造函数)的声明类。

class IMReceiver : MessageReceiver
{ ... }

class EmailReceiver : MessageReceiver
{ ... }

这两个类的实例都会获取名为“MessageReceiver”的日志,而我希望它们的名称为“IMReceiver”和“EmailReceiver”。

我知道通过在调用CreateLog时传递对创建对象的引用可以很容易地完成(并且完成),因为对象上的GetType()方法实现了我想要的。

有一些小的理由不喜欢不添加参数,个人感到不安,因为没有找到没有额外参数的解决方案。

是否有人可以告诉我如何实现零参数CreateLog()从子类而不是声明类获取名称?

编辑:

CreateLog函数比上面提到的更多。每个实例有一个日志的原因是能够在日志文件中的不同实例之间有所不同。这由CreateLog / CreateLogWithName对强制执行。

扩展CreateLog()的功能以激发其存在。

public ILog CreateLog()
{
        System.Diagnostics.StackFrame stackFrame = new System.Diagnostics.StackFrame(1);
        System.Reflection.MethodBase method = stackFrame.GetMethod();
        Type type = method.DeclaringType;

        if (method.IsStatic)
        {
            return CreateLogWithName(type.FullName);
        }
        else
        {
            return CreateLogWithName(type.FullName + "-" + GetAndInstanceCountFor(type));
        }
}

我更喜欢编写ILog Log = Util.CreateLog();而不是每当我写一个新类时,从另一个文件中复制一些长的神秘线。我知道Util.CreateLog中使用的反射不能保证工作 - System.Reflection.MethodBase.GetCurrentMethod()是否可以保证工作?

5 个答案:

答案 0 :(得分:2)

通常情况下,MethodBase.ReflectedType会有您的信息。但是,根据MSDN StackFrame.GetMethod

当前正在执行的方法可以从基类继承,尽管它是在派生类中调用的。在这种情况下,GetMethod返回的MethodBase对象的ReflectedType属性标识基类,而不是派生类。

这意味着你可能运气不好。

答案 1 :(得分:2)

我想你可能会问错误的问题。首先,记录器应该对每个类都是静态的 - 每个类都应声明自己的记录器(以确保正确报告类名,并允许从配置文件中选择性地报告由项目或命名空间过滤的日志消息。

其次看来你创建这个方法只是为了识别调用类的名字?如果是这样,我们使用粘贴到每个类中的样板代码:

private static ILog log = 
log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

因为它是私有的,所以你要确保你的继承类必须声明自己的记录器,而不是使用你的记录器。因为它是静态的,所以确保查找记录器的开销只产生一次。

如果您对Util.CreateLog()方法进行编码有不同的原因,我表示歉意。

答案 2 :(得分:1)

  

是否有人可以告诉我如何实现零参数CreateLog()从子类而不是声明类获取名称?

我认为你不能通过查看堆栈框架来做到这一点。

虽然您的课程为IMReceiver,但CreateLog方法的调用位于MessageReceiver课程中。堆栈框架必须告诉您调用方法的位置,或者它不会有任何用处,因此它始终会说MessageReceiver

如果您在CreateLog和其他类中明确调用了IMReceiver,那么它就可以工作,因为堆栈帧显示了派生类中调用的方法(因为它实际上是)。

这是我能想到的最好的事情:

class BaseClass{
  public Log log = Utils.CreateLog();
}
class DerivedClass : BaseClass {
  public DerivedClass() {
    log = Utils.CreateLog();
  }
}

如果我们跟踪日志的创建,我们得到这个:

new BaseClass();
# Log created for BaseClass

new DerivedClass();
# Log created for BaseClass
# Log created for DerivedClass

为派生类创建的第二个'日志'会覆盖实例变量,因此您的代码将正常运行,您只需创建一个立即丢弃的BaseClass日志。这对我来说似乎很糟糕,我只是在构造函数中指定type参数或使用泛型。

恕我直言,指明这种类型比在堆栈框架中徘徊更干净

如果你可以在不查看堆栈框架的情况下获得它,那么你的选项会大大扩展

答案 3 :(得分:0)

尝试使用StackTrace.GetFrames方法。它返回调用堆栈中所有StackFrame对象的数组。你的来电者应该在索引一。

class Program
{
    static void Main(string[] args)
    {
        Logger logger = new Logger();
        Caller caller = new Caller();
        caller.FirstMethod(logger);
        caller.SecondMethod(logger);
    }
}

public class Caller
{
    public void FirstMethod(Logger logger)
    {
        Console.Out.WriteLine("first");
        logger.Log();
    }

    public void SecondMethod(Logger logger)
    {
        Console.Out.WriteLine("second");
        logger.Log();
    }
}

public class Logger
{
    public void Log()
    {
        StackTrace trace = new StackTrace();
        var frames = trace.GetFrames();
        Console.Out.WriteLine(frames[1].GetMethod().Name);
    }
}

此输出

第一 FirstMethod 第二 SecondMethod

答案 4 :(得分:0)

向上移动堆栈检查基类 - 派生类关系。

        var type = new StackFrame(1).GetMethod().DeclaringType;
        foreach (var frame in new StackTrace(2).GetFrames())
            if (type != frame.GetMethod().DeclaringType.BaseType)
                break;
            else
                type = frame.GetMethod().DeclaringType;
        return CreateLogWithName(type.FullName);

您可能需要检查所检查的方法是构造函数。但是,子类在除了构造函数之外的方法中实例化超类的情况可能仍然需要当前的行为。