420

私は見ていますMvcContribグリッドコンポーネントと私は魅了されていますが、同時に反発しています。グリッド構文

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

上記の構文は、生成されたHTMLのstyle属性を次のように設定します。width:100%。今あなたが注意を払うならば、「スタイル」はどこにも特定されていません、から推論されます式内のパラメータの意味私はこれを掘り下げる必要があり、「魔法」が起こる場所を見つけました:

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

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

では、C#3.5 / 4.0コミュニティやラムダ式を愛する人たちとの共通のやり方なのでしょうか。それとも私が心配してはいけない不正なワントリック異端者ですか?


  • 単に構文を解析するのではなく、コードの意図を見ても構わない限り、これは明白に見えると私は主張します。あなたが良いコードを読んでいるのであれば、それはあなたがとにかくやるべきことです。構文は単なる意図のための手段であり、私はこれが意図を明らかにするコードであると主張するでしょう。 - Jamie Penney
  • 私はちょうどAnders(そしてデザインチームの他のメンバー)に彼らが何を考えたのか尋ねました。その結果が家族向けの新聞に印刷できないとしましょう。 - Eric Lippert
  • C#には現在、マップ、特に関数に渡されるマップのための明快で軽い構文が欠けています。さまざまな動的言語(Ruby、Python、JavaScript、ColdFusion 9)には、ある程度まではっきりした軽い構文があります。 - yfeldblum
  • ああ、私はそれが嬉しいと思います。マップの構文については、ええ、新しい{{" Do"、" A deer"を実行できれば素晴らしいでしょう。 new [] {1、2,3}がint配列の構築を推論するのと同様に、コンパイラにマップの構築を推論させます。このようなことを検討しています。 - Eric Lippert
  • Eric Lippert、私はあなたを非常に尊敬していますが、あなたとそのグループ(私はFREAKINを非常に尊重しているAndersを含む)はこれをあまりにも厳しく非難しています。あなたが認めるように、C#はマップのための厳密な構文を欠いています、そして、他のいくつかの言語(Rubyのような)は素晴らしいものを持っています。この男は彼が欲しい構文を取得する方法を見つけました。それを表現するための類似の方法があることを認めますほとんど欠点が少なくても、彼の構文と同じくらい表現力豊かです。しかし、彼の構文とそれを得るために彼が一生懸命働いたという事実は、言語強化の必要性を明らかに示しています。過去のパフォーマンスは、皆さんがそのために素晴らしいものを生み出すことを示しています。 - Charlie Flowers

21 답변


146

これは相互運用性が良くありません。たとえば、この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"ではありません)。

結果として、ライブラリ設計者は、これらの種類の '不正使用'を回避するか、または.Net言語間で相互運用性を確保したい場合は少なくとも '標準的な'オーバーロード(文字列名を追加パラメータとして使用)を提供する必要があります。


  • あなたはしません。この戦略は単なる移植性がありません。 (逆の例として、F#が戻り型のみが異なるメソッドをオーバーロードできる場合があります(型推論はそれを行うことができます)。これはCLRで表現できます。そうすれば、それらのAPIはC#から呼び出すことはできません。)相互運用性に関しては、'エッジ'には常にトレードオフがあります。どのようなメリットがあるのか、どのような相互運用性があるのかについての機能。 - Brian
  • 何もしない理由として、相互運用性がないことを嫌います。相互運用性が要件であるならばそれをし、そうでなければ、なぜそれを心配しますか?これはYAGNI IMHOです。 - jfar
  • @jfar:.NET CLRでは、アセンブリが次の言語で生成されるため、土地の相互運用性はまったく新しい次元になります。どれかコンパイラは、どのコンパイラからも消費されるはずです。その他の言語。 - Remus Rusanu
  • CLSに準拠する必要はないことに同意しますが、ライブラリやコントロールを作成している場合は(これを開始するスニペットがグリッドからのものである場合は、はい)、これは良い考えのようです。あなたのオーディエンス/顧客基盤を制限するだけ - JMarsch
  • :Func< object、string>を変更する価値があるかもしれません。 <<< Func< object、string>>にまた、式の右辺を単に定数になるように制限している場合は、これを行う実装を使用できます。public static IDictionary< string、string>ハッシュ(params式< Func< object、string>> [] hash){辞書< string、string>値=新しい辞書<文字列、文字列>(); foreach(ハッシュ内のvar関数){values [func.Parameters [0] .Name] =(文字列)((ConstantExpression)func.Body).Value;戻り値。 } - davidfowl

153

私はそのおかげであまり変わっていないことがわかりますしかし、ラムダは不要;それは匿名型を使用することができ、そしてより柔軟性があります:

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

これは、(たとえば)ASP.NET MVCの大部分で使用されているパターンです。その他の用途(a警告、また注意してくださいAyendeの考え名前が呼び出し側固有ではなく魔法の値である場合)


  • これには相互運用性の問題もあります。そのような匿名型の作成をすべての言語がその場でサポートしているわけではありません。 - Brian
  • 誰もこの質問に本当に答えなかったのではなく、代わりに「これはより良い」と回答しています。引数。 :pそれは虐待ですか? - Sam Saffron
  • Eric Lippertのこれに対する反応を見てみたいです。なぜならFRAMEWORKコードで。それは恐ろしいことです。 - Matt Hinze
  • 読みやすさが問題だとは思わない後にコードは書かれています。本当の問題はコードの学習可能性だと思います。あなたの知性が言うときあなたは何を考えるつもりですか.Attributes(オブジェクトobj)?何をメソッドに渡すべきかわからないので、あなたはドキュメントを(誰もしたくない)を読んで行かなければなりません。これが質問の例よりも優れているとは思わない。 - NotDan
  • @Arnis - なぜもっと柔軟なのか:暗黙のパラメータ名に頼るのではなく、たぶん(私を引用しないでください)いくつかのラムダ実装(他の言語)で問題を起こします - しかし、定義されたプロパティを持つ通常のオブジェクトを使うこともできます。たとえば、HtmlAttributes期待される属性を持つクラス(インテリセンス用)、そして単にそれらを無視するnull値... - Marc Gravell

137

私の意見を投げ入れたいだけでした(私はMvcContribグリッドコンポーネントの作者です)。

これは間違いなく言語の乱用です - それについて疑いはありません。しかし、私は実際には直感に反するとは考えていません。Attributes(style => "width:100%", @class => "foo")

何が起こっているのかは明らかだと思います(確かに匿名型のアプローチよりも悪くありません)。理解の観点からすると、私はそれがかなり不透明であることに同意します。

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

私は個人的な好みとしてこれをグリッドに追加しました - 私は辞書として匿名型を使うのは好きではありません( "object"をとるパラメータはparams Func []をとるものと同じくらい不透明です)そしてDictionaryコレクション初期化子はどちらかと言えば冗長です(私はまた、冗長な流暢なインターフェースのファンではありません。たとえば、Attribute( "style"、 "display:none")を複数回呼び出して連鎖させる必要があります。Attribute( "class"、 "foo")など)

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

私はまた、MvcContribでこれを使用することは完全にオプションであることを指摘したい - これらは代わりにIDictionaryをとるオーバーロードをラップする拡張メソッドである。あなたがこのような方法を提供するならば、あなたはまた、例えば他の言語との相互運用のために、より「通常の」アプローチをサポートするべきであることが重要であると思います。

また、誰かが「リフレクションオーバーヘッド」について言及しました、そして私はちょうどこのアプローチではあまりオーバーヘッドがないことを指摘したかった - 実行時リフレクションも式のコンパイルも含まれていません。http://blog.bittercoder.com/PermaLink,guid,206e64d1-29ae-4362-874b-83f5b103727f.aspx


  • 私のブログで、ここで取り上げた問題のいくつかについてもう少し詳しく説明してみました。jeremyskinner.co.uk/2009/12/02/lambda-abuse-the-mvccontrib-hash - Jeremy Skinner
  • Intellisenseでは、匿名のオブジェクトと同じくらい不透明です。 - Brad Wilson
  • インターフェースが経由して追加されたことを言及するための+1オプション拡張メソッドC#以外のユーザー(および言語の乱用に気を悪くされる人)は、単にそれを使用することを控えることができます。 - Jørn Schou-Rode

48

を好む

Attributes.Add(string name, string value);

それはもっと明白で標準的で、ラムダを使っても何も得られません。


  • それでも?html.Attributes.Add("style", "width:100%");同じくらい上手に読まないstyle = "width:100%"(実際に生成されたHTML)style => "width:100%"それは結果のHTMLでどのように見えるかに非常に近いです。 - Jamie Penney
  • それらの構文は、.Attributes(id =>' foo'、@ class =>' bar'、style =>' width:100%&#のようなトリックを可能にします。 39;)。関数シグネチャは、可変数の引数にparams構文を使用します。Attributes(params Func< object、object> [] args)。それは非常に強力です、しかしそれは私を連れて行きましたかなり長い間wtfを理解する - Remus Rusanu
  • @Jamie:C#コードをHTMLコードのように見せようとするのは、設計上の理由から悪い理由です。それらは完全に異なる目的のために完全に異なる言語であり、そしてそれらは同じに見えるべきではありません。 - Guffa
  • 「美しさ」を犠牲にすることなく、匿名のオブジェクトを使用することもできます。属性(new {id = "foo"、@class = "bar"、style = "width:100%"}) - Funka
  • @Guffaなぜそれがデザイン決定の悪い理由になるのでしょうか?なぜ彼らは同じに見えないのですか?その推論によって彼らはすべきです意図的に異なって見えます?私はあなたの間違ったことを言っているのではなく、私はあなたがより完全にあなたの主張を詳しく述べたいと思うかもしれないと言っているのです。 - Samantha Branham

46

レールランドへようこそ:)

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

Railsフレームワークの全体は、設定に関する規約の考え方に基づいて構築されています。ある意味で物事に名前を付けることは彼らが使っている慣習にあなたをかぎらせ、あなたは無料でたくさんの機能を手に入れる。命名規則に従うことで、あなたはより速く進んでいます。全体が見事に機能します。

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


  • 私は少し躊躇していました、しかし私は同意します。リフレクションのオーバーヘッドを除いて、Add()のように文字列を使用することとラムダパラメータ名を使用することの間に大きな違いはありません。少なくとも私が考えることができること。あなたはそれを消し去って" sytle"と入力することができます。両方の方法に気づかずに。 - Samantha Branham
  • なぜこれが私にとって奇妙ではなかったのか理解できず、それから私はRailsを思い出しました。 :D - Allyn

42

これは恐ろしい複数のレベルで。いいえ、これはRubyのようなものではありません。これはC#と.Netの悪用です。

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

それがそれほど悪くなるのは、それ自身の利益のために空想するのがその正しい方法だということです。

  • あなたがVBからこれを呼び出す必要があるとき何が起こりますか?

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

  • その完全に直感的ではない、インテリセンスは、どのようにしてものを渡すのかを理解するのにほとんど役に立ちません。

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

  • どのようにしてそれを維持するかについての手がかりは誰にもないでしょう。

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

私はあなたが完全にそれらの反発の感情を持つことを正当化すると思います。


  • 私は言うでしょう - それは完全に直感的です。 :) - Arnis Lapsa
  • あなたはそれがRubyのようではないと言います。しかし、ハッシュテーブルのキーと値を指定するためのRubyの構文に非常によく似ています。 - Charlie Flowers
  • アルファ変換の下で壊れるコード!そうだね。 - Phil
  • @Charlie、構文的には似ていますが、意味的には違います。 - Sam Saffron

40

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


  • アーメン、兄弟。アーメン(2番目のアーメンは、コメントを記入するのに最低限の長さを必要とします:) - Charlie Flowers
  • あなたのコメントだけでも必要以上のものでした。しかし、それなら、あなたはアーメンだけを一度だけそれからあなたのコメントに入れることができます:D - Sleeper Smith

37

二人とも。ラムダ式の乱用ですそして構文の輝き。


  • それで、それはラムダ式の素晴らしい構文上の悪用ですか?私は同意すると思います:) - Seth Petry-Johnson

21

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

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

短所

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

長所

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

ボトムライン - 公のAPI設計では、もっと明確な方法を選んだほうがいいでしょう。


  • @Elisha - あなたの長所と短所は逆になります。少なくとも、Proがコードを「直感的ではない」と言っているのではないことを祈ってください。 ;-) - Metro Smurf
  • この特定のケースでは - ラムダパラメータ名と文字列パラメータはどちらも壊れやすいです。これは、xmlの解析にdynamicを使用するようなものです - とにかくxmlについては確実ではないため、これは適切です。 - Arnis Lapsa

18

いいえ、それは確かに一般的なやり方ではありません。直感に反するので、コードを見てそれが何をするのか理解するだけの方法はありません。あなたはそれがどのように使われているかを理解するためにそれがどのように使われているかを知る必要があります。

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

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

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


  • 本当に?私がそれを見たとき、私はまさにそのコードの断片が何を意図しているかを知っていました。あなたが非常に厳格でない限り、それはそれほど曖昧ではありません。文字列連結のための+のオーバーロードについても同じ議論を与えることができます、そして私たちは常に代わりにConcat()メソッドを使うべきです。 - Samantha Branham
  • @Stuart:いいえ、正確にはわかりませんでした。使用した値に基づいて推測していただけです。誰でも推測できますが、推測することはコードを理解するための良い方法ではありません。 - Guffa
  • 使用していると思います.Attribute("style", "width:100%")私にくれstyle="width:100%"しかし、すべての人にとってそれは私に与えることができることを知っていますfoooooo。違いがわかりません。 - Samantha Branham
  • "使用された値に基づいて推測"コードを見たときに必ずすることです。あなたがstream.close()への呼び出しに遭遇したなら、あなたはそれがストリームを閉じると仮定します、それでもなお全く違う何かをするかもしれません。 - Wouter Lievens
  • @Wouter:あなたが常にコードを読むときに推測しているのなら、あなたはコードを読むのに大変な困難を抱えていなければなりません。私はクラスの作者が命名規則について知らないことを集めることができます、それで私はメソッドが何をするかについて当然のこととしてまったく何かを取るのを非常に躊躇します。 - Guffa

17

次の何が問題なのです。

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


  • 衛生上の質問に対して+1! - Askolein

17

"恐ろしさ"についてのこのすべての憤慨は、長い間C#の人たちが過度に反応しすぎていることです(そして私は長い間C#プログラマーであり、それでもこの言語の大ファンです)。この構文について恐ろしいことは何もありません。これは単に、構文を表現しようとしているもののように見せるための試みです。構文の「ノイズ」が少ないほど、プログラマは理解しやすくなります。 1行のコードでノイズを減らすのは少しだけ効果がありますが、ますます多くのコードでそれを積み重ねることを可能にし、それは大きな利益であることがわかります。

これは作者がDSLと同じ利点を得ようとする試みです - あなたが言おうとしているものがコードのように見えるようになれば、魔法の場所にたどり着きます。これが相互運用に適しているのか、それとも「複雑さ」のコストを正当化するのに匿名の方法よりも十分に優れているのかを議論することができます。あなたのプロジェクトでは、この種の構文を使うかどうかの正しい選択をするべきです。しかしそれでも...これはプログラマーによる賢い試みであり、結局のところ、私たち全員がやろうとしていること(実現しているかどうかにかかわらず)です。そして、私たち全員がやろうとしているのは、「コンピュータにしたいことをできるだけ近い言語で、コンピュータに伝えたい」ということです。

社内で考えているのと同じ方法で私たちの指示をコンピュータに表現することに近づくことは、ソフトウェアをより保守しやすく、より正確にするための鍵です。

編集:私は「ソフトウェアをより保守しやすく、より正確にするための鍵」と言っていました。私はそれを「鍵」に変えました。


16

これを使ってフレーズをコインできますか?

magic lambda(n):マジックストリングを置き換える目的でのみ使用されるラムダ関数。


  • ええ...それは面白いです。そして、コンパイル時の安全性がないという意味で、魔法のようなちょっとしたことが、コンパイル時のエラーではなくランタイムを引き起こす場所があるのでしょうか。 - Maslow

12

これは式ツリーの利点の1つです - コード自体で追加情報を調べることができます。こうやって.Where(e => e.Name == "Jamie")同等のSQL Where節に変換できます。私はそれがこれ以上先に行かないことを願っていますが、これは式ツリーの賢い使い方です。もっと複雑なものは置き換えたいと思っているコードよりも難しいと思われるので、私はそれが自己制限的になると思う。


  • 有効なポイントですが、広告の真実:LINQにはTableAttributeやColumnAttributeのような一連の属性が含まれているため、これがより合法的なものになります。また、linqマッピングはクラス名とプロパティ名を調べます。これらはパラメータ名よりも間違いなく安定しています。 - Remus Rusanu
  • 私はあなたに賛成です。 Eric Lippert / Anders Helsberg / etcがこの問題について語ったことを読んだ後で、私の考えを少し変えました。この回答はまだ役に立ちますので、お任せください。その価値のために、私は今、HTMLを扱うこのスタイルがいいと思いますが、それは言語に合いません。 - Jamie Penney

7

それは面白いアプローチです。式の右辺を定数のみに制限した場合は、次のように実装できます。

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;
}

これは、スレッドの前半で言及されているクロスランゲージ相互運用の問題にも対処する可能性があります。


6

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

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

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

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

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

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

あるいは:

.Attributes.Style.Width.Percent = 100;

それはコードの作者にとってより多くの仕事ですが、このアプローチはC#の強い型チェック能力を利用します。それはバグが最初からコードに入り込むのを防ぐのを助けます。


  • 私はコンパイル時のチェックを評価しますが、これは意見の問題に帰着すると思います。多分new Attributes(){Style:" width:100%"のようなものです。これは、より簡潔なので、これにより、より多くの人に勝つでしょう。それでも、HTMLで許可されているものすべてを実装するのは大変な作業であり、文字列/ラムダ/匿名クラスを使用しただけではだれかを責めることはできません。 - Samantha Branham

5

少なくともRuby =のように思えますが、少なくとも私にとっては後の動的な "lookup"のための静的リソースの使用はAPI設計の考慮には適さないので、このAPIではこの巧妙なトリックはオプションであることを願います。

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

お役に立てれば


5

私見、それはそれを行うためのクールな方法です。私たちは皆、クラスControllerに名前を付けることでMVCのコントローラになるという事実を気に入っています。そのため、命名が重要な場合があります。

ここでもその意図は明らかです。それを理解するのはとても簡単です.Attribute( book => "something")になりますbook="something"そして.Attribute( log => "something")になりますlog="something"

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


  • クラスControllerに名前を付けると、コントローラから継承しない場合でも、スクワットを実行できませんでした。 - Jordan Wallwork

4

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

構文の輝きに関しては私が見つけるstyle=>"width:100%"わかりにくいです。特に=>の代わりに=


3

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


1

私はこれが「魔法の弦」以上のものではないと思います。私はこれのためにも匿名型のファンではありません。より良い& Aが必要です。強く型付けされたアプローチ

リンクされた質問


関連する質問

最近の質問