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


11

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

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

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

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

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

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


明確にするために、これはある種のプラグインシステム用ですよね?
whatsisname 2017

2
あなたは同僚があなたがするすべてを無視するか、見逃すだろうと仮定しています...あなたはコードが仕事でそのような非効率性から妨げることはできません。文書化は、私にとっては、より簡単で、よりクリーンで、より安価で、賢明な方法のようです。そうでなければ、宣言型プログラミングは存在しません。
Laiv

1
Resharperには[UsedImplictly]属性があります。
CodesInChaos 2017

4
Xml docコメントオプションはおそらくあなたの最良のオプションだと思います。それは短く、自己文書化されており、「ハック」や追加の定義は必要ありません。
Docブラウン

2
XMLドキュメントコメントへの別の投票。とにかくドキュメントを作成している場合は、生成されたドキュメントで目立つはずです。
フランクHileman

回答:


12

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

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


効果のあるMeansImplicitUseAttribute独自の属性を作成するためにも使用できることに注意してくださいUsedImplicitly。これにより、適切な状況で多くの属性ノイズを低減できます。
Dave Cousineau

JetBrainsへのリンクが壊れています。
ジョンザブロスキー

5

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

Javaに相当するC#@SuppressWarningsSuppressMessageAttributeです。以下のためのプライベートメソッドあなたは、メッセージに使用することができ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と同様の属性をサポートしていると想定しています。誰かがそれを知っている場合は、私の回答を参照として編集してください...)
Jules

stackoverflow.com/q/10926385/5934037は次のようになります。
Laiv

1
最近、テストを実行してカバレッジを測定すること(Javaで)が、コードのブロックが実際に使用されていないかどうかを知る良い方法であることを発見しました。次に、それを削除するか、なぜ未使用であるかを確認します(反対が予想される場合)。検索中はコメントにもっと注意を払います。
Laiv

SuppressMessageAttributemsdn.microsoft.com/en-us/library/…)が存在します。最も近いメッセージはCA1811: Avoid uncalled private codemsdn.microsoft.com/en-us/library/ms182264.aspx)です。公開コードのメッセージはまだ見つかりません。
Kasper van den Berg

2

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

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


0

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


真の通常の継承が進むでしょう。そして、クラスは確かに継承によって関連付けられています。ただし、継承を超えて新しいインスタンスを作成する。その上、私は静的メソッドの「継承」に依存しています。C#はクラススロットをサポートしていないためです。私が使用する方法がありますが、それはこのコメントの範囲を超えています。
Kasper van den Berg
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.