5

我正在使用 log4net,我们的代码中有很多这样的:

public class Foo {
    private static readonly ILog log = LogManager.GetLogger(typeof(Foo));
    ....
}

一个缺点是这意味着我们将这个 10 字的部分粘贴到所有地方,并且时不时有人忘记更改类名。log4net FAQ也提到了这种替代可能性,它更加冗长:

public class Foo {
    private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    ...
}

是否可以编写一个装饰器来定义它?我真的很想简单地说:

[LogMe]  // or perhaps: [LogMe("log")]
public class Foo {
    ...
}

我用其他语言做过类似的事情,但从来没有像 C# 这样的静态编译语言。我可以从装饰器中定义类成员吗?

编辑:呵呵。我是一名 Lisp 程序员。我很欣赏切换语言的建议,但实际上,如果我要切换语言以获得更好的元编程能力,我会一直使用 Lisp,而不是半途而废。不幸的是,在这个项目中不能选择使用不同的语言。

4

8 回答 8

5

这正是 AOP - Aspect Oriented Programming 的任务。看看PostSharp,它是一个 .NET AOP 框架,它可以让你做你想做的事。

这通过在编译后修改(或编织)IL 代码,并将日志记录方面添加到修饰方法来工作。

编辑:看来 PostSharp 现在是一种商业产品。如果您正在寻找开源(免费)解决方案,我建议LinFu AOP

于 2010-05-17T21:30:47.943 回答
3

我们使用几乎相同的东西:

private static readonly ILog Logger = LogManager.GetLogger();

该方法实现如下:

[MethodImpl(MethodImplOptions.NoInlining)]
public static ILog GetLogger()
{
    Type t;
    try { t = new StackFrame(1, false).GetMethod().DeclaringType; }
    catch { t = typeof(UnknownObject); }
    return GetLogger(t);
}
private static class UnknownObject { }

事实证明,我愿意经历更多的痛苦。我更喜欢使用静态方法进行日志记录的静态对象。如果您没有获取文件信息,则获取调用方法并不是那么昂贵。与仅调用 Log4Net 获取调用类型的费用相比,这没什么(取决于所使用的记录器)。

于 2010-05-17T21:50:12.283 回答
1

.NET 中的属性不是影响它们所附加的类/成员的装饰器/活动组件。属性是可以通过反射检索的元数据。C# 中没有类似装饰器的工具,尽管有 AOP 解决方案可以根据您的需要扩展 .NET。最简单的解决方案可能只是将该行复制到每个类。

于 2010-05-17T21:27:46.497 回答
1

我们包装了 log4net,以便我们可以轻松地将其切换出去。这是我们将来很可能改变主意的事情。

虽然我们不这样做,但这样做可能会对性能造成影响......而且我什至不敢建议这样做,因为我不确定这是一个好主意......你可以...如果你觉得够邪恶的话....包装 log4net 并在你的包装器中做这样的事情。

var callingType = new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().DeclaringType

如果您对如何记录日志很了解,那么只有在需要日志消息时才会产生该成本。

于 2010-05-17T21:49:32.033 回答
0

伙计,这在 python 中将是一件轻而易举的事。我会研究C# 属性。

于 2010-05-17T21:27:14.810 回答
0

如果这是 ASP.NET 或 Windows 窗体,您可以只创建一个基本页面或所有窗体/页面派生自的基本窗体。您可以在该级别声明此成员,并可能实现您想要的。

于 2010-05-17T21:30:51.517 回答
0

您可能可以在PostSharp或其他面向方面的编程工具的帮助下实现一个解决方案。

在 Boo 中使用宏将非常简单,但这对您的情况没有多大帮助。

于 2010-05-17T21:31:21.920 回答
0

也许一个简单的方法是在接口上编写一个扩展方法,然后你的类只需要“实现”(但不是真的因为扩展实现了)接口

public class Foo : IGetLog<Foo>
{

}

扩展类似于....

  public interface IGetLog<T> { }

  public static class IGetLogExtension
  {
    public static ILog GetLogger<T>(this IGetLog<T> getLog)
    {
      return LogManager.GetLogger(typeof(T));
    }
  }
于 2010-05-17T21:33:19.047 回答