Лучшая практика, чтобы пометить метод, который вызывается с помощью отражения?

11

Наше программное обеспечение имеет несколько классов, которые должны быть динамически найдены с помощью отражения. Все классы имеют конструктор с определенной сигнатурой, посредством которой код отражения создает объекты.
Однако, когда кто-то проверяет, есть ли ссылка на метод (например, через Visual Studio Code Lens), ссылка через отражение не учитывается. Люди могут пропустить свои ссылки и удалить (или изменить) явно неиспользуемые методы.

Как мы должны пометить / документировать методы, предназначенные для вызова через отражение?

В идеале метод должен быть помечен таким образом, чтобы как коллеги, так и Visual Studio / Roslyn и другие автоматизированные инструменты «видели», что метод предназначен для вызова с помощью отражения.

Я знаю два варианта, которые мы можем использовать, но оба они не совсем удовлетворяют. Поскольку Visual Studio не может найти ссылки:

  • Используйте пользовательский атрибут и пометьте конструктор этим атрибутом.
    • Проблема заключается в том, что свойства атрибута не могут быть ссылкой на метод, поэтому конструктор все равно будет отображаться как имеющий 0 ссылок.
    • Коллеги, не знакомые с пользовательским атрибутом, вероятно, проигнорируют его.
    • Преимущество моего текущего подхода состоит в том, что часть отражения может использовать атрибут, чтобы найти конструктор, который он должен вызвать.
  • Используйте комментарии, чтобы задокументировать, что метод / конструктор предназначен для вызова через отражение.
    • Автоматизированные инструменты игнорируют комментарии (и коллеги могут делать то же самое).
    • Комментарии к XML-документации могут использоваться для подсчета в Visual Studio дополнительной ссылки на метод / конструктор:
      Позвольте MyPluginбыть классом, конструктор которого вызывается через отражение. Предположим, что вызывающий код отражения ищет конструкторы, которые принимают intпараметр. Следующая документация показывает, что кодовая линза показывает конструктор, имеющий 1 ссылку:
      /// <see cref="MyPlugin.MyPlugin(int)"/> is invoked via reflection

Какие лучшие варианты существуют?
Какова наилучшая практика для маркировки метода / конструктора, который должен вызываться с помощью отражения?

Каспер ван ден Берг
источник
Просто чтобы быть ясно, это для какой-то системы плагинов, верно?
whatsisname
2
Вы предполагаете, что ваши коллеги будут игнорировать или пропускать все, что вы делаете ... Вы не можете предотвратить код от такой неэффективности на работе. Документирование кажется мне проще, чище, дешевле и целесообразнее. Иначе не было бы декларативного программирования.
17
1
Resharper имеет атрибут [UsedImplictly].
CodesInChaos
4
Я думаю, вариант комментария к документу Xml, вероятно, ваш лучший вариант. Он короткий, самодокументируемый и не требует каких-либо «взломов» или дополнительных определений.
Док Браун
2
Еще один голос за комментарии XML документации. Если вы все равно создаете документацию, она должна выделяться в сгенерированной документации.
Фрэнк Хилман

Ответы:

12

Комбинация предложенных решений:

  • Используйте теги XML Documentation для документирования вызова конструктора / метода с помощью отражения.
    Это должно прояснить предполагаемое использование коллегам (и моему будущему я).
  • Используйте трюк через <see>-tag, чтобы увеличить количество ссылок для конструктора / метода.
    Это делает этот код объективным, а поиск ссылок показывает, что на конструктор / метод ссылаются.
  • Аннотировать с Решарпером UsedImplicitlyAttribute
    • Resharper является стандартом де-факто и [UsedImplicitly]имеет точно предполагаемую семантику.
    • Те, кто не использует Resharper, могут установить аннотации JetBrains ReSharper через NuGet:
      PM> Install-Package JetBrains.Annotations.
  • Если это частный метод, и вы используете анализ кода Visual Studio, используйте SupressMessageAttributeдля сообщения CA1811: Avoid uncalled private code.

Например:

class MyPlugin
{
    /// <remarks>
    /// <see cref="MyPlugin.MyPlugin(int)"/> is called via reflection.
    /// </remarks>
    [JetBrains.Annotations.UsedImplicitly]
    public MyPlugin(int arg)
    {
        throw new NotImplementedException();
    }

    /// <remarks>
    /// <see cref="MyPlugin.MyPlugin(string)"/> is called via reflection.
    /// </remarks>
    [JetBrains.Annotations.UsedImplicitly]
    [System.Diagnostics.CodeAnalysis.SuppressMessage(
        "Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode",
        Justification = "Constructor is called via reflection")]
    private MyPlugin(string arg)
    {
        throw new NotImplementedException();
    }
}

Решение предоставляет предполагаемое использование конструктора как читателям-людям, так и трем системам статического анализа кода, наиболее часто используемым в C # и Visual Studio.
Недостатком является то, что и комментарий, и одна или две аннотации могут показаться немного излишними.

Каспер ван ден Берг
источник
Обратите внимание, что есть также, MeansImplicitUseAttributeчто может быть использовано для создания ваших собственных атрибутов, которые имеют UsedImplicitlyэффект. Это может уменьшить много шума атрибута в правильных ситуациях.
Дэйв Кузино
Ссылка на JetBrains не работает.
Джон Заброски
5

У меня никогда не было этой проблемы в проекте .Net, но у меня регулярно есть такая же проблема с проектами Java. Мой обычный подход заключается в использовании @SuppressWarnings("unused")аннотации с добавлением комментария, объясняющего почему (документирование причины отключения любых предупреждений является частью моего стандартного стиля кода - всякий раз, когда компилятор не может что-то выяснить, я предполагаю, что, вероятно, человек может бороться тоже). Преимущество этого заключается в том, что инструменты статического анализа автоматически получают информацию о том, что в коде не должно быть прямых ссылок, и дают подробное объяснение для читателей.

С # эквивалент Java @SuppressWarningsэто SuppressMessageAttribute. Для приватных методов вы можете использовать сообщение CA1811: избегайте невостребованного приватного кода ; например:

class MyPlugin
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage(
        "Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode",
        Justification = "Constructor is called via reflection")]
    private MyPlugin(int arg)
    {
        throw new NotImplementedException();
    }
}
Жюль
источник
(Я не знаю, но предполагаю, что CLI поддерживает атрибут, похожий на атрибут Java, о котором я упоминал; если кто-то знает, что это такое, отредактируйте мой ответ как ссылку на него ...)
Жюль
stackoverflow.com/q/10926385/5934037 выглядит так.
Laiv
1
Недавно я обнаружил, что выполнение тестов и тестирование покрытия (в java) - это хороший способ узнать, действительно ли блок кода не используется. Тогда я могу удалить его или посмотреть, почему не используется (если я ожидаю обратного). В то время как поиск, когда я уделяю больше внимания комментариям.
Laiv
Существует SuppressMessageAttribute( msdn.microsoft.com/en-us/library/… ). Наиболее близким сообщением является CA1811: Avoid uncalled private code( msdn.microsoft.com/en-us/library/ms182264.aspx ); Я еще не нашел сообщение для открытого кода.
Каспер ван ден Берг
2

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

Таким образом, если кто-то изменяет или удаляет методы, ваш процесс сборки / тестирования должен предупредить вас, что вы что-то сломали.

Ник Уильямс
источник
0

Ну, не видя ваш код, вроде бы это было бы хорошим местом для введения некоторого наследования. Может быть, виртуальный или абстрактный метод, который может вызвать конструктор этих классов? Если вы метод, который вы пытаетесь пометить, это просто конструктор, то вы действительно пытаетесь пометить класс, а не метод, да? Что-то, что я сделал в прошлом, чтобы пометить классы, - создать пустой интерфейс. Затем инструменты проверки кода и рефакторинга могут искать классы, которые реализуют интерфейс.

Джефф Салл
источник
Правда, как правило, наследство будет в порядке. И классы действительно связаны по наследству. Но создание нового экземпляра в наследство. Кроме того, я полагаюсь на «наследование» статических методов, и поскольку C # не поддерживает слоты классов; Есть способ обойти, который я использую, но это выходит за рамки этого комментария.
Каспер ван ден Берг