6

.NET 3.5 SP1をターゲットにしており、CommentCheckerを使用してXMLドキュメントを検証しています。次のようなクラスに到達するまで、すべてが正常に機能します。

/// <summary>
/// documentation
/// </summary>
public sealed class MyClass {
    /// <summary>
    /// documentation
    /// </summary>
    public void Method() {
    }
}

上記の例では、私が理解しているように、コンパイラーは私のクラスのデフォルトコンストラクターを生成します。これに伴う問題は、CommentCheckerが、コンストラクターにコメントがないことを通知する警告を生成することです。

この特殊なケースを検出して無視するようにプログラムを変更しようとしましたが、行き詰まりました。すでに試しましIsDefined(typeof(CompilerGeneratedAttribute), true)たが、うまくいきませんでした。

つまり、リフレクションを使用してデフォルトのコンストラクターを検出するにはどうすればよいですか?

4

3 に答える 3

6

あなたがILを少し掘り下げても構わないと思っているなら、あなたはそこへの道のほとんどを得ることができます。

まず、パラメーターがないことがわかっているインスタンスがあると仮定するConstructorInfoと、メソッド本体とメソッド本体のバイトを次のように取得できます(これを行うための拡張メソッドの作成を開始します)。

public static bool MightBeCSharpCompilerGenerated(
    this ConstructorInfo constructor)
{
    // Validate parmaeters.
    if (constructor == null) throw new ArgumentNullException("constructor");

    // If the method is static, throw an exception.
    if (constructor.IsStatic)
        throw new ArgumentException("The constructor parameter must be an " +
            "instance constructor.", "constructor");

    // Get the body.
    byte[] body = constructor.GetMethodBody().GetILAsByteArray();

7バイトを持たないメソッド本体はすべて拒否できます。

    // Feel free to put this in a constant.
    if (body.Length != 7) return false;

その理由は、次のコードで明らかになります。

ECMA-335(共通言語基盤(CLI)パーティションIからVI)のセクションI.8.9.6.6には、CLSルール21が記載されています。

CLSルール21:オブジェクトコンストラクターは、継承されたインスタンスデータへのアクセスが発生する前に、その基本クラスのインスタンスコンストラクターを呼び出す必要があります。(これは、コンストラクターを必要としない値型には適用されません。)

これは、他のことを行う前に、基本コンストラクターを呼び出す必要があることを意味します。これはILで確認できます。このためのILは次のようになります(ILコマンドの前にバイト値を括弧で囲んでいます)。

// Loads "this" on the stack, as the first argument on an instance
// method is always "this".
(0x02) ldarg.0

// No parameters are loaded, but metadata token will be explained.
(0x28) call <metadata token>

これで、バイトのチェックを開始できます。

    // Check the first two bytes, if they are not the loading of
    // the first argument and then a call, it's not
    // a call to a constructor.
    if (body[0] != 0x02 || body[1] != 0x28) return false;

次に、メタデータトークンがあります。このcall命令では、コンストラクターとともにメタデータトークンの形式でメソッド記述子を渡す必要があります。これは、(派生元の)クラスのMetadataTokenプロパティを通じて公開される4バイトの値です。MemberInfoConstructorInfo

メタデータトークンが有効であるかどうかを確認できましたが、メソッド本体のバイト配列の長さ(7バイト)をすでに確認しており、確認するバイトが1バイトしか残っていないことがわかっているため(最初の2つのオペコード) +4バイトのメタデータトークン=6バイト)、パラメーターのないコンストラクターに対するものかどうかを確認する必要はありません。パラメータがある場合は、スタックにパラメータをプッシュしてバイト配列を拡張する他のオペコードがあります。

最後に、コンストラクターで他に何も行われていない場合(コンパイラーが、ベースを呼び出すだけのコンストラクターを生成したことを示します)ret、メタデータトークンの呼び出し後に命令が発行されます。

(0x2A) ret

このように確認できます:

    return body[6] == 0x2a;
}

MightMightBeCSharpCompilerGeneratedに重点を置いて、メソッドが呼び出される理由に注意する必要があります。

次のクラスがあるとします。

public class Base { }
public class Derived : Base { public Derived() { } }

最適化なしでコンパイルする場合(通常はDEBUGモード)、C#コンパイラーはクラスにいくつかのnopコード(おそらくデバッガーを支援するため)を挿入します。Derivedこれにより、呼び出しでMightBeCSharpCompilerGeneratedfalseが返されます。

ただし、最適化がオンになっている場合(通常はモード)、C#コンパイラはオペコードRELEASEなしで7バイトのメソッド本体を出力するため、コンパイラが生成したコンストラクタはないように見えます。nopDerived

これが、メソッドがorMightの代わりに名前が付けられている理由です。それはあなたが見なければならない方法があるかもしれないことを示していますが、確かに言うことはできません。つまり、偽陰性が発生することはありませんが、陽性の結果が得られるかどうかを調査する必要があります。IsHas

于 2012-09-10T14:58:57.363 に答える
5

メタデータを介して自動生成されたデフォルトコンストラクターを検出する方法はありません。これをテストするには、2つのクラスを含むクラスライブラリを作成します。1つは明示的なデフォルトコンストラクターを使用し、もう1つは使用しません。次に、アセンブリでildasmを実行します。2つのコンストラクターのメタデータは同じです。

生成されたコンストラクターを検出しようとするのではなく、デフォルトのコンストラクターで不足しているドキュメントを許可するようにプログラムを変更するだけです。NDocやSandcastleGUIなどのほとんどのドキュメント生成プログラムには、すべてのデフォルトコンストラクタに標準ドキュメントを追加するオプションがあります。したがって、それらを文書化する必要はまったくありません。コードに明示的なデフォルトコンストラクターがある場合は、コンストラクターの上に3つのスラッシュ(///)を配置して(他には何もありません)、ドキュメントの欠落に関するVisualStudioの警告を無効にすることができます。

于 2010-07-06T22:49:41.437 に答える
1

次のコードは、タイプ内のパラメーターなしのコンストラクターに関する情報を返します。

var info = typeof(MyClass).GetConstructor(new Type[] {});

デフォルトのコンストラクターと明示的に指定されたパラメーターなしのコンストラクターを区別する方法がわかりません。

CommentCheckerの問題の考えられる回避策は、必要な場所にパラメーターなしのコンストラクターを明示的に作成し、適切にコメント化することです。

于 2010-07-06T22:50:03.430 に答える