Я использую 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 вместо того, чтобы полуваляться дурака он. К сожалению, использование другого языка не является опцией на этом проекте.
Мы используем что-то почти идентичное:
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 получение типа вызова - ничто (в зависимости от используемых регистраторов).
Атрибуты в .NET не являются декораторами / активными компонентами, которые влияют на класс / член, к которому они прикреплены. Атрибуты - это метаданные , которые могут быть получены с помощью отражения. В C # нет средств декоратора, хотя есть решения АОП, расширяющие .NET тем, что вам нужно. Однако самое простое решение - просто скопировать эту строку в каждый класс.
Это именно задача для AOP - Aspect Oriented Programming. Посмотрите на PostSharp, это фреймворк .NET AOP, он позволит вам сделать именно то, что вы хотите.
Это работает путем модификации (или плетения) IL-кода в пост-компиляции, и добавления аспекта протоколирования в декорированный метод.
EDIT: Похоже, что PostSharp теперь является коммерческим продуктом. Если вы ищете решение с открытым исходным кодом (бесплатное), я предлагаю LinFu AOP.
Черт возьми, это было бы проще простого в питоне. Я бы посмотрел на Атрибуты C #.
Если это ASP.NET или Windows Forms, вы можете просто создать базовую страницу или базовую форму, от которой будут происходить все ваши формы/страницы. Вы можете объявить этот член на этом уровне и, вероятно, добиться желаемого.
Вы, вероятно, могли бы решить эту проблему с помощью PostSharp или другого инструмента аспектно-ориентированного программирования.
Было бы довольно тривиально использовать макрос в Boo, но это не сильно поможет в вашей ситуации.
Возможно, простым способом было бы написать метод расширения для интерфейса, тогда ваш класс просто должен "реализовать" (но не совсем, потому что расширение делает 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));
}
}
Мы обернули log4net, чтобы можно было легко его отключить. Это то, о чем мы вполне могли бы изменить свое мнение в будущем.
Пока мы этого не делаем, и вы, вероятно, получите удар по производительности, чтобы сделать это ... и я действительно не решаюсь даже предлагать это, потому что я не уверен, что это хорошая идея ... .вы могли бы ... если бы вы чувствовали себя достаточно дьявольски ... оберните log4net и сделайте что-нибудь подобное в своей оболочке.
var callingType = new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().DeclaringType
Если вы хорошо разбираетесь в том, как вести журнал, вы можете понести эти расходы только тогда, когда вам понадобится сообщение журнала.