1

少し前に、JavaScript コード生成フレームワークを実装していたときに、特定の C# 設計上の問題に対処する必要がありました。私が思いついた解決策の 1 つは、「using」キーワードをまったく異なる方法で使用することでした。私はそれを階層的なコード構造を構築するための構文シュガーとして使用しました (まあ、元はとにかく 1 つでした)。このようなもの:

CodeBuilder cb = new CodeBuilder();

using(cb.Function("foo"))
{
    // Generate some function code
    cb.Add(someStatement);
    cb.Add(someOtherStatement);

    using(cb.While(someCondition))
    {
        cb.Add(someLoopStatement);

        // Generate some more code
    }
}

Function および While メソッドが IDisposable オブジェクトを返すため、これは機能しています。これは、廃棄時にビルダーに現在のスコープを閉じるように指示します。このようなことは、ハードコーディングする必要があるツリーのような構造に役立ちます。

そのような「ハッキング」は正当化されると思いますか? たとえば、C++ では、テンプレートや演算子のオーバーロードなどの機能の多くが乱用されており、この動作は多くの人によって奨励されていると言えます (たとえば、boost を見てください)。一方で、現代の多くの言語は、そのような悪用を思いとどまらせ、より制限された特定の機能を提供していると言えます。

もちろん、私の例はやや難解ですが、現実的です。では、特定のハッキングと問題全体についてどう思いますか? 同様のジレンマに遭遇したことがありますか? 虐待はどこまで許せる?

4

7 に答える 7

3

これは、言語内で言語を作成するためのより広範なメカニズムを備えた Ruby のような言語から吹き飛ばされたものだと思います (詳細を知りたい場合は、「dsl」または「ドメイン固有言語」を検索してください)。この点で、C# は柔軟性に欠けます。

このように DSL を作成することは良いことだと思います。より読みやすいコードになります。ブロックの使用は、C# の DSL の一部として役立つ場合があります。この場合、より良い代替手段があると思います。この場合の using の使用は、本来の目的から少し離れすぎています。これは読者を混乱させる可能性があります。たとえば、Anton Gogolevのソリューションの方が好きです。

于 2009-02-19T13:31:56.443 に答える
3

トピック外ですが、これがラムダでどれほどきれいになるかを見てください。

var codeBuilder = new CodeBuilder();
codeBuilder.DefineFunction("Foo", x =>
{
    codeBuilder.While(condition, y =>
    {
    }
}
于 2009-02-19T13:28:00.963 に答える
2

cb.Function(name) から返された使い捨てオブジェクトがステートメントを追加するオブジェクトであるとよいでしょう。この関数ビルダーが CodeBuilder のプライベート/内部関数への呼び出しを介して内部的に渡されることは問題ありませんが、パブリック コンシューマーにとってはシーケンスが明確であることだけです

Dispose の実装により、次のコードで実行時エラーが発生する限り。

CodeBuilder cb = new CodeBuilder();
var f = cb.Function("foo")
using(function)
{
    // Generate some function code
    f.Add(someStatement);
}
function.Add(something); // this should throw

次に、動作は直感的で比較的合理的であり、正しい使用法 (以下) は、これが発生するのを促進および防止します。

CodeBuilder cb = new CodeBuilder();
using(var function = cb.Function("foo"))
{
    // Generate some function code
    function.Add(someStatement);
}

ただし、提供された CodeDomProvider 実装ではなく、独自のクラスを使用している理由を尋ねなければなりません。(これには十分な理由があります。特に、現在の実装には c# 3.0 の機能の多くが欠けているためです)が、自分で言及していないため...

編集:ラムダを使用するというAnotonの提案を2番目に使用します。読みやすさが大幅に改善されました (また、式ツリーを許可するオプションがあります)

于 2009-02-19T14:03:15.713 に答える
1

IDisposable の最も厳密な定義に従うと、これは乱用になります。これは、マネージド オブジェクトによって決定論的な方法でネイティブ リソースを解放する方法として使用されることを意図しています。

IDisposable の使用は、本質的に「確定的な有効期間を持つ必要があるすべてのオブジェクト」によって使用されるように進化しました。これが間違っているとか間違っていると言っているわけではありませんが、多くの API とユーザーが IDisposable の使用を選択しています。その定義を考えると、それは虐待ではありません。

于 2009-02-19T13:36:57.697 に答える
1

私はそれをひどく悪い悪用とは考えていませんが、メンテナンス開発者のために構築している認知の壁のために、良い形だとも考えていません。using ステートメントは、特定のクラスのライフタイム管理を意味します。これは、通常の使用とわずかにカスタマイズされた使用 (@heeen の RAII アナログへの参照など) では問題ありませんが、これらの状況でも using ステートメントの精神は損なわれません。

あなたの特定のケースでは、@ Anton Gogolevのようなより機能的なアプローチは、言語の精神に沿っており、保守しやすいと主張するかもしれません。

あなたの主な質問に関しては、そのようなハックはそれぞれ、特定の状況における特定の言語の「最良の」ソリューションとして、最終的には独自のメリットに基づいている必要があると思います。もちろん、最善の定義は主観的なものですが、間違いなく (特に予算とスケジュールの外部制約が混在している場合) もう少しハック的なアプローチが唯一の合理的な答えである場合があります。

于 2009-02-19T13:40:39.433 に答える
1

私はよくブロックを使って「悪用」します。スコープを定義する優れた方法を提供すると思います。状態を変更する可能性のある操作中に、状態 (コンボ ボックスやマウス ポインターなど) をキャプチャして復元するために使用する一連のオブジェクトがあります。また、データベース接続の作成と削除にも使用します。

例えば:

using(_cursorStack.ChangeCursor(System.Windows.Forms.Cursors.WaitCursor))
{
    ...
}
于 2009-02-19T13:42:59.303 に答える
0

私はそれを虐待とは呼びません。私には、想像を絶する RAII テクニックのように見えます。人々はこれらをモニターなどに使用しています。

于 2009-02-19T13:32:07.257 に答える