web-dev-qa-db-ja.com

リフレクションを介して呼び出されるメソッドをマークするためのベストプラクティス?

私たちのソフトウェアには、リフレクションを介して動的に見つけられるべきいくつかのクラスがあります。すべてのクラスには、リフレクションコードがオブジェクトをインスタンス化する特定のシグネチャを持つコンストラクターがあります。
ただし、メソッドが参照されているかどうか(Visual Studio Code Lensなどを介して)を確認すると、リフレクションを介した参照はカウントされません。人々はそれらの参照を見逃して、明らかに使用されていないメソッドを削除(または変更)することができます。

リフレクションを介して呼び出されるメソッドをマーク/ドキュメント化するにはどうすればよいですか?

理想的には、メソッドは、同僚とVisual Studio/Roslynおよび他の自動化ツールの両方が、メソッドがリフレクションを介して呼び出されることが意図されていることを「確認」できるようにマークする必要があります。

私たちが使用できる2つのオプションを知っていますが、どちらも十分に満足できるものではありません。 Visual Studioは参照を見つけることができないので:

  • カスタム Attribute を使用し、この属性でコンストラクタをマークします。
    • 問題は、Attributeプロパティをメソッド参照にすることはできないため、コンストラクターは参照が0と表示されることです。
    • カスタム属性に慣れていない同僚は、おそらくそれを無視するでしょう。
    • 現在のアプローチの利点は、リフレクションパーツが属性を使用して、呼び出すコンストラクターを見つけることができることです。
  • コメントを使用して、メソッド/コンストラクターがリフレクション経由で呼び出されることを意図していることを文書化します。
    • 自動化ツールはコメントを無視します(同僚もそうするかもしれません)。
    • Xml Documentation Comments を使用して、Visual Studioにメソッド/コンストラクターへの追加の参照をカウントさせることができます。
      MyPluginを、リフレクターを介して呼び出すコンストラクターを持つクラスにしましょう。呼び出し側のリフレクションコードが、intパラメーターを受け取るコンストラクターを検索するとします。次のドキュメントは、そのコードlensに、1つの参照を持つコンストラクターを示しています。
      /// <see cref="MyPlugin.MyPlugin(int)"/> is invoked via reflection

より良いオプションはどれですか?
リフレクションを介して呼び出されることが意図されているメソッド/コンストラクターをマークするためのベストプラクティスは何ですか?

11

提案されたソリューションの組み合わせ:

  • XML Documentationタグを使用して、コンストラクター/メソッドがリフレクションを介して呼び出されることを文書化します。
    これにより、同僚(および私の将来の自分)が意図する使用法が明確になります。
  • <see> -タグを使用して、コンストラクタ/メソッドの参照カウントを増やします。
    これにより、そのコードlensおよびfind参照は、コンストラクター/メソッドが参照されていることを示します。
  • Resharperの注釈 UsedImplicitlyAttribute
    • Resharperは事実上の標準であり、[UsedImplicitly]は、意図したセマンティクスを正確に持っています。
    • Resharperを使用していないユーザーは、NuGetを介して JetBrains ReSharper Annotations をインストールできます。
      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で最もよく使用される3つの静的コード分析システムの両方に提供します。
欠点は、コメントと1つまたは2つの注釈の両方が少し冗長に見える可能性があることです。

12

私は.Netプロジェクトでこの問題を経験したことがありませんが、Javaプロジェクトで同じ問題が定期的に発生します。私の通常のアプローチは、@SuppressWarnings("unused")アノテーションを使用して理由を説明するコメント(警告を無効にする理由の文書化は私の標準コードスタイルの一部です-コンパイラーが何かを理解できないときはいつでも、人間も苦労している可能性が高いと思います)これには自動的に静的を確保するという利点があります分析ツールは、コードが直接参照することは想定されておらず、人間の読者に詳細な理由を与えることを認識しています。

Javaの@SuppressWarningsに相当するC#は SuppressMessageAttribute です。 privateメソッドの場合、メッセージ を使用できますCA1811:呼び出されていないプライベートコードを使用しないでください ;例えば。:

class MyPlugin
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage(
        "Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode",
        Justification = "Constructor is called via reflection")]
    private MyPlugin(int arg)
    {
        throw new NotImplementedException();
    }
}
5
Jules

文書化の代替手段は、リフレクション呼び出しが正常に実行されることを確認する単体テストを行うことです。

そうすれば、誰かがメソッドを変更または削除した場合、ビルド/テストプロセスは、何かが壊れていることを警告するはずです。

2
Nick Williams

さて、コードを確認しないと、このようなサウンドは、継承を導入するのに適しています。多分これらのクラスのコンストラクターが呼び出すことができる仮想または抽象メソッドですか?あなたがマークしようとしているメソッドが単なるコンストラクタである場合、あなたは本当にクラスではなくメソッドをマークしようとしていますね?クラスをマークするために過去に私が行ったことの1つは、空のインターフェースを作成することです。次に、コード検査ツールとリファクタリングは、インターフェースを実装するクラスを探すことができます。

0
Jeff Sall