web-dev-qa-db-ja.com

パラメーター名の反映:C#ラムダ式の乱用または構文の輝き?

MvcContrib グリッドコンポーネントを見て、 グリッド構文 で使用される構文上のトリックに魅了されましたが、同時に反発しました。

.Attributes(style => "width:100%")

上記の構文は、生成されたHTMLのスタイル属性をwidth:100%に設定します。ここで注意を払えば、「スタイル」はどこにも指定されず、式のパラメーターのnameから推定されます!私はこれを掘り下げなければならず、「魔法」が発生する場所を見つけました:

Hash(params Func<object, TValue>[] hash)
{
    foreach (var func in hash)
    {
        Add(func.Method.GetParameters()[0].Name, func(null));
    }
}

実際、コードは正式なコンパイル時のパラメーター名を使用して、属性の名前と値のペアの辞書を作成しています。結果として得られる構文コンストラクトは確かに非常に表現力豊かですが、同時に非常に危険です。ラムダ式の一般的な使用により、副作用なしで使用されるnamesを置き換えることができます。本にcollection.ForEach(book => Fire.Burn(book))と書かれている例があります。コードにcollection.ForEach(log => Fire.Burn(log))同じことを意味すると書くことができます。しかし、ここのMvcContrib Grid構文は突然、私は自分の変数に選択した名前に基づいて積極的に調べて決定を下すコードを見つけました!

それでは、C#3.5/4.0コミュニティとラムダ式愛好家とのこの一般的な慣習はありますか?それとも、私が心配するべきではない不正な1つのトリックマーベリックですか?

423
Remus Rusanu

これには相互運用性がありません。たとえば、このC#-F#の例を検討してください

C#:

public class Class1
{
    public static void Foo(Func<object, string> f)
    {
        Console.WriteLine(f.Method.GetParameters()[0].Name);
    }
}

F#:

Class1.Foo(fun yadda -> "hello")

結果:

「arg」が出力されます(「yadda」ではありません)。

その結果、ライブラリ設計者は、これらの種類の「乱用」を回避するか、。

146
Brian

nameのせいで奇妙ではないが、lambdaは不要だからだ;匿名タイプを使用し、より柔軟にすることができます。

.Attributes(new { style = "width:100%", @class="foo", blip=123 });

これは、ASP.NET MVCの多くで使用されているパターンであり(たとえば)、 その他の用途 (a 警告 、注 Ayendeの考え 名前が呼び出し元固有ではなくマジック値の場合)

154
Marc Gravell

私の意見を述べたかっただけです(私はMvcContribグリッドコンポーネントの作成者です)。

これは間違いなく言語の乱用です。疑いの余地はありません。ただし、Attributes(style => "width:100%", @class => "foo")への呼び出しを見たとき、私はそれが直観に反するとは考えません。
何が起きているかは明らかだと思います(匿名型アプローチよりも悪くはありません)。インテリセンスの観点からは、かなり不透明であることに同意します。

興味がある人のために、MvcContribでの使用に関する背景情報をいくつか...

私はこれを個人的な好みとしてグリッドに追加しました-辞書として匿名型を使用するのは好きではありません(「オブジェクト」をとるパラメーターを持つのはparams Func []をとるのと同じくらい不透明です)むしろ冗長です(たとえば、Attribute( "style"、 "display:none")。Attribute( "class"、 "foo")などへの複数の呼び出しを連結する必要があるなど、冗長な流なインターフェイスのファンでもありません)

C#の辞書リテラルの冗長な構文があれば、グリッドコンポーネントにこの構文を含める必要はありませんでした:)

また、MvcContribでのこの使用は完全にオプションであることも指摘しておきます。これらは、IDictionaryを使用するオーバーロードをラップする拡張メソッドです。このようなメソッドを提供する場合、他の言語との相互運用など、より「通常の」アプローチもサポートすることが重要だと思います。

また、誰かが「リフレクションのオーバーヘッド」に言及しましたが、このアプローチには実際にはあまりオーバーヘッドがないことを指摘したかっただけです-実行時のリフレクションや式のコンパイルはありません( http:// blog .bittercoder.com/PermaLink、guid、206e64d1-29ae-4362-874b-83f5b103727f.aspx )。

137
Jeremy Skinner

を好む

Attributes.Add(string name, string value);

それははるかに明示的で標準的なものであり、ラムダを使用しても何も得られません。

48
NotDan

Rails Landへようこそ:)

何が起こっているかを知っている限り、それは本当に悪いことではありません。 (問題があるのは、この種のものがうまく文書化されていないときです)。

Railsフレームワーク全体は、設定よりも規約の考え方に基づいて構築されています。物事に特定の方法で名前を付けると、使用している規則に結び付けられ、多くの機能を無料で入手できます。命名規則に従うことで、高速化を実現できます。すべてが見事に機能します。

このようなトリックを見たもう1つの場所は、Moqのメソッド呼び出しアサーションです。ラムダを渡しますが、ラムダは実行されません。彼らは単に式を使用してメソッド呼び出しが発生したことを確認し、そうでない場合は例外をスローします。

46
Jason Punyon

これは、複数のレベルでhorribleです。いいえ、これはRubyのようなものではありません。 C#と.Netの不正使用です。

タプル、匿名型、流doなインターフェイスなど、これをより簡単な方法で行う方法について多くの提案がありました。

何がそんなに悪いのかといえば、それが自分自身の利益のために空想する正しい方法だということです。

  • VBからこれを呼び出す必要がある場合はどうなりますか?

    .Attributes(Function(style) "width:100%")

  • 完全に直感に反するインテリセンスは、物を渡す方法を理解するのにほとんど役立ちません。

  • 不必要に非効率的です。

  • 誰もそれを維持する手がかりを持ちません。

  • 属性に渡される引数のタイプは何ですか、それはFunc<object,string>ですか?その意図はどのように明らかになっていますか。 「オブジェクトのすべての値を無視してください」と言うインテリセンスのドキュメントは何ですか

嫌悪感を持っているあなたは完全に正当化されていると思います。

42
Sam Saffron

私は「構文の輝き」のキャンプにいます。彼らがそれを明確に文書化すれば、それはこのおかしくてかっこいいように見えますが、ほとんど問題ありません!

40
Blindy

両方。ラムダ式の乱用および構文の輝き。

37
Arnis Lapsa

私はこのような使い方に出会うことはほとんどありませんでした。私はそれが「不適切」だと思う:)

これは一般的な使用方法ではなく、一般的な規則と矛盾しています。この種の構文には、もちろん長所と短所があります。

短所

  • コードは直感的ではありません(通常の規則は異なります)
  • 壊れやすい傾向があります(パラメーターの名前を変更すると機能が壊れます)。
  • テストするのはもう少し難しくなります(APIを偽造するには、テストでリフレクションを使用する必要があります)。
  • 式が集中的に使用される場合、値(反射コスト)だけでなくパラメーターを分析する必要があるため、遅くなります。

長所

  • 開発者がこの構文に調整した後は読みやすくなります。

最下行-パブリックAPI設計では、より明示的な方法を選択します。

21
Elisha

いいえ、それは確かに一般的な習慣ではありません。それは直感に反します。コードを見て、それが何をするのかを理解する方法はありません。使用方法を理解するには、使用方法を知る必要があります。

デリゲートの配列を使用して属性を提供する代わりに、メソッドの連鎖はより明確でパフォーマンスが向上します。

.Attribute("style", "width:100%;").Attribute("class", "test")

これはもう少し入力する必要がありますが、明確で直感的です。

18
Guffa

次の問題点:

html.Attributes["style"] = "width:100%";
17
Tommy Carlier

この「恐ろしさ」についての不満は、長年のc#の人たちが過剰に反応していることです(そして私は長年のC#プログラマであり、今でも言語の大ファンです)。この構文について恐ろしいことは何もありません。これは、構文を表現しようとしているもののように見せるための単なる試みです。何かの構文に「ノイズ」が少ないほど、プログラマはそれを理解しやすくなります。 1行のコードのノイズを減らすことは少しだけ助けになりますが、それがますます多くのコードに蓄積されるようにすれば、大きなメリットになることがわかります。

これは、DSLがあなたに与えているのと同じ利点を目指して作者が試みた試みです。コードがあなたが言おうとしていることのように「見える」とき、あなたは魔法の場所に到達しました。これが相互運用に適しているかどうか、または「複雑さ」のコストを正当化するために匿名メソッドよりも優れているかどうかを議論できます。公平なことです...そのため、プロジェクトでは、この種の構文を使用するかどうかを適切に選択する必要があります。しかし、それでも...これはプログラマーによる賢明な試みであり、結局のところ、私たち全員がやろうとしていることです(気づいているかどうかにかかわらず)。そして、私たち全員がやろうとしているのは、「コンピューターに、やりたいことについて考えている方法にできるだけ近い言語で、やりたいことをコンピューターに伝えてください」です。

内部的にソフトウェアをより保守しやすく、より正確にするための鍵であると考えるのと同じ方法で、コンピューターに命令を表現することに近づきます。

編集:私は「ソフトウェアをより保守しやすく、より正確にするための鍵」と言っていましたが、これは非常に素朴で誇張されたユニコーンのビットです。 「キー」に変更しました。

17
Charlie Flowers

これを使用してフレーズを作成できますか?

マジックラムダ(n):マジックストリングを置き換えるためだけに使用されるラムダ関数。

17
citizenmatt

これは、式ツリーの利点の1つです。追加の情報については、コード自体を調べることができます。これが、.Where(e => e.Name == "Jamie")を同等のSQL Where句に変換する方法です。これは式ツリーの巧妙な使用法ですが、これ以上先に進めないことを望みます。より複雑なものは、置き換えたいコードよりも難しい可能性が高いため、自己制限的であると思われます。

12
Jamie Penney

これは興味深いアプローチです。式の右側を定数のみに制限した場合、次を使用して実装できます。

Expression<Func<object, string>>

デリゲートの代わりに本当に欲しいものだと思います(ラムダを使用して両側の名前を取得しています)以下の素朴な実装を参照してください:

public static IDictionary<string, string> Hash(params Expression<Func<object, string>>[] hash) {
    Dictionary<string, string> values = new Dictionary<string,string>();
    foreach (var func in hash) {
        values[func.Parameters[0].Name] = ((ConstantExpression)func.Body).Value.ToString();
    }
    return values;
}

これは、スレッドで前に言及した言語間の相互運用の問題に対処することさえあります。

7
davidfowl

このコードは非常に巧妙ですが、解決するより多くの問題を引き起こす可能性があります。

指摘したように、パラメーター名(スタイル)とHTML属性の間にあいまいな依存関係があります。コンパイル時のチェックは行われません。パラメーター名の入力ミスがあった場合、ページにはおそらくランタイムエラーメッセージは表示されませんが、論理バグを見つけるのははるかに難しくなります(エラーはありませんが、不正な動作)。

より良い解決策は、コンパイル時にチェックできるデータメンバーを持つことです。したがって、これの代わりに:

.Attributes(style => "width:100%");

styleプロパティを持つコードは、コンパイラでチェックできます。

.Attributes.Style = "width:100%";

あるいは:

.Attributes.Style.Width.Percent = 100;

これはコードの作成者にとってはより多くの作業ですが、このアプローチはC#の強力な型チェック機能を利用しており、そもそもバグがコードに侵入するのを防ぎます。

6

実際、Ruby =)のように見えます。少なくとも私にとっては、後の動的な「ルックアップ」に静的リソースを使用することは、API設計の考慮事項に適合しません。

値を設定するためにキーを追加する必要がない場合、IDictionaryから継承する(またはしない)ことで、PHP配列のように動作するインデクサーを提供できます。これは、c#だけでなく.netセマンティクスの有効な使用であり、ドキュメントが必要です。

お役に立てれば

5

私見、それはそれを行うクールな方法です。クラスコントローラーに名前を付けると、MVCのコントローラーになるという事実が大好きです。そのため、ネーミングが重要な場合があります。

また、ここでの意図は非常に明確です。 .Attribute( book => "something")book="something"になり、.Attribute( log => "something")log="something"になることを理解するのは非常に簡単です

慣習のように扱うなら、それは問題ではないと思います。私は、あなたがより少ないコードを書いて、意図を明白にするものは何でも良いことだと思います。

5
madaboutcode

私の意見では、ラムダの乱用です。

シンタックスの輝きに関しては、style=>"width:100%"はわかりにくいです。特に=>の代わりに=が原因で

4
mfeingold

メソッド(func)名が適切に選択されている場合、これはメンテナンスの頭痛を回避する素晴らしい方法です(つまり、新しいfuncを追加しますが、関数パラメーターマッピングリストに追加するのを忘れました)。もちろん、それを大量に文書化する必要があり、そのクラスの関数のドキュメントからパラメータのドキュメントを自動生成する方が良いでしょう...

3
Craig Trader

これは「マジックストリングス」に勝るとは思いません。私はこれについても匿名型のファンではありません。より良く、強く型付けされたアプローチが必要です。

1
JamesK