Декоратор класса для объявления статического члена (например, для log4net)? - PullRequest
5 голосов
/ 18 мая 2010

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

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

Ответы [ 8 ]

5 голосов
/ 18 мая 2010

Это как раз задача для AOP - Аспектно-ориентированного программирования. Взгляните на PostSharp , это платформа .NET AOP, она позволит вам делать именно то, что вы хотите.

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

РЕДАКТИРОВАТЬ: Похоже, что PostSharp теперь является коммерческим продуктом. Если вы ищете решение с открытым исходным кодом (бесплатно), я предлагаю LinFu AOP .

3 голосов
/ 18 мая 2010

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

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

1 голос
/ 18 мая 2010

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

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

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

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

1 голос
/ 18 мая 2010

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

0 голосов
/ 18 мая 2010

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

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 голосов
/ 18 мая 2010

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

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

0 голосов
/ 18 мая 2010

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

0 голосов
/ 18 мая 2010

Чувак, это будет бриз на питоне. Я бы посмотрел в C # атрибуты.

...