it-swarm-vi.com

Việc sử dụng LINQ và Lambda Expressions có dẫn đến mã ít đọc hơn không?

Tôi đang thảo luận với đồng nghiệp trên Linq, tôi sẽ sao chép ở đây:

Đồng nghiệp: Hãy trung thực ở đây. Cú pháp Linq hút. Nó khó hiểu và không trực quan.

Tôi: oh nào, khó hiểu hơn T-SQL?

Đồng nghiệp: uh, vâng.

Tôi: nó có các phần cơ bản giống nhau, chọn, ở đâu và từ

Đồng nghiệp: Linq, với tôi, là một sự khốn của quan hệ + OO. Đồng nghiệp: Đừng hiểu sai ý tôi - nó cực kỳ mạnh mẽ, nhưng họ đã tái sử dụng SQL để sử dụng lại các bộ sưu tập đối tượng.

Tôi cho rằng việc sử dụng Linq + Lamda rất mạnh mẽ (anh ấy đồng ý) và cũng làm cho mã dễ đọc hơn (anh ấy không đồng ý ở điểm đó):

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

hoặc là

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

hoặc (mã VB tại đây)

   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

Đối với tôi điều này là sạch sẽ và dễ dàng (ít nhất là dễ dàng hơn so với các lựa chọn thay thế) để đọc, ý kiến ​​của bạn về nó là gì?

43
BlackICE

Tôi không thể tìm thấy bài viết phù hợp nữa, nhưng Eric Lippert (và có thể một số loại kẹo mềm khác) đã đôi khi nói về cách Linq là khai báo, trong đó, đối với một số loại vấn đề, trực quan hơn nhiều than mệnh lệnh cú pháp.

Linq cho phép bạn viết mã thể hiện ý định , chứ không phải cơ chế .

Bạn cho tôi biết cái nào dễ đọc hơn. Điều này:

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

Hay cái này?

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

Hay thậm chí là cái này?

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

Ví dụ đầu tiên chỉ là một loạt các nồi hơi vô nghĩa để có được kết quả đơn giản nhất. Bất cứ ai nghĩ rằng nó nhiều hơn dễ đọc hơn các phiên bản Linq cần phải được kiểm tra đầu. Không chỉ vậy, nhưng cái đầu tiên lãng phí bộ nhớ. Bạn thậm chí không thể viết nó bằng cách sử dụng yield return vì sắp xếp.

Đồng nghiệp của bạn có thể nói những gì anh ấy muốn; cá nhân, tôi nghĩ Linq đã cải thiện khả năng đọc mã của tôi vô cùng lớn.

Không có gì "quan hệ" về Linq cả. Nó có thể có một số điểm tương đồng bề ngoài với SQL nhưng nó không cố gắng ở bất kỳ hình dạng hoặc hình thức nào để thực hiện phép tính quan hệ. Nó chỉ là một loạt các phần mở rộng giúp cho việc truy vấn và trình tự dự án dễ dàng hơn. "Truy vấn" không có nghĩa là "quan hệ" và trên thực tế có một số cơ sở dữ liệu không liên quan sử dụng cú pháp giống như SQL. Linq là hoàn toàn hướng đối tượng, nó chỉ hoạt động với cơ sở dữ liệu quan hệ thông qua các khung như Linq sang SQL vì một số voodoo của cây biểu thức và thiết kế thông minh từ nhóm C #, làm cho các hàm ẩn danh hoàn toàn có thể chuyển đổi thành cây biểu thức.

74
Aaronaught

Đồng nghiệp: Hãy trung thực ở đây. Cú pháp Linq hút. Nó khó hiểu và không trực quan.

Bạn không thể tranh luận với những lời chỉ trích đó. Đối với đồng nghiệp của bạn, nó rất tệ . Chúng tôi đã thất bại trong việc thiết kế một cú pháp mà đối với họ là rõ ràng và trực quan. Đó là thất bại của chúng tôi và bạn có thể chuyển lời xin lỗi của tôi đến đồng nghiệp của bạn. Tôi rất vui khi được gợi ý về cách làm cho nó tốt hơn; Điều gì đặc biệt làm đồng nghiệp của bạn thấy khó hiểu hoặc không trực quan?

Tuy nhiên, bạn không thể làm hài lòng tất cả mọi người. Ý kiến ​​cá nhân của tôi và ý kiến ​​của hầu hết những người tôi đã nói về chủ đề này là cú pháp hiểu truy vấn rõ ràng hơn nhiều so với cú pháp mệnh lệnh tương đương. Rõ ràng không phải ai cũng đồng ý, nhưng may mắn thay, chúng tôi không yêu cầu sự đồng thuận của tất cả hàng triệu khách hàng khi chúng tôi thiết kế ngôn ngữ.

Mặc dù về chủ đề "trực quan", tôi nhớ lại câu chuyện về nhà ngôn ngữ học người Anh đã nghiên cứu nhiều ngôn ngữ khác nhau và cuối cùng kết luận rằng tiếng Anh là ngôn ngữ tốt nhất trong tất cả các ngôn ngữ vì trong tiếng Anh, các từ đến theo thứ tự mà bạn nghĩ chúng. Không giống như tiếng Pháp, nơi họ liên tục nói những câu như "con chó trắng ăn thịt đỏ". Người Pháp phải khó khăn thế nào nghĩ các từ trong đúng đặt hàng và sau đó phải nói chúng theo thứ tự tiếng Pháp ! Tiếng Pháp thật không trực quan! Thật đáng kinh ngạc khi người Pháp quản lý để nói nó. Và tiếng Đức? họ nghĩ "con chó ăn thịt" nhưng rồi phải nói "con chó ăn thịt"!?! Thật không trực quan.

Thông thường những gì "trực quan" chỉ là vấn đề quen thuộc. Tôi đã mất tháng để làm việc với LINQ trước khi tôi ngừng bắt đầu các truy vấn của mình bằng mệnh đề "select". Bây giờ nó là bản chất thứ hai và thứ tự SQL có vẻ kỳ quái.

Mà đó là! Các quy tắc phạm vi đều bị rối tung trong SQL. Một cái gì đó bạn có thể muốn chỉ ra cho đồng nghiệp của mình là LINQ đã được thiết kế cẩn thận để (1) giới thiệu các biến và phạm vi xảy ra từ trái sang phải (*) và (2) thứ tự truy vấn xuất hiện trên trang là thứ tự mà nó được thực thi. Đó là khi bạn nói

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

c xuất hiện trong phạm vi ở bên trái và ở trong phạm vi qua bên phải. Và thứ tự xảy ra là: "khách hàng" đầu tiên được đánh giá. Sau đó, "nơi" được ước tính để lọc chuỗi. Sau đó, chuỗi được lọc được chiếu bởi "chọn".

SQL không có thuộc tính này. Nếu bạn nói

SELECT Name FROM Customers WHERE City = 'London'

sau đó "Tên" được đưa vào phạm vi bởi một cái gì đó ở bên phải của nó, không phải bên trái của nó và truy vấn được thực hiện theo thứ tự hoàn toàn sai lầm; mệnh đề giữa được đánh giá đầu tiên, sau đó là mệnh đề cuối cùng và sau đó là mệnh đề đầu tiên. Điều đó bây giờ có vẻ điên rồ và không trực quan với tôi, khi chỉ làm việc với LINQ từ lâu.


(*) Quy tắc phạm vi có một chút kỳ lạ trong LINQ với các mệnh đề tham gia. Nhưng khác hơn, phạm vi tổ độc đáo.

69
Eric Lippert

Giống như bất cứ điều gì khác trong thế giới lập trình, bạn phải làm quen với cú pháp, và sau đó nó (có khả năng) dễ đọc hơn.

Giống như bất cứ điều gì khác trong thế giới lập trình, có tiềm năng cho mã spaghetti hoặc lạm dụng khác.

Giống như bất cứ điều gì khác trong thế giới lập trình, bạn có thể làm theo cách này hoặc cách khác.

Giống như bất cứ điều gì khác trong thế giới lập trình, số dặm của bạn có thể thay đổi.

22
Wonko the Sane

Tôi đã thấy một bình luận/nhận xét trong đó nó đã nêu điều gì đó - liên quan đến LINQ/lambda - dọc theo dòng: "Viết mã có thể đọc được cho con người, thay vì có thể đọc được trên máy tính của bạn".

Tôi nghĩ rằng tuyên bố này có rất nhiều giá trị, tuy nhiên, hãy xem xét nhà phát triển (chẳng hạn như tôi), người đã trải qua toàn bộ các ngôn ngữ phát triển từ hội, thông qua thủ tục, thông qua OO, thông qua quản lý, thông qua các giải pháp song song nhiệm vụ thông lượng cao .

Tôi đã tự hào về việc làm cho mã của mình dễ đọc và có thể tái sử dụng nhất có thể và áp dụng nhiều nguyên tắc mẫu thiết kế GOF để cung cấp các hệ thống và dịch vụ chất lượng sản xuất trên nhiều lĩnh vực kinh doanh khác nhau.

Lần đầu tiên tôi bắt gặp biểu cảm lambda tôi đã nghĩ: "Cái quái gì thế!? Nó ngay lập tức phản trực giác với cú pháp khai báo rõ ràng quen thuộc (và do đó thoải mái). Người trẻ hơn <5yrs trong công việc tuy nhiên mọi người đã đưa nó lên như thể đó là manna từ thiên đường!

Đó là bởi vì trong nhiều năm suy nghĩ như một máy tính (theo nghĩa cú pháp) đã dịch rất dễ dàng thành cú pháp mã hóa trực tiếp (không phân biệt ngôn ngữ). Khi bạn có tư duy tính toán cho khoảng 20+ năm (30+ trong trường hợp của tôi), bạn phải đánh giá cao cú pháp ban đầu shock của biểu thức lambda có thể dễ dàng chuyển thành sợ hãi và ngờ vực.

Có lẽ đồng nghiệp trong OP đã đến từ một nền tảng tương tự như tôi (tức là đã ở quanh khối một vài lần) và nó phản tác dụng với họ vào thời điểm đó? Câu hỏi của tôi là: bạn đã làm gì về nó? Bạn đã cố gắng giáo dục lại đồng nghiệp của mình để hiểu được lợi ích của cú pháp nội tuyến hay bạn đã cố chấp/tẩy chay họ vì đã không "đồng hành cùng chương trình"? Người trước có thể đã thấy đồng nghiệp của bạn đi theo lối suy nghĩ của bạn, người sau có thể sẽ khiến họ không tin vào cú pháp LINQ/lambda hơn nữa và do đó làm trầm trọng thêm ý kiến ​​tiêu cực.

Đối với bản thân tôi, tôi đã phải giáo dục lại cách suy nghĩ của riêng mình (vì Eric đã nói ở trên, đó không phải là một sự thay đổi tâm trí không đáng kể và tôi phải lập trình ở Miranda vào những năm 80 vì vậy tôi đã chia sẻ kinh nghiệm lập trình chức năng của mình) nhưng một khi tôi đã trải qua nỗi đau đó thì lợi ích rất rõ ràng nhưng - quan trọng hơn - trong đó việc sử dụng nó là đã qua sử dụng (nghĩa là được sử dụng vì mục đích sử dụng), quá phức tạp và lặp đi lặp lại (xem xét DRY nguyên tắc trong trường hợp đó).

Là một người không chỉ viết nhiều mã mà còn phải xem xét kỹ về nhiều mã, điều bắt buộc là tôi phải hiểu các nguyên tắc này để tôi có thể xem xét các mục một cách vô tư, khuyên rằng việc sử dụng biểu thức lambda có thể hiệu quả hơn/có thể đọc được và cũng để khiến các nhà phát triển xem xét khả năng đọc của các biểu thức lambda nội tuyến rất phức tạp (trong đó một lệnh gọi phương thức - trong các trường hợp đó - làm cho mã dễ đọc hơn, có thể duy trì và mở rộng hơn).

Vì vậy, khi ai đó nói rằng họ "Đừng lấy lambda?" hoặc cú pháp LINQ, thay vì đặt nhãn hiệu cho họ luddite cố gắng giúp họ hiểu các nguyên tắc cơ bản. Rốt cuộc họ có thể có một nền tảng "trường học cũ" như tôi.

6
CWC

Biểu thức Lambda dẫn đến mã ít đọc hơn nếu truy vấn quá dài. Tuy nhiên, nó tốt hơn nhiều so với quá nhiều các vòng lặp lồng nha.

Sẽ tốt hơn với một hỗn hợp của cả hai.

Viết nó trong Lambda nếu nó nhanh hơn (bạn cần nó nhanh) hoặc dễ đọc hơn.

4
Amir Rezaei

Tôi nghĩ rằng nó phụ thuộc vào hầu hết các trường hợp (ngoại trừ khi làm điều gì đó rất kỳ quái) nếu bạn có nghĩa là "có thể đọc được" khi ai đó có ý tưởng về những gì đang xảy ra hoặc nếu họ có thể dễ dàng tìm thấy tất cả các chi tiết nhỏ.

Tôi nghĩ rằng liên kết giúp với cái trước, nhưng thường (đặc biệt là khi gỡ lỗi) làm tổn thương cái sau.

IMHO khi tôi đang xem mã tôi không quen thuộc với cái trước thì cực kỳ quan trọng hơn cái sau, vì vậy tôi thấy nó dễ đọc hơn nhiều.

1
Bill

Tôi thấy cú pháp LINQ trực quan và dễ đọc, đặc biệt là khi họ đặt TỪ ở đầu nơi nó thuộc về thay vì ở giữa như trong SQL. Nhưng lambdas IMO gây nhầm lẫn và làm cho mã khó đọc hơn.

1
Mason Wheeler

Tôi đồng ý với bạn rằng cú pháp Linq không khác biệt đáng kể so với T-SQL. Tôi nghĩ rằng đồng nghiệp của bạn thực sự có thể phản đối những thứ quan hệ bị lẫn lộn trong đó mã đẹp và sáng bóng của anh ấy OO. Mặt khác, lập trình chức năng cần một chút để làm quen và sẵn sàng làm quen với nó.

1
Larry Coleman

Nó phụ thuộc. Rõ ràng, T-SQL duy nhất cung cấp một số giải pháp quan hệ DB. Rõ ràng LINQ cung cấp một số giải pháp OO.

Tuy nhiên; "khó hiểu hơn T-SQL?" - được thảo luận/hỏi trong câu hỏi ban đầu. Điều này rõ ràng ngụ ý một số tính năng mà không có địa chỉ câu trả lời nào hiện có, thay vào đó cáo buộc nhà phê bình (rõ ràng là SQL quen thuộc) bị mắc kẹt trong quá khứ.

Vì vậy, mặc dù tôi đánh giá cao LINQ về những phẩm chất nhất định và không đồng ý nhiều với các câu trả lời hiện có ở đây, tôi cảm thấy đối trọng xứng đáng đại diện:

Nhiều năm sau khi làm quen với LINQ, việc thực hiện một số loại hoạt động nhóm nhất định, tham gia ngoài và không đẳng thức, làm việc với các khóa tổng hợp và các hoạt động khác trong LINQ vẫn khiến tôi chùn bước. (Đặc biệt là khi nhắm mục tiêu một back-end quan hệ với các câu hỏi nhạy cảm về hiệu suất.)

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

Nếu bạn nghĩ rằng đó là trực quan, nhiều sức mạnh hơn cho bạn. ;)

0
shannon