Как стать автором
Обновить

Комментарии 25

Я понимаю, что статья полезна другим, но я только что, благодаря статье, открыл для себя NLog!
Спасибо!
Я сам написал эту статью по мотивам ответов на несколько другие проблемы :). Так что и за это мы хабр любим.
А я открыл для себя OzCode (www.oz-code.com)
а его про себя называю «решарпо-дебаггер» :)
Можно попробовать ещё для анализа уже скомпилированных сборок использовать Gendarme (штука, работающая с Mono.Cecil).
Решил начать с самого простого: объект лог, если он объявлен как поле некого класса должен быть статическим, как советуют на сайте nlog.
Использование статических переменных в любом виде — страшное зло. И система логирования не исключение.
Я придерживаюсь мнения, что нет плохих качеств, а есть не хорошие размеры.
Другими словами, статические переменные конечно зло, но все-таки есть исключения.

и конечно, спасибо, за наводку.
Есть. Например, для вещей, которые не меняются в ходе работы приложения, например, для кеша акцессоров свойств типов (когда через рефлексию вытаскивается список свойств, из них MethodInfo, далее Delegate.CreateDelegate, в общем, небыстрая операция). И случай логирования в них не входит. Невозможно нормально настроить раздельное логирование экземпляров объекта с разными настройками, например. Для разрешения зависимостей нужно использовать DI, а не статический класс внешней библиотеки.
Невозможно нормально настроить раздельное логирование экземпляров объекта с разными настройками, например. Для разрешения зависимостей нужно использовать DI, а не статический класс внешней библиотеки.

Согласен, но у меня есть пример, когда DI еще нет, а лог уже есть.
Интересно, почему ребята из nlog советуют:
In most cases you will have one logger per class, so it makes sense to give logger the same name as the current class. LogManager exposes a method which creates a logger for current class, called GetCurrentClassLogger(). Because loggers are thread-safe, you can simply create the logger once and store it in a static variable:


namespace MyNamespace
{
  public class MyClass
  {
    private static Logger logger = LogManager.GetCurrentClassLogger();
  }
}


или это, что бы nlog было сложнее выпилить? :)
Я думаю, что этот метод имеет смысл использовать если:
1. Вас не сильно интресует внедрение зависимостей
Объявляя переменную logger приватной статической, вы тем самым скрываете зависимость класса MyClass и тем самым ухудшаете его характеристики переиспользуемости и сопровождения. На самом деле есть много случаев, когда они не сильно важны, но об этом надо помнить.

2. Вам не сильно важна производительность
Метод GetCurrentClassLogger использует обращение к текущему стэку вызовов, что является небыстрой операцией.
github.com/NLog/NLog/blob/master/src/NLog/LogManager.cs#L161

В общем случаем, здесь все зависит от контекста, иногда стоит пожертвовать гибкостью, иногда нет.
2. Вам не сильно важна производительность
Метод GetCurrentClassLogger использует обращение к текущему стэку вызовов, что является небыстрой операцией.
github.com/NLog/NLog/blob/master/src/NLog/LogManager.cs#L161


Но разве плохая производительность не подталкивает к использованию именно «статической» переменной, которая создается одна на тип (класс), а не на каждый объект?
Ну почему же, если вы используете внедрение зависимостей через конструктор или свойство,
тогда почти любой контейнер зависимостей может создавать объект зависимости(в нашем случае это объект типа Logger) один раз на весь жизненный цикл приложения, а не на каждый объект требующий эту зависимость, что в свою очередь, почти эквивалентно использованию статической переменной.

Зависимость-зависимость-зависимость-… :-)
Конечно, но я говорю, про ситуацию, где внедрения зависимостей еще нет, а лог уже есть.
Всё ещё не понимаю, что мешает передавать через конструктор LogFactory.
Ну допустим, давайте пофантазируем, что может помешать передавать через конструктор LogFactory.
Например, сотни или тысячи классов, длинные цепочки от контекста, до конкретного класса, наличие нескольких клентов (нескольких разных контекстов). Все это может существенно усложнить работу по добавлению во все конструкторы LogFactory.
Напомню, внедрения зависимостей еще нет.
Например, сотни или тысячи классов, длинные цепочки от контекста, до конкретного класса
Напомню, внедрения зависимостей еще нет.
ССЗБ. Вы продолжаете писать неправильный код, потому что сейчас лениво сделать DI хотя бы в виде ServiceLocator-а, умеющего создавать графы объектов для тех классов, которые про DI не знают.
ССЗБ. Вы продолжаете писать неправильный код, потому что сейчас лениво сделать DI хотя бы в виде ServiceLocator-а, умеющего создавать графы объектов для тех классов, которые про DI не знают.

Извините, но вы делаете слишком много предположений, и не все из них соответствуют действительности.

Итого, согласимся на:
1. Писать статические поля — плохо.
2. Хорошо бы использовать DI или хотя бы ServiceLocator.
3. Не надо лениться и продолжать писать неправильный код.

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

Кстати, чисто, что бы улучшить мир, поделитесь примером или описанием "ServiceLocator-а, умеющего создавать графы объектов для тех классов, которые про DI не знают".
Кстати, чисто, что бы улучшить мир, поделитесь примером или описанием «ServiceLocator-а, умеющего создавать графы объектов для тех классов, которые про DI не знают».

Делаем статический класс Locator с методом Resolve<T>(), весь код которого заключается в обращении к DI-контейнеру. При необходимости получить экземпляр поддерживающего DI класса из кода, который про DI не знает, вместо new Foo() пишем Locator.Resolve<Foo>(). В случае наличия параметров, которые надо передать из этого кода, делаем Locator.Resolve<IFooFactory>.Create(arg1, arg2).
Может сам mezastel может так сразу ответить на этот вопрос.
А еще нет, но попробую посмотреть.
Конечно! Посмотрите например на плагин Agent Mulder, который использует SSR для добавления поддержки IoC в ReSharper.
Спасибо за плагин! (pun intended) :)

Кстати, автор этой статьи (constructor) написал замечательную статью про плагин Agent Mulder на хабре, можно почитать здесь.
Для таких целей пора Roslyn использовать.
Узнать, что передается в Dispose, можно по предыдущей инструкции. Предыдущая инструкция кладет 0 или 1 на стек, т. е. true или false.
Спасибо за совет.
Неужели опять, как в молодсти, придется «ассемблером» развлекаться :).
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории