8

#warningC#で、特定の条件が満たされた場合など、コードにプリプロセッサ ディレクティブを追加する MSIL コードを作成することは可能ですか? または、これはリフレクションで実行できるかもしれませんが、わかりません。

クラスのメソッドまたはプロパティに正しく適用されない場合、コンパイラの警告を生成するカスタム属性を作成しようとしています。カスタム属性を使用するだけで警告が発生するため、既存のObsolete属性を使用しても機能しません。これは望ましくありません。カスタム属性コンストラクターで条件をチェックし、その条件が true の場合はコンパイル警告が発生するようにします。

更新: 私の質問を読み返した後、コンパイル時と実行時の制約を混在させているという理由だけで、私が求めていることは不可能だと思います。ビルド後のタスクを実行して、ビルドしたばかりの DLL をチェックし、条件が true の場合にエラー メッセージを出力させることになると思います。

4

7 に答える 7

6

この質問はあなたの前のスレッドから来ているのを見ました。偉大なジェイミー・ザウィンスキーの言葉を誤って引用すると、「問題に直面したときに、『分かった、属性を使用する』と考える人がいます。今、彼らは 2 つの問題を抱えています」.

属性は、アセンブリのメタデータにコンパイルされた単なる帯域外データです。プログラムまたはツールが特定の属性を認識するように明示的にプログラムされていない限り、プログラムの実行またはツールの動作に影響を与えることはできません。リフレクションを使用してこれを行う必要があります。

あなたがする必要があるのは、独自のツールを作成することです。プロジェクトのビルド後のステップを使用して、アセンブリがビルドされた後に実行する必要があります。アセンブリをロードし、リフレクションを使用してアセンブリ内の型を反復処理する必要があります。タイプごとに、Type.GetMethods() を使用してメソッドを反復し、MethodInfo.GetCustomAttributes() を使用して、プログラムされている可能性のある属性を検出して構築します。

Type.GetInterfaces() を使用して、型によって実装されているインターフェイスを検出できます。インターフェース メソッドを実装するメソッドが存在するが、それを示す属性が欠落している場合は、文句を言うことができるようになりました。最終的な目標: インターフェイス メソッドを実装しているが、型がそれを継承していないという属性を持つメソッドを見つけたら、文句を言うことができます。

何か問題がある場合は、Environment.ExitCode を使用してツールのビルドを失敗させます。これにより、強制が処理されます。ところで、プログラマーはビルドを壊すことを本当に嫌います。それは彼らが属性を宗教的に使用することを奨励するかもしれません. または、ビルド後のステップを編集するように促すかもしれません。

于 2010-01-30T20:49:13.793 に答える
2

ブーについて聞いたことがありますか?コンパイラ パイプラインに接続できる興味深い方法があります。そのような機能の 1 つは構文属性と呼ばれます。これは、コンパイラが呼び出すインターフェイスを実装する属性であり、コード生成に参加できるようにします。

class Person:
  [getter(FirstName)]
  _fname as string

  [getter(LastName)]
  _lname as string

  def constructor([required] fname, [required] lname):
    _fname = fname
    _lname = lname

このコードの属性は、フィールドのパブリック ゲッターと、コンストラクター パラメーターの null チェックを生成します。それはすべて、コンパイルされたアセンブリになります。

私は常に、このような拡張性を C# コンパイラの一部にしたいと考えていました。たぶん、いつかそうなるでしょう。それまでは、CciSharpなどのポストコンパイラを使用できます。CCiSharp は、Boo シンタティック属性と同様に、アセンブリ内の特別な属性に基づいてCILを書き換えます。

このコードを考えると:

class Foo {
  [Lazy]
  public int Value { 
    get { return Environment.Ticks; } 
  }
}

CCiSharp は、に基づいてコードを次のように変更LazyAttributeします。

class Foo {
  int Value$Value; // compiler generated
  int Value$Initialized;
  int GetValueUncached() { 
    return Environment.Ticks;
  }
  public int Value  {
    get {
      if(!this.Value$Initialized) {
        this.Value$Value = this.GetValueUncached();
        this.Value$Initialized = true;
      }
      return this.Value$Value;
    }
}

CCiSharp はCommon Compiler Infrastructureプロジェクトに基づいています。これは、今後の .NET Framework 4.0 でコード コントラクトポスト コンパイラを実装するために使用されるものと同じです。

これが、生成された CIL を変更する方法です。

ただし、#warningディレクティブには CIL 表現がなく、コンパイラ ディレクティブのみです。このディレクティブを追加するために変更する必要があるのは、生成された CIL ではなく、C# コード自体です。そのためには、C# パーサーを実装する必要があります。他の回答で述べたように、生成されたアセンブリを反映して目的の警告を発行するビルド後のイベントを作成するのが最善の選択肢だと思います。

于 2010-02-06T23:20:30.247 に答える
2

コンパイラは、カスタム属性について次の 2 つを保存します。

  • 呼び出す属性コンストラクター
  • コンストラクターに渡す各パラメーターのデータ

コンストラクターは、アプリケーションの実行中にのみ呼び出され、誰かが Assembyl、Type、MethodInfo、ParameterInfo などに対して GetCustomAttributes を呼び出します。

考慮すべき他のオプションがいくつかあります。

  • コンパイル段階の後に実行するカスタム MSBuild タスクを記述し、コンパイル済みのアセンブリを読み込み、アプリケーションの属性の使用状況をチェックします。
  • 属性を使用して、AttributeUsage属性を適用できるコード項目を指定します。
  • 属性の検証を実行時まで延期します。
于 2010-01-30T19:45:37.943 に答える
2

要するに、いいえ。前処理ディレクティブは、ソース ファイルのコンパイル中に使用されるメタデータとしてのみ存在するため、IL 表現はありません。

あなたがやっているようなことは、カスタム FxCop ルールとしてより良いかもしれません。

于 2010-01-30T19:49:18.093 に答える
1

私の直感では、カスタム属性と条件に基づいて #warning ディレクティブを挿入することはできません。これは、コンパイラによってコンパイル時にキャッチされるためです。カスタム属性を最初に評価する必要があるため、鶏が先か卵が先かのような状況です。 #warning を挿入しますが、それを行うには、コンパイル時のアクションを最初に実行する必要があります。

これがお役に立てば幸いです。よろしくお願いします、トム。

于 2010-01-30T20:31:13.383 に答える
0

#warning ディレクティブは CSC のもの (つまり、コンパイラに特定の方法で動作するように指示している) であるため、答えはノーだと思います。未加工の MISL を作成する場合、明らかに、CSC は混在しません。したがって、何かを行うように指示するコンパイラはありません。

基本的に a ディレクティブ ('#warning' のような指示) は、指定された条件下で特定の方法で動作するように CSC に指示するものです。

于 2010-01-30T19:45:44.040 に答える