Найкраща практика позначити метод, який викликається через рефлексію?


11

У нашому програмному забезпеченні є кілька класів, які слід динамічно знаходити за допомогою рефлексії. Усі класи мають конструктор з певним підписом, за допомогою якого код відображення створює об'єкти.
Однак, коли хтось перевіряє, чи посилається на метод (наприклад, через Visual Studio Code Lens), посилання через відображення не враховуються. Люди можуть пропустити свої посилання та видалити (або змінити), мабуть, невикористані методи.

Як слід позначити / документувати методи, які слід викликати через рефлексію?

В ідеалі метод повинен бути позначений таким чином, щоб і колеги, і Visual Studio / Roslyn та інші автоматизовані інструменти «бачили», що метод повинен бути викликаний за допомогою рефлексії.

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

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

Які кращі варіанти існують?
Яка найкраща практика для маркування методу / конструктора, який повинен бути викликаний за допомогою рефлексії?


Просто, щоб було зрозуміло, це для якоїсь системи плагінів, правда?
whatsisname

2
Ви припускаєте, що ваші колеги збираються ігнорувати або пропускати все, що ви робите ... Ви не можете запобігти коду від такої неефективності на роботі. Документування здається мені простішим, чистішим, дешевшим та доцільнішим способом. В іншому випадку не існувало б декларативного програмування.
Лаїв

1
Resharper має атрибут [UsedImplictly].
CodesInChaos

4
Я думаю, що опція коментарів документа Xml, мабуть, найкращий варіант. Це коротке, самодокументування, і не потребує жодних «хакків» чи додаткових визначень.
Doc Brown

2
Ще один голос за коментарі до документації xml. Якщо ви все одно створюєте документацію, вона повинна виділятися в створеній документації.
Френк Хілеман

Відповіді:


12

Поєднання запропонованих рішень:

  • Використовуйте теги документації XML для документування того, що конструктор / метод викликається через відображення.
    Це має пояснити передбачуване використання колегам (і моєму майбутньому самому).
  • Використовуйте 'трюк' через <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();
    }
}

Рішення надає призначене використання конструктора як людським читачам, так і 3 системам статичного аналізу коду, які найбільш використовуються для C # та Visual Studio.
Мінус полягає в тому, що як коментар, так і одна чи дві примітки можуть здатися трохи зайвими.


Зауважте, є також MeansImplicitUseAttributeте, що можна використовувати для створення власних атрибутів, які мають UsedImplicitlyефект. Це може зменшити велику кількість атрибутного шуму в правильних ситуаціях.
Дейв Кузен

Посилання на JetBrains порушено.
Іван Заброський

5

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

C # еквівалент 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 підтримує атрибут, схожий на той, який я згадую; якщо хтось знає, що це таке, будь ласка, відредагуйте мою відповідь як посилання на нього ...)
Жюль,


1
Нещодавно я виявив, що виконання тестів і охоплення мережевим покриттям (у Java) - це хороший спосіб дізнатися, чи блок коду справді не використовується. Тоді я можу видалити його або подивитися, чому не використовується (якщо очікую навпаки). Хоча пошук, коли я більше уваги приділяю коментарям.
Лаїв

Існує SuppressMessageAttribute( msdn.microsoft.com/en-us/library/… ). Найближче повідомлення є CA1811: Avoid uncalled private code( msdn.microsoft.com/en-us/library/ms182264.aspx ); Я ще не знайшов повідомлення для публічного коду.
Каспер ван ден Берг

2

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

Таким чином, якщо хтось змінить або видалить методи, які ваш процес складання / тестування повинен сповістити вас про те, що ви щось порушили


0

Ну, не бачачи свого коду, це виглядає так, що це було б хорошим місцем для введення певної спадщини. Може бути віртуальний або абстрактний метод, який може викликати конструктор цих класів? Якщо ви метод, який ви намагаєтеся позначити, - це лише конструктор, то ви справді намагаєтесь позначити клас, а не метод, так? Щось я робив для позначення класів - це зробити порожній інтерфейс. Тоді інструменти перевірки коду та рефакторинг можуть шукати класи, що реалізують інтерфейс.


Правда, зазвичай наслідування було б таким шляхом. І класи дійсно пов’язані між собою у спадок. Але створення нового екземпляра поза спадщиною. Крім того, я покладаюся на "успадкування" статичних методів, і оскільки C # не підтримує слоти класів; є спосіб, який я використовую, але це виходить за рамки цього коментаря.
Каспер ван ден Берг
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.