Декоратор класса для объявления статического участника (например, для log4net)?

Я использую 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#. Я могу определить участников класса от декоратора?

Править: Heh. Я - программист Lisp. Я ценю предложения для переключения языков, но действительно, если бы я собирался переключить языки для лучших возможностей метапрограммирования, я перешел бы полностью к Lisp вместо того, чтобы полуваляться дурака он. К сожалению, использование другого языка не является опцией на этом проекте.

5
задан Ken 17 May 2010 в 21:53
поделиться

8 ответов

Мы используем что-то почти идентичное:

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 получение типа вызова - ничто (в зависимости от используемых регистраторов).

3
ответ дан 13 December 2019 в 22:03
поделиться

Атрибуты в .NET не являются декораторами / активными компонентами, которые влияют на класс / член, к которому они прикреплены. Атрибуты - это метаданные , которые могут быть получены с помощью отражения. В C # нет средств декоратора, хотя есть решения АОП, расширяющие .NET тем, что вам нужно. Однако самое простое решение - просто скопировать эту строку в каждый класс.

1
ответ дан 13 December 2019 в 22:03
поделиться

Это именно задача для AOP - Aspect Oriented Programming. Посмотрите на PostSharp, это фреймворк .NET AOP, он позволит вам сделать именно то, что вы хотите.

Это работает путем модификации (или плетения) IL-кода в пост-компиляции, и добавления аспекта протоколирования в декорированный метод.

EDIT: Похоже, что PostSharp теперь является коммерческим продуктом. Если вы ищете решение с открытым исходным кодом (бесплатное), я предлагаю LinFu AOP.

5
ответ дан 13 December 2019 в 22:03
поделиться

Черт возьми, это было бы проще простого в питоне. Я бы посмотрел на Атрибуты C #.

0
ответ дан 13 December 2019 в 22:03
поделиться

Если это ASP.NET или Windows Forms, вы можете просто создать базовую страницу или базовую форму, от которой будут происходить все ваши формы/страницы. Вы можете объявить этот член на этом уровне и, вероятно, добиться желаемого.

0
ответ дан 13 December 2019 в 22:03
поделиться

Вы, вероятно, могли бы решить эту проблему с помощью PostSharp или другого инструмента аспектно-ориентированного программирования.

Было бы довольно тривиально использовать макрос в Boo, но это не сильно поможет в вашей ситуации.

0
ответ дан 13 December 2019 в 22:03
поделиться

Возможно, простым способом было бы написать метод расширения для интерфейса, тогда ваш класс просто должен "реализовать" (но не совсем, потому что расширение делает impl) интерфейс

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));
    }
  }
0
ответ дан 13 December 2019 в 22:03
поделиться

Мы обернули log4net, чтобы можно было легко его отключить. Это то, о чем мы вполне могли бы изменить свое мнение в будущем.

Пока мы этого не делаем, и вы, вероятно, получите удар по производительности, чтобы сделать это ... и я действительно не решаюсь даже предлагать это, потому что я не уверен, что это хорошая идея ... .вы могли бы ... если бы вы чувствовали себя достаточно дьявольски ... оберните log4net и сделайте что-нибудь подобное в своей оболочке.

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

Если вы хорошо разбираетесь в том, как вести журнал, вы можете понести эти расходы только тогда, когда вам понадобится сообщение журнала.

1
ответ дан 13 December 2019 в 22:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: