LINQおよびLambda Expressionsを使用すると、コードが読みにくくなりますか?[閉まっている]


43

私はLinqの同僚と議論しています。ここにコピーします:

同僚:ここで正直に話しましょう。Linq構文はダメです。わかりにくく直感的ではありません。

私:ああ、T-SQLよりも混乱しますか?

同僚:ええ、はい。

私:それは同じ基本的なパーツ、select、where、fromを持っています

同僚:私にとって、Linqはリレーショナル+ OOのろくでなしです。同僚:誤解しないでください。信じられないほど強力ですが、SQLを再利用してオブジェクトコレクションを使用しました。

私は、Linq + Lamdaを使用することは非常に強力であり(彼は同意します)、またコードを読みやすくします(彼はその点で同意しません)と思います。

pickFiles = from f in pickFolder.GetFiles("*.txt")
where ValidAuditFileName.IsMatch(f.Name)
select f;

または

var existing = from s in ActiveRecordLinq.AsQueryable<ScannedEntity>()
where s.FileName == f.FullName && s.DocumentType != "Unknown"
select s;

または(VBコードはこちら)

   Dim notVerified = From image In images.AsParallel
     Group Join verifyFile In verifyFolder.GetFiles("*.vfy").AsParallel.Where(
      Function(v) v.Length > 0
      ).AsParallel
   On image.Name.Replace(image.Extension, ".vfy") Equals verifyFile.Name
     Into verifyList = Group
    From verify In verifyList.DefaultIfEmpty
    Where verify Is Nothing
    Select verify

私にとって、これはきれいで読みやすいです(少なくとも他の選択肢よりも簡単です)、あなたの意見は何ですか?


11
一般に、人間は変化を嫌います。多くの人がそれを嫌い、実際に恐れています。
トニー

9
それに直面しましょう... linqは、C#およびVB.Netに追加された、機能が低下した機能的なプログラミング構文です。linqとlambda式のバッシングは基本的に「FP sucks」と言っています。それはあなたの同僚が言っていることです。議論は他の場所でハッシュ化されたと思います。
スコットホイットロック

48
人々が本当に「馴染みがある」という意味で「直感的」という言葉を使用する傾向があるのは、他の誰かを悩ませますか?
ラリーコールマン

7
とにかく、C#チーム(Eric Lippertを含む)は、LinqはSQLの移植ではなく、他のほとんどの機能と同様にゼロから設計されていることを説明するために脱出しました。私はあなたの同僚がルッダイトであると言わなければならないでしょう。
アーロンノート

16
価値があることについて:私はちょうど妻(オフィス管理者-実際のプログラミング経験がゼロに近い)にaaronaughtの3つの例を見てもらい、linqとlambdaの例の意図を従来の命令型の例よりもはるかに簡単に解読することができました。
スティーブンエバーズ

回答:


73

私はもう正しい投稿を見つけることができませんが、Eric Lippert(およびおそらく他のいくつかのソフト)は、Linqが宣言的である方法について何度か意見を述べています。これは、いくつかのクラスの問題では、命令型構文よりもはるかに直感的です。

Linqを使用すると、メカニズムではなく意図を表すコードを作成できます。

どちらが読みやすいか教えてください。この:

IEnumerable<Customer> GetVipCustomers(IEnumerable<Customer> source)
{
    List<Customer> results = new List<Customer>();
    foreach (Customer c in source)
    {
        if (c.FirstName == "Aaron")
        {
            results.Add(c);
        }
    }
    results.Sort(new LastNameComparer());
    return results;
}

class LastNameComparer : IComparer<Customer>
{
    public int Compare(Customer a, Customer b)
    {
        return x.LastName.CompareTo(b.LastName);
    }
}

それともこれ?

IEnumerable<Customer> GetVipCustomers(IEnumerable<Customer> source)
{
    return from c in source
           where c.FirstName == "Aaron"
           orderby c.LastName
           select c;
}

それともこれ?

IEnumerable<Customer> GetVipCustomers(IEnumerable<Customer> source)
{
    return source.Where(c => c.FirstName == "Aaron").OrderBy(c => c.LastName);
}

最初の例は、最も単純な結果を得るための無意味なボイラープレートの束です。それがあると思う誰より LINQのバージョンよりも読みやすいが、彼の頭を調べている必要があります。それだけでなく、最初のものはメモリを浪費します。yield returnソートのために、それを使って書くことさえできません。

あなたの同僚は彼が望んでいることを言うことができます。個人的に、私は、LINQのが向上したと思います私の計り知れないほどのコードの読みやすさを。

Linqについても「関係」はありません。SQLに表面的な類似点がいくつかあるかもしれませんが、どのような形や形式でもリレーショナル計算を実装しようとはしていません。これは、シーケンスのクエリと投影を簡単にする一連の拡張機能です。「クエリ」は「リレーショナル」を意味するものではなく、実際にはSQLに似た構文を使用する非リレーショナルデータベースがいくつかあります。Linqは純粋にオブジェクト指向であり、C#チームの式ツリーのブードゥーと巧妙な設計により、Linq to SQLなどのフレームワークを介してリレーショナルデータベースと連携し、匿名関数を暗黙的に式ツリーに変換できるようにします。


6
+1 LINQが気に入らない場合は、おそらく「正しいことをしていない」からです:)
Darknight

1
Linq is purely object-oriented+1。これが、チームスタイルガイドがクエリ構文よりも流fluentな構文の使用を強制する理由でもあります。リンクのオブジェクト指向の性質が、Cに似た言語のオブジェクト表記に慣れている人にとって、より明白になることがわかりました。
SBI

1
私はこの投稿が7歳であることを知っています。しかし、質問は次のとおりです。Linqに会う前に、主なツールとして関数型言語を使用しましたか?
Sergey.quixoticaxis.Ivanov 16

4
率直に言って、私はfoorループを好みます。それは、NULL参照の例外、NULLの処理方法、およびNULLの処理方法をすぐに確認できるためです。同様に、forループもより効率的です。
苦境

1
@Aaronaught、ソートを削除し、ホルストマンスタイルで手続き型コードを再フォーマットすると、LINQバージョンよりも読みやすくなります。そして、単一責任の原則によれば、ソートは実際にはに属していません。GetVipCustomers()その名前が示すように、VIP顧客のコレクションを任意の順序でのみ返すものです。以下のためにな画面への出力として、順序が重要な場合、呼び出し側がコレクションを並べ替えてみましょう。
Ant_222

69

同僚:ここで正直に話しましょう。Linq構文はダメです。わかりにくく直感的ではありません。

その批判に反論することはできません。あなたの同僚にとって、それは最悪です。彼らにとって、明確で直感的な構文の設計に失敗しました。それは私たちの失敗であり、あなたはあなたの同僚に私の謝罪を伝えることができます。それを改善する方法についての提案を喜んで受けます。あなたの同僚は、特に混乱したり直感的でないと感じたりしますか?

しかし、皆さんを満足させることはできません。私の個人的な意見、および私がこのテーマについて話したほとんどの人々の意見は、クエリ理解構文は同等の命令構文よりもはるかに明確だということです。誰もが同意するわけではないことは明らかですが、幸いなことに、言語設計を行う際に何百万人ものお客様のコンセンサスを必要としません。

しかし、「直感的」である何をテーマに、私は多くの異なる言語を学び、最終的にので、英語がすべての言語の最高だったと結論付けた英語の言語学者の話を思い出しています英語で、言葉が来て、あなたと同じ順序でそれらを考えるフランス語とは異なり、「犬の白は肉を赤く食べる」というようなことを常に言っています。フランス人が言葉を正しい順序で考え、それをフランス語の順番で言わなければならないのはどれほど難しいことでしょう!フランス語はとても直感的ではありません!フランス人がなんとか話せるのは驚くべきことです。そしてドイツ人?彼らは「犬は肉を食べる」と思うが、「肉が食べる犬」と言わなければならない!

多くの場合、「直感的」なものは単に親しみやすさの問題です。"select"句でクエリを開始するのをやめるまでに、LINQで作業するのに数か月かかりました。今ではそれは第二の性質であり、SQLの順序は奇妙に思えます。

どっちだ!スコープルールはすべてSQLで台無しにされます。同僚に指摘したいことは、LINQが(1)変数とスコープの導入が左から右に行われるように(*)、(2)ページにクエリが表示される順序が慎重に設計されていることです実行される順序。つまり、あなたが言うとき

from c in customers where c.City == "London" select c.Name

cはスコープの左側に表示され、右側からスコープ内に留まります。そして、物事が起こる順序は次のとおりです。最初の「顧客」が評価されます。次に、「where」が評価されて、シーケンスがフィルタリングされます。次に、フィルタリングされたシーケンスが「選択」によって投影されます。

SQLにはこのプロパティはありません。あなたが言うなら

SELECT Name FROM Customers WHERE City = 'London'

次に、「名前」は、左側ではなく右側の何かによってスコープに取り込まれ、クエリは完全に混乱した順序で実行されます。中央の節が最初に評価され、次に最後の節、次に最初の節が評価されます。それは今では私にとってクレイジーで直感的ではないように見えます。


(*)LINQでの結合句のスコープ規則は少し奇妙です。しかし、それ以外は、スコープはうまくネストされます。


5
Nitpick:ドイツは、実際に英語と同じ順序である「デアフントははFleischは、DASをfrisst」、「犬の食べる肉は」:)それはさておき、私は、LINQクエリは非常に読みやすい構文た私は、それはSQLではないことに気づいた後
マイケルスタム

18
@gbjbaanb:LINQ構文がより理解しやすいという完全に主観的な根拠で正当化しているわけではありません。むしろ、LINQ構文はツールを念頭に置いて設計されているため、ユーザーがLINQクエリの構築を支援するツールを設計する方がはるかに簡単であり、クエリ内でイベントが発生する順序。これらはイベントが字句的に同じ順序で来るためです。
エリックリッパー

2
エリック、宣言的なスタイルのプログラミングの観点から、私はLinqがSQLよりも適切であると理解しています(読みやすいと言います)。しかし、SQLよりも人間が読みやすいのでしょうか?ありえない。「犬が肉を赤く食べる」のが難しい場合、「色が赤のキッチンからナイフを得る」のはどれくらい簡単ですか?実際、私たちは皆、「キッチンからテーブルの上のナイフを手に入れる」ように考えています。そして、SQLがLINQよりも現実に近いところです。
-nawfal

6
店が深夜まで営業しているスターバックスのコーヒーを飲みたい。それはあなたによく聞こえますか?SQLクエリとまったく同じ順序です。はい、LINQは標準のSQLクエリを実行するために生成する必要のある不要なコードよりも読みやすい場合がありますが、LINQはSQLクエリ自体よりも読みやすくありません。あ、はい; LINQ開発者が左から右にスコープを設定することは有利かもしれませんが、ユーザーとして、なぜ気にするのですか?私は使いやすさだけに関心があります。
カイルム

8
@KyleM:もちろんです。使いやすさは、LINQ設計の非常に重要な側面でした。特に、生産性ツールを受け入れることが重要でした。スコープは左からLINQクエリに書き込むためc.、IDE を入力するまでに、IDEのタイプはすでにわかっており、cIntelliSenseを提供できます。LINQでは、「cの顧客のcから」と言います。そして、ブームの場合、IntelliSenseがのメンバーをリストすることであなたを助けてくれますCustomer。SQLで「SELECT name FROM customers」と入力name すると、前に 入力したので「name」が適切な選択であることを示すIDEヘルプを取得できませんcustomers
エリックリッパー

22

プログラミングの世界の他の何かと同様に、構文に慣れる必要があり、それから(潜在的に)読みやすくなります。

プログラミングの世界の他のすべてと同様に、スパゲッティコードやその他の悪用の可能性があります。

プログラミングの世界の他のすべてと同様に、この方法でも別の方法でも実行できます。

プログラミングの世界で他のものと同様に、走行距離は異なる場合があります。


6

「LINQ / lambdaに関連して」「コンピュータではなく人間が読めるコードを書く」という行に沿って何かを述べたコメント/コメントを見ました。

このステートメントには多くのメリットがあると思いますが、アセンブリ、手続き、オブジェクト指向、マネージド、高スループットタスクの並列ソリューションを活用することにより、アセンブリから開発言語の全範囲を経験した開発者(私のような)を検討してください。

私は、コードをできる限り読みやすく再利用可能にし、多くの異なるビジネスセクターに生産品質システムとサービスを提供するために、GOFデザインパターンの原則の多くを採用することに誇りを持っています。

初めてラムダ式に出会ったとき、「それは一体何だ!?!」それはすぐに私の馴染みのある(したがって快適な)明示的な宣言構文に直観に反していました。職人の5歳未満の若者は、しかし、それは天国からのマナのようにそれをラップしました!

何年もの間、コンピュータのように(構文的な意味で)考えることは(言語に関係なく)直接コーディング構文に非常に簡単に変換されるためです。およそ20年以上(私の場合は30年以上)の計算マインドセットを持っている場合、ラムダ式の最初の構文ショックは恐怖と不信に簡単に変換できることを理解する必要があります。

おそらく、OPの同僚は私と同じようなバックグラウンドから来ていた(つまり、数回ブロックを回っていた)ので、そのとき彼らには直観に反していましたか?私の質問は:あなたはそれについて何をしましたか?インラインシンタックスの利点を理解するようにピアを再教育しようとしましたか、それとも「プログラムに参加していない」ためにそれらを略奪/追放しましたか?前者はおそらくあなたの同僚があなたの考え方に近づき、後者はおそらく彼らがLINQ / lambda構文をさらに信用せず、したがって否定的な意見を悪化させるでしょう。

私自身のために、私自身の考え方を再教育しなければなりませんでした(エリックが上記のように、それは取るに足りないマインドシフトではなく、80年代にミランダでプログラムしなければならなかったので、関数型プログラミングの経験がありました)しかし、一度その痛みを経験すると、利点は明白でしたが、より重要なことには、その使用法が使い古されている(つまり、使用目的で使用されている)場合、複雑で繰り返し使用されている場合(その場合のDRY原則を考慮して)

まだ多くのコードを書くだけでなく、技術的に多くのコードをレビューする必要がある人として、アイテムを公平にレビューできるようにこれらの原則を理解することが不可欠であり、ラムダ式の使用がより効率的である可能性がある場所をアドバイスします/また、開発者が非常に複雑なインラインラムダ式の読みやすさを考慮できるようにします(その場合、メソッド呼び出しによりコードがより読みやすく、保守しやすく、拡張可能になります)。

誰かが「ラムダを取得しないでください」と言うとき。むしろブランドそれらよりか、LINQの構文、ラッダイト運動彼らは基本的な原則を理解してみてください。彼らは結局私自身のような「古い学校」のバックグラウンドを持っているかもしれません。


興味深いコメント-強く型付けされた静的言語から動的言語に切り替えたときにこれがありました。しばらく調整(恐怖と不信感)をしましたが、今では正直に戻るのが難しいと感じています。
lunchmeat317 14

4

クエリが長すぎる場合、Lambda Expressionsはコードを読みにくくします。ただし、ネストされたループが多すぎるよりはましです。

2つの混合物を使用した方が良いでしょう。

速い(高速である必要がある)または読みやすい場合は、Lambdaで記述します。


はい。しかし、linq / lamda が読みにくくなる理由何ですか?
BlackICE

申し訳ありませんが、代替手段よりも読みやすくはありませんでした。
BlackICE

1

ほとんどの場合(非常に奇妙なことをする場合を除く)、何が起こっているのかを誰かが理解できるように「読みやすい」ことを意味するか、または小さな詳細をすべて簡単に見つけることができるかどうかに依存すると思います。

リンクは前者の助けになると思いますが、しばしば(特にデバッグの際に)後者は痛いです。

私が前者に不慣れなコードを見ているとき、私見は後者よりも非常に重要なので、私はそれがはるかに読みやすいと思います。


いい視点ね。後者は通常、適切な単体テストでカバーされます。
スコットホイットロック

それでは、linq評価の内部構造がすべての詳細を見つけることを難しくすると思いますか?これがいつ起こるかの例を提供できますか?
BlackICE

3
@David:Linq式は単に遅延評価されるだけでなく、です。linq式の受信コードは実際に式を変更できるため、s式で動作するlispマクロのように、まったく異なる処理を行うことができます。たとえば、linq to SQLを使用する場合、実際に式where e.FirstName == "John"を受け取り、それをSQLクエリに変換します!コンパイルされていない(しかし解析された)式を見て、それFirstNameが永続化されたエンティティで呼び出されるプロパティの比較であり、文字列などと比較していることを確認します。多くのことが行われています。
スコットホイットロック

@david一般的に、それ自体に対してlinqを使い始めるまで、詳細を見つけるのは難しいとは思いません。私の頭を包むのに長い時間がかかったこの「単純な」例は次のとおりですbugsquash.blogspot.com/2008/07/y-combinator-and-linq.htmlしかし、いくつかのより多くの必然的な例があります動的、動的オブジェクト、およびIDynamicMetaObjectProvider領域の式を使用して人々が行っていることのすべて。linqが何について線を引くかは議論の余地がありますが、scottが指摘しているように、linqは同じ式ツリーを使用しているため、linqですべてのものを利用できます。
ビル

@ビル大丈夫、私はそれは難しいことをお伝えしますが、yコンビネータと高階関数はほとんどの頭を傷つけるでしょう、それは再帰のようなものです、あなたはそれを手に入れますか、しません。再帰的な実装は読みやすくなっていますか(どちらも知らなかった場合)。
BlackICE

1

LINQ構文は直感的で読みやすいと思います。特に、SQLのように真ん中ではなく所属する場所にFROMを配置するためです。しかし、IMOラムダは混乱を招き、コードを読みにくくします。


なぜラムダは混乱していると思いますか?型推論ですか?
ChaosPandion

1
@ChaosPandion:最初に型推論、2番目に記号。=と>を一緒に置くと、誰かが台無しにして後ろ向きに書いた> =のように見えます。
メイソンウィーラー

@Mason-Haskell(\x -> x * x)またはF#(fun x -> x * x)の構文が好きですか?
ChaosPandion

@ChaosPandion:いいえ、特にそうではありません。ラムダは書くのが速いかもしれませんが、特に複雑さを自明でない場合、解析するのは難しいと思います。あなたの例はそれほど悪くはありませんが、もっと悪くなる傾向があります。
メイソンウィーラー

6
@Mason:そもそもラムダの考え方に反対するのではなく、ラムダが虐待されることに反対しているように聞こえます。
アノン。

1

Linq構文はT-SQLとそれほど違わないことに同意します。あなたの同僚は、彼の素晴らしくて光沢のあるオブジェクト指向コードが混在している関係のものに本当に反対しているのではないかと思います。一方、関数型プログラミングには少し慣れる必要があり、それに慣れる意欲があります。


0

場合によります。明らかに、T-SQLはいくつかのDBリレーショナルソリューションを独自に提供します。明らかに、LINQはいくつかのオブジェクト指向ソリューションを独自に提供します。

しかしながら; 「T-SQLよりも混乱しますか?」-最初の質問で議論/質問されています。これは、既存の回答のどれも対処していない機能を明確に暗示しており、代わりに(明らかにSQLに精通した)批判者が過去に行き詰まっていると非難するものです。

そのため、特定の品質についてはLINQに感謝しますが、ここでの既存の回答に大きく異議を唱えることはありませんが、カウンターポイントは表現に値すると思います。

LINQに慣れてから数年後、特定の種類のグループ操作、外部結合および非等価結合の実行、複合キーの操作、およびLINQでの他の操作を行うと、まだ気が散ります。(特に、パフォーマンスに敏感な質問を持つリレーショナルバックエンドを対象とする場合)。

from c in categories
join p in products on c equals p.Category into ps
from p in ps.DefaultIfEmpty()

それが直感的だと思うなら、あなたにもっと力を。;)


-4

これらの教科書の例の代わりに、Linqが吸う理由はおそらく、実世界の例を作ってみてください。

この関数をlinqlamdathingsで表示してみてください。古典的な方法は読みやすいままですが、すべての美しさがなくなっていることがわかります。遅延実行の問題とブレークポイントの設定は言うまでもありません。

真実は、LinqLambdaThingsがいくつかの場合に非常に有用であり、あなたが理解していない気の利いたオブジェクトの向きをすべて置き換えるとは考えられていないことです

    public IList<Customer> GetVipCustomers(IList<Customer> source, int maxCount)
    {
        var results = new SortedList<string,Customer>();

        foreach (Customer c in source)
        {
            if (maxCount == results.Count)
                break;

            if (c.IsVip && c.FirstName== "Aaron" || c.SecondName== "Aaron")
                results.Add(c.LastName, c);
        }

        return results.Values;
    }

6
私はそれsource.Where(c => c.IsVip && c.FirstName == "Aaron" || c.SecondName == "Aaron").Take(maxCount).OrderBy(d => d.LastName);があなたのものを読むのが難しいと思うので、私は間違いを犯したかもしれません;)
jk。

1
これらを教科書の例としてどのように考えましたか?それは実際に本番使用コードです。誰もがそれを変換することを期待するのではなく、比較のために古典的な「読み取り可能な」コードとlinq / lamdaバージョンの両方を提供した場合に役立ちます。
BlackICE

3
LINQについて苦情を申し立てるために特別に登録しましたか?
KChaloux

1
@KChaloux私は、彼らはただ正直に言うとトローリングたと思う
JK。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.