22

C# 仕様にはプリプロセッサと基本的なディレクティブ (#define、#if など) が含まれていますが、言語には C/C++ などの言語に見られるような柔軟なプリプロセッサはありません。このような柔軟なプリプロセッサの欠如は、Anders Hejlsberg による設計上の決定であったと思います (ただし、残念ながら、これについての言及は現在見つかりません)。経験上、これは確かに良い決断でした。C/C++ をたくさん使っていた頃には、非常にひどい保守不可能なマクロが作成されていたからです。

とはいえ、もう少し柔軟なプリプロセッサが役立つと思われるシナリオはたくさんあります。次のようなコードは、いくつかの単純なプリプロセッサ ディレクティブによって改善される可能性があります。

public string MyProperty
{
  get { return _myProperty; }
  set
  {
    if (value != _myProperty)
    {
      _myProperty = value;
      NotifyPropertyChanged("MyProperty");
      // This line above could be improved by replacing the literal string with
      // a pre-processor directive like "#Property", which could be translated
      // to the string value "MyProperty" This new notify call would be as follows:
      // NotifyPropertyChanged(#Property);
    }
  }
}

このような非常に単純なケースを処理するプリプロセッサを作成するのは良い考えでしょうか? Steve McConnell はCode Complete (p208)で次のように書いています。

独自のプリプロセッサを作成 する 言語にプリプロセッサが含まれていない場合、プリプロセッサを作成するのはかなり簡単です...

私は引き裂かれています。このような柔軟なプリプロセッサを C# から除外することは、設計上の決定でした。ただし、私が非常に尊敬している著者は、状況によっては問題ないかもしれないと述べています。

C# プリプロセッサをビルドする必要がありますか? 私がやりたい簡単なことをするものはありますか?

4

13 に答える 13

11

カスタム属性に基づいて事後にコードを挿入するPostSharpのようなアスペクト指向ソリューションを検討することを検討してください。これはプリコンパイラの反対ですが、探している種類の機能(PropertyChanged通知など)を提供できます。

于 2008-08-31T23:37:27.910 に答える
6

C# プリプロセッサをビルドする必要がありますか? 私がやりたい簡単なことをするものはありますか?

いつでも C プリプロセッサを使用できます。C# は構文的に十分に近いものです。M4もオプションです。

于 2008-08-31T23:20:02.087 に答える
4

多くの人が短いコードはエレガントなコードと同じだと思っていますが、そうではありません。

あなたが提案した例は、あなたが示したようにコードで完全に解決されていますが、プリプロセッサディレクティブは何に必要ですか? コードを「前処理」するのではなく、コンパイラにプロパティにコードを挿入してもらいたい。これは一般的なコードですが、それはプリプロセッサの目的ではありません。

あなたの例では、制限をどこに置きますか? 明らかに、それはオブザーバー パターンを満たし、有用であることは間違いありませんが、プリプロセッサが提供しない柔軟性をコードが提供するため、実際に行われる有用なことがたくさんあります。プリプロセッサ ディレクティブを使用して一般的なパターンを実装しようとすると、言語自体と同じくらい強力である必要があるプリプロセッサで終了します。コードを別の方法で処理したい場合は、プリプロセッサ ディレクティブを使用しますが、コード スニペットだけが必要な場合は、プリプロセッサがそれを行うことを意図していないため、別の方法を見つけてください。

于 2008-09-01T00:20:52.703 に答える
3

C++ スタイルのプリプロセッサを使用すると、OP のコードを次の 1 行に減らすことができます。

 OBSERVABLE_PROPERTY(string, MyProperty)

OBSERVABLE_PROPERTY は多かれ少なかれ次のようになります。

#define OBSERVABLE_PROPERTY(propType, propName) \
private propType _##propName; \
public propType propName \
{ \
  get { return _##propName; } \
  set \
  { \
    if (value != _##propName) \
    { \
      _##propName = value; \
      NotifyPropertyChanged(#propName); \
    } \
  } \
}

処理するプロパティが 100 ある場合、コードの行数は最大 1,200 行になりますが、コード行数は最大 100 行になります。読みやすく、理解しやすいのはどれですか。どっちが書きやすい?

C# では、カット アンド ペーストで各プロパティを作成すると仮定すると、プロパティごとに 8 回の貼り付け、合計 800 回の貼り付けになります。マクロでは、貼り付けは一切ありません。コーディングエラーを含む可能性が高いのはどれですか? IsDirty フラグなどを追加する必要がある場合、どちらが変更しやすいですか?

かなりの数のケースでカスタム バリエーションが存在する可能性が高い場合、マクロはあまり役に立ちません。

他のツールと同様に、マクロは悪用される可能性があり、悪用されると危険にさえなる可能性があります。一部のプログラマーにとって、これは宗教的な問題であり、あるアプローチが別のアプローチより優れているかどうかは関係ありません。それがあなたなら、マクロを避けるべきです。非常に高度なツールを定期的に、巧みに、そして安全に使用している私たちにとって、マクロは、コーディング中だけでなく、下流のデバッグやメンテナンス中にもすぐに生産性を向上させることができます。

于 2013-09-30T16:40:01.083 に答える
3

C# のプリプロセッサの構築に反対する主な議論は、Visual Studio への統合です。インテリセンスと新しいバックグラウンド コンパイルをシームレスに動作させるには、(可能な場合でも) 多大な労力が必要です。

代替手段は、 ReSharperCodeRushなどの Visual Studio 生産性プラグインを使用することです。後者は、私の知る限りでは比類のないテンプレート システムを備えており、優れたリファクタリングツールが付属しています。

あなたが言及している正確なタイプの問題を解決するのに役立つもう1つのことは、PostSharpのようなAOPフレームワークです。
その後、カスタム属性を使用して共通機能を追加できます。

于 2008-09-01T12:59:13.270 に答える
1

INotifyPropertyChangedを実装するときに、問題の1つの重要な部分が欠落している可能性があると思います。あなたの消費者は、プロパティ名を決定する方法を必要としています。このため、プロパティ名を定数または静的読み取り専用文字列として定義する必要があります。これにより、コンシューマーはプロパティ名を「推測」する必要がなくなります。プリプロセッサを使用した場合、消費者はプロパティの文字列名をどのようにして知ることができますか?

public static string MyPropertyPropertyName
public string MyProperty {
    get { return _myProperty; }
    set {
        if (!String.Equals(value, _myProperty)) {
            _myProperty = value;
            NotifyPropertyChanged(MyPropertyPropertyName);
        }
    }
}

// in the consumer.
private void MyPropertyChangedHandler(object sender,
                                      PropertyChangedEventArgs args) {
    switch (e.PropertyName) {
        case MyClass.MyPropertyPropertyName:
            // Handle property change.
            break;
    }
}
于 2009-08-27T01:30:49.200 に答える
1

現在実行されているメソッドの名前を取得するには、スタック トレースを確認します。

public static string GetNameOfCurrentMethod()
{
    // Skip 1 frame (this method call)
    var trace = new System.Diagnostics.StackTrace( 1 );
    var frame = trace.GetFrame( 0 );
    return frame.GetMethod().Name;
}

プロパティ セット メソッドを使用している場合、名前は set_Property です。

同じ手法を使用して、ソース ファイルと行/列情報を照会することもできます。

ただし、私はこれをベンチマークしませんでした。プロパティ セットごとに 1 回スタック トレース オブジェクトを作成するのは、時間がかかりすぎる操作になる可能性があります。

于 2008-09-01T13:29:22.980 に答える
0

次のバージョンのC#を設計する場合、クラスの名前と関数の名前を保持するローカル変数が自動的に含まれる各関数について考えます。ほとんどの場合、コンパイラのオプティマイザがそれを取り出します。

でも、そういうものへの需要が多いかどうかはわかりません。

于 2008-08-31T23:47:48.540 に答える
0

@Jorge は次のように書いています。別の方法でコードを処理したい場合は、プリプロセッサ ディレクティブを使用しますが、コード スニペットだけが必要な場合は、プリプロセッサがそれを行うことを意図していないため、別の方法を見つけてください。

面白い。私は、プリプロセッサが必ずしもこのように機能するとは考えていません。提供されている例では、単純なテキスト置換を行っています。これは、ウィキペディアのプリプロセッサの定義と一致しています。

これがプリプロセッサの適切な使用法でない場合、一般的にコンパイルの前に行う必要がある単純なテキスト置換を何と呼ぶべきでしょうか?

于 2008-09-01T00:34:14.123 に答える
0

少なくとも提供されたシナリオでは、プリプロセッサを構築するよりもクリーンでタイプ セーフなソリューションがあります。

ジェネリックを使用します。そのようです:

public static class ObjectExtensions 
{
    public static string PropertyName<TModel, TProperty>( this TModel @this, Expression<Func<TModel, TProperty>> expr )
    {
        Type source = typeof(TModel);
        MemberExpression member = expr.Body as MemberExpression;

        if (member == null)
            throw new ArgumentException(String.Format(
                "Expression '{0}' refers to a method, not a property",
                expr.ToString( )));

        PropertyInfo property = member.Member as PropertyInfo;

        if (property == null)
            throw new ArgumentException(String.Format(
                "Expression '{0}' refers to a field, not a property",
                expr.ToString( )));

        if (source != property.ReflectedType ||
            !source.IsSubclassOf(property.ReflectedType) ||
            !property.ReflectedType.IsAssignableFrom(source))
            throw new ArgumentException(String.Format(
                "Expression '{0}' refers to a property that is not a member of type '{1}'.",
                expr.ToString( ),
                source));

        return property.Name;
    }
}

これは、代わりに a を返すように簡単に拡張できPropertyInfo、プロパティの名前だけでなく、より多くのものを取得できます。

であるためExtension method、事実上すべてのオブジェクトでこのメソッドを使用できます。


また、これは型安全です。
それを十分に強調することはできません。

(私はそれが古い質問であることを知っていますが、実用的な解決策が欠けていることがわかりました。)

于 2013-09-25T09:30:18.420 に答える
-1

C# を捨てる準備ができている場合は、 AST (Abstract Syntax Tree) 操作による信じられないほど柔軟なマクロサポートを備えたBoo言語をチェックしてみてください。C# 言語を捨てることができれば、それは本当に素晴らしいことです。

Boo の詳細については、次の関連する質問を参照してください。

于 2010-07-12T13:13:29.180 に答える