web-dev-qa-db-ja.com

LINQ式とラムダ式を使用すると、コードが読みにくくなりますか?

Linqで同僚と話し合っています。ここにコピーします。

同僚:ここでは正直にしましょう。 Linqの構文はひどい。混乱しやすく、直感的ではありません。

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

同僚:ええ、はい。

私:それは同じ基本的な部分、選択、場所、から

同僚: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

私にとってこれはクリーンで読みやすい(少なくとも他の方法よりも簡単)ですが、それに対するあなたの意見は何ですか?

43
BlackICE

適切な投稿はもう見つかりませんが、Eric Lippert(およびおそらく他のいくつかのソフト)が、Linqがどのようにdeclarativeであるかについて何度か意見を述べています。より命令的構文。

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はmyコードの可読性を計り知れないほど改善したと思います。

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

74
Aaronaught

同僚:ここでは正直にしましょう。 Linqの構文はひどい。混乱しやすく、直感的ではありません。

あなたはその批判に反対することはできません。 あなたの同僚にとって、それは最悪です。彼らにとって、明確で直感的な構文を設計することに失敗しました。それは私たちの失敗です、そしてあなたは私の謝罪をあなたの同僚に伝えることができます。私はそれをより良くする方法について提案をうれしく思います。あなたの同僚は具体的に何を混乱させるか、直感的でないと思いますか?

しかし、皆さんを喜ばせることはできません。私の個人的な意見、およびこの件に関して話し合ったほとんどの人々の意見は、クエリ理解構文は同等の命令構文よりもはるかに明確であるということです。明らかに誰もが同意するわけではありませんが、幸いなことに、言語設計を行う際に何百万人ものお客様の同意を得る必要はありません。

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

多くの場合、「直感的」とは単に親しみやすさの問題です。 "select"句でクエリを開始するのをやめる前に、LINQを使用してか月かかりました。これは第二の性質であり、SQLの順序は奇妙に見えます。

どっちだ!スコーピングルールはすべてSQLで混乱しています。同僚に指摘したいと思うかもしれませんが、LINQは(1)変数とスコープの導入が左から右(*)に行われ、(2)クエリがページに表示される順序が実行される順序。つまり、あなたが言うとき

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

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

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

SELECT Name FROM Customers WHERE City = 'London'

次に、「名前」が左側ではなく右側の何かによってスコープに入れられ、クエリは完全に混乱した順序で実行されます。真ん中の節が最初に評価され、次に最後の節、次に最初の節が評価されます。長い間LINQだけで作業していたので、今ではそれはおかしくて直感的ではないようです。


(*)スコープルールは、結合句を使用するLINQでは少し奇妙です。しかし、それ以外はスコープがうまく入れ子になります。

69
Eric Lippert

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

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

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

プログラミングの世界の他のすべてのように、あなたの走行距離は異なる場合があります。

22
Wonko the Sane

LINQ/lambdaに関連して、「コンピューターではなく人間が読めるコードを書く」というコメント/コメントが見られました。

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

私は自分のコードをできるだけ読みやすく再利用できるようにし、多くのGOF設計パターンの原則を採用して、さまざまな異なるビジネスセクターに生産品質のシステムとサービスを提供することに誇りを持っています。

初めてラムダ式に出くわしたとき、「一体何なんだ!?!」これは、私のfamiliar(したがって快適)の明示的な宣言構文に直感的に反するものでした。若い<5年間の仕事の男たちが、天国のマナのようにそれをラップしました!

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

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

私自身、私は自分自身の考え方を再教育する必要がありました(エリックが上記で推測しているように、それは重要な心変わりではありません。80年代にミランダでプログラミングしなければならなかったため、関数型プログラミングの経験を共有しました)しかし、私がその苦痛を経験した後、利点は明白でしたが、より重要なのは、その使用がover used(つまり、それを使用するために使用された) )、複雑で反復的(DRY原則をその例で考えると)。

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

したがって、誰かが「ラムダを取得しないでください」と言うときまたはLINQ構文では、ブランド化するのではなく、luddite基本的な原理を理解できるようにしてください。結局のところ、私のような「古い学校」の背景を持っているかもしれません。

6
CWC

クエリが長すぎる場合、ラムダ式を使用するとコードが読みにくくなります。ただし、多すぎるネストされたループよりもはるかに優れています。

2つの混合物の方が適しています。

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

4
Amir Rezaei

私は、ほとんどの場合(非常に奇妙なことをする場合を除いて)、何が起こっているのかを理解している人として「読み取り可能」を意味するか、または小さな細部すべてを簡単に見つけられるかどうかに依存すると思います。

リンクは前者に役立つと思いますが、(特にデバッグの場合)しばしば後者に害を与えます。

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

1
Bill

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

1
Mason Wheeler

Linqの構文はT-SQLとそれほど変わらないことに同意します。あなたの同僚は、彼の素敵で光沢のあるOOコードが混在するリレーショナルなものに本当に反対しているかもしれません。一方、関数型プログラミングには少し慣れが必要です。それに慣れる。

1
Larry Coleman

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

しかしながら; 「T-SQLよりも混乱しますか?」 -最初の質問で議論/質問されています。これは明らかに、既存の回答のどれも対応していないいくつかの機能を暗示しています。

したがって、私はLINQを一定の品質で評価し、ここでの既存の回答に大いに反対しないが、対置点は表現に値すると感じます。

LINQに慣れてから数年後、特定の種類のグループ操作、外部結合と非等価結合、複合キーの操作、およびLINQでの他の操作を実行すると、依然として不快に感じます。 (特に、パフォーマンスに敏感な質問でリレーショナルバックエンドをターゲットにする場合。)

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

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

0
shannon