2009-03-23 67 views
50

Rất nhiều câu hỏi đang được trả lời trên Stack   Tràn, với các thành viên chỉ định cách giải quyết các vấn đề thời gian thực/thời gian thực này bằng cách sử dụng lambda expressions.Khi không sử dụng biểu thức lambda

Chúng tôi có lạm dụng nó không và chúng tôi có đang xem xét tác động hiệu suất của việc sử dụng biểu thức lambda không?

Tôi tìm thấy một vài điều mà khám phá những ảnh hưởng hiệu suất của lambda vs đại biểu vô danh vs for/foreach vòng với kết quả khác nhau

  1. Anonymous Delegates vs Lambda Expressions vs Function Calls Performance
  2. Performance of foreach vs. List.ForEach
  3. .NET/C# Loop Performance Test (FOR, FOREACH, LINQ, & Lambda).
  4. DataTable.Select is faster than LINQ

gì nên là tiêu chí đánh giá khi lựa chọn giải pháp thích hợp? Ngoại trừ lý do rõ ràng là mã ngắn gọn hơn và dễ đọc hơn khi sử dụng lambda.

+2

Tôi rất khuyên bạn nên truy cập www.lambdaexpression.net – Delashmate

+5

@Delashmate miền được tiếp quản bởi kẻ gửi thư rác hiện nay – ozz

Trả lời

15

Sao chép mã.
Nếu bạn thấy mình viết cùng một chức năng ẩn danh nhiều lần, nó không nên là một.

+0

Nó phụ thuộc vào nơi mã đang được nhân đôi. Giả sử bạn phải sắp xếp danh sách bằng cách sử dụng cùng một điều kiện một vài lần trong một phương thức. Tôi có thể tuyên bố một lambda trong phạm vi phương pháp và tái sử dụng đó. Nhưng nhìn chung, tôi đồng ý với điều này! +1 –

+0

Vâng, không hoàn toàn. Nếu các kiểu của bạn thậm chí còn phức tạp từ xa, việc xác định kiểu đầy đủ của lambda có thể hoạt động đáng kể hơn là sao chép nó một vài lần. – MichaelGG

+0

Đó là những gì 'var' là cho. OTOH, nếu bạn cần chuyển hoặc trả lại lambda làm tham số, thì tôi đồng ý. –

14

Vâng, khi chúng ta đang nói chuyện sử dụng đại biểu bout, không nên có bất kỳ sự khác biệt giữa lambda và các phương pháp vô danh - họ là như nhau, chỉ với cú pháp khác nhau. Và các phương thức được đặt tên (được sử dụng làm đại biểu) cũng giống hệt với quan điểm của thời gian chạy. Sự khác biệt, sau đó, là giữa việc sử dụng các đại biểu, so với mã nội tuyến - tức là

list.ForEach(s=>s.Foo()); 
// vs. 
foreach(var s in list) { s.Foo(); } 

(nơi tôi mong chờ sau này để được nhanh hơn)

Và không kém, nếu bạn đang nói về bất cứ điều gì khác so với các đối tượng trong bộ nhớ, lambdas là một trong những công cụ mạnh mẽ nhất của bạn trong việc duy trì kiểm tra kiểu (thay vì phân tích chuỗi mọi lúc). Chắc chắn, có những trường hợp khi một đơn giản foreach với mã sẽ nhanh hơn phiên bản LINQ, vì sẽ có ít lời gọi hơn để thực hiện, và gọi chi phí một thời gian nhỏ nhưng có thể đo lường được. Tuy nhiên, trong nhiều trường hợp, mã đơn giản không phải là nút cổ chai, và mã đơn giản hơn (đặc biệt là cho nhóm, vv) có giá trị nhiều hơn một vài nano giây.

Cũng lưu ý rằng trong .NET 4.0 có additional Expression nodes cho những thứ như vòng lặp, dấu phẩy, vv Ngôn ngữ không hỗ trợ chúng, nhưng thời gian chạy không. Tôi đề cập đến điều này chỉ cho đầy đủ: Tôi chắc chắn không nói bạn nên sử dụng hướng dẫn sử dụng xây dựng Expression nơi foreach sẽ làm!

+1

Tắt chủ đề, nhưng ý tôi là đã nghe điều gì đó trên các dòng của List.ForEach (. => ..) để nhanh hơn/hiệu quả hơn hoặc một cái gì đó hơn là sử dụng foreach (..) {..} bởi vì nó có truy cập vào mảng nội bộ và không sử dụng các điều tra viên và như vậy? có thể sai mặc dù ... – Svish

+0

Nó cũng có thể có quyền truy cập như vậy, nhưng nó cần phải làm đại biểu gọi, do đó, tăng gấp đôi lưỡi. Bạn sẽ cần phải hồ sơ để chắc chắn, nhưng tôi hy vọng 'foreach' sẽ nhanh hơn một chút. –

+2

Trớ trêu thay, đó không phải là trường hợp. Tin hay không, List.ForEach có cạnh. Yêu cầu của một đại biểu duy nhất là khá nhanh (tiếp cận cùng một tốc độ như một cuộc gọi giao diện). –

6

Tôi muốn nói rằng sự khác biệt về hiệu suất thường quá nhỏ (và trong trường hợp vòng lặp, rõ ràng, nếu bạn xem kết quả của bài viết thứ 2 (btw, Jon Skeet có bài viết tương tự here)) mà bạn hầu như không bao giờ chọn giải pháp vì lý do hiệu suất, trừ khi bạn đang viết một phần mềm khi hiệu suất hoàn toàn là yêu cầu không có chức năng số và bạn thực sự phải tối ưu hóa vi mô.

Khi nào nên chọn gì? Tôi đoán nó phụ thuộc vào tình hình mà còn là người. Cũng giống như một ví dụ, một số người perfer Danh sách.Foreach qua một vòng lặp foreach bình thường. Tôi cá nhân thích thứ hai, vì nó thường dễ đọc hơn, nhưng tôi là ai để tranh luận chống lại điều này?

+0

không có đối số từ tôi hoặc là :) –

+0

I * luôn luôn * sử dụng 'foreach' khi kết quả là cho tác dụng phụ. Lambdas cho tất cả những niềm vui LINQ, tất nhiên. –

4

Quy tắc ngón tay cái:

  1. Viết mã của bạn được tự nhiên và dễ đọc.
  2. Tránh trùng lặp mã (biểu thức lambda có thể yêu cầu thêm một chút sự tích cực).
  3. Chỉ tối ưu hóa khi có sự cố và chỉ với dữ liệu để sao lưu vấn đề đó thực sự là gì.
+2

"Làm cho nó chính xác, làm cho nó rõ ràng, làm cho nó ngắn gọn, làm cho nó nhanh chóng. Trong thứ tự đó" (Wes Dyer http://blogs.msdn.com/wesdyer/archive/2007/03/01/immutability-purity-and -referential-transparency.aspx) – Benjol

+0

Điều đó thật đúng. Phần đầu tiên thường ít được nói đến, "làm cho nó đúng". Trong thực tế khá thường xuyên, ba đầu tiên rơi vào vị trí tốt. Nếu bạn làm cho nó chính xác, nó khá dễ đọc mà lần lượt sẽ khá ngắn gọn. – nawfal

2

Nếu bạn cần đệ quy, không sử dụng lambdas, or you'll end up getting very distracted!

+0

So sánh với F #: "hãy nhớ y f x = f (y f) x" :) – MichaelGG

+0

Tôi không đồng ý. Đệ quy không rõ ràng trong bất kỳ cách tiếp cận nào. –

+3

@Jerry Nixon - Đệ quy đôi khi rõ ràng hơn nhiều so với giải pháp thay thế. Hãy thử làm việc thông qua http://mitpress.mit.edu/sicp/ –

31

Mặc dù tôi sẽ tập trung vào điểm một, tôi bắt đầu bằng cách dành 2 xu cho toàn bộ vấn đề hiệu suất. Trừ khi sự khác biệt là lớn hoặc sử dụng là chuyên sâu, thường tôi không bận tâm về micro giây mà khi được thêm vào không tính đến bất kỳ sự khác biệt có thể nhìn thấy cho người dùng. Tôi nhấn mạnh rằng tôi chỉ không quan tâm khi xem xét các phương pháp không được gọi là chuyên sâu. Nơi tôi có những cân nhắc về hiệu suất đặc biệt là trên cách tôi tự thiết kế ứng dụng. Tôi quan tâm đến bộ nhớ đệm, về việc sử dụng các chủ đề, về các cách thông minh để gọi các phương thức (cho dù thực hiện một số cuộc gọi hoặc cố gắng thực hiện chỉ một cuộc gọi), cho dù kết nối với nhau hay không, v.v. không tập trung vào hiệu suất thô, nhưng về khả năng mở rộng. Tôi không quan tâm nếu nó chạy tốt hơn bởi một lát nhỏ của một nano giây cho một người dùng duy nhất, nhưng tôi quan tâm rất nhiều để có khả năng tải hệ thống với số lượng lớn người dùng đồng thời mà không nhận thấy tác động.

Có nói rằng, ở đây đi ý kiến ​​của tôi về điểm 1. Tôi yêu phương pháp vô danh. Họ mang lại cho tôi sự linh hoạt và mã hóa sang trọng. Một tính năng tuyệt vời khác về các phương thức nặc danh là chúng cho phép tôi trực tiếp sử dụng các biến cục bộ từ phương thức container (từ góc nhìn C#, không phải từ quan điểm IL, tất nhiên). Họ phụ tùng cho tôi vô số mã. Khi nào tôi sử dụng các phương thức nặc danh? Evey lần duy nhất đoạn mã tôi cần là không cần thiết ở nơi khác. Nếu nó được sử dụng ở hai nơi khác nhau, tôi không thích sao chép-dán như là một kỹ thuật tái sử dụng, vì vậy tôi sẽ sử dụng một đại biểu ol 'đồng bằng. Vì vậy, giống như shoosh trả lời, nó không phải là tốt để có bản sao mã. Về lý thuyết không có sự khác biệt về hiệu suất như ẩn danh là thủ đoạn C#, chứ không phải thứ IL.

Hầu hết những gì tôi nghĩ về các phương thức nặc danh áp dụng cho các biểu thức lambda, vì sau này có thể được sử dụng như một cú pháp nhỏ gọn để biểu diễn các phương thức ẩn danh. Hãy giả sử phương pháp sau:

public static void DoSomethingMethod(string[] names, Func<string, bool> myExpression) 
{ 
    Console.WriteLine("Lambda used to represent an anonymous method"); 
    foreach (var item in names) 
    { 
     if (myExpression(item)) 
      Console.WriteLine("Found {0}", item); 
    } 
} 

Nó nhận được một chuỗi các chuỗi và cho mỗi chuỗi, nó sẽ gọi phương thức được truyền cho nó. Nếu phương thức đó trả về true, nó sẽ nói "Found ...". Bạn có thể gọi phương pháp này theo cách sau:

string[] names = {"Alice", "Bob", "Charles"}; 
DoSomethingMethod(names, delegate(string p) { return p == "Alice"; }); 

Tuy nhiên, bạn cũng có thể gọi nó theo cách sau:

DoSomethingMethod(names, p => p == "Alice"); 

Không có sự khác biệt trong IL giữa cả hai, được rằng một trong những cách sử dụng Biểu thức Lambda dễ đọc hơn nhiều. Một lần nữa, không có tác động hiệu suất vì đây là tất cả các thủ thuật trình biên dịch C# (không phải thủ thuật biên dịch JIT). Cũng giống như tôi không cảm thấy chúng ta đang lạm dụng các phương thức ẩn danh, tôi không cảm thấy chúng ta đang lạm dụng các biểu thức Lambda để biểu diễn các phương thức nặc danh. Tất nhiên, cùng một logic áp dụng cho mã lặp lại: Đừng làm lambdas, sử dụng các đại biểu thường xuyên. Có những hạn chế khác dẫn bạn trở lại các phương thức nặc danh hoặc các đại biểu đơn giản, chẳng hạn như tham gia ngoài hoặc đối số.

Những điều tốt đẹp khác về biểu thức Lambda là cú pháp chính xác không cần phải đại diện cho một phương thức ẩn danh. Biểu thức lambda cũng có thể biểu diễn ... bạn đoán, biểu thức. Lấy ví dụ sau:

public static void DoSomethingExpression(string[] names, System.Linq.Expressions.Expression<Func<string, bool>> myExpression) 
{ 
    Console.WriteLine("Lambda used to represent an expression"); 
    BinaryExpression bExpr = myExpression.Body as BinaryExpression; 
    if (bExpr == null) 
     return; 
    Console.WriteLine("It is a binary expression"); 
    Console.WriteLine("The node type is {0}", bExpr.NodeType.ToString()); 
    Console.WriteLine("The left side is {0}", bExpr.Left.NodeType.ToString()); 
    Console.WriteLine("The right side is {0}", bExpr.Right.NodeType.ToString()); 
    if (bExpr.Right.NodeType == ExpressionType.Constant) 
    { 
     ConstantExpression right = (ConstantExpression)bExpr.Right; 
     Console.WriteLine("The value of the right side is {0}", right.Value.ToString()); 
    } 
} 

Chú ý chữ ký hơi khác. Tham số thứ hai nhận một biểu thức chứ không phải một đại biểu. Cách gọi phương thức này sẽ là:

DoSomethingExpression(names, p => p == "Alice"); 

Đúng như cuộc gọi chúng tôi đã thực hiện khi tạo phương thức ẩn danh với lambda. Sự khác biệt ở đây là chúng ta không tạo ra một phương thức nặc danh, nhưng tạo ra một cây biểu thức. Đó là do những cây biểu thức mà sau đó chúng ta có thể dịch các biểu thức lambda thành SQL, đó là những gì Linq 2 SQL làm, thay vì thực thi các công cụ trong động cơ cho mỗi mệnh đề như Where, the Select, vv là cú pháp gọi là giống nhau cho dù bạn đang tạo một phương thức nặc danh hay gửi một biểu thức.

+3

+1 Tất cả nội dung hay. Một điều chỉnh nhỏ: chi phí của việc gọi instantiating và GC-ing một phương thức nặc danh được đo bằng nano giây, không phải là micro giây! –

+0

Vâng, tất nhiên, cảm ơn !!! lolol –

4

Bất kỳ lúc nào lambda chỉ chuyển trực tiếp đối số của nó sang hàm khác. Đừng tạo một lambda cho ứng dụng hàm.

Ví dụ:

var coll = new ObservableCollection<int>(); 
myInts.ForEach(x => coll.Add(x)) 

là đẹp hơn như:

var coll = new ObservableCollection<int>(); 
myInts.ForEach(coll.Add) 

Ngoại lệ chính là nơi mà C# 's suy luận kiểu không vì lý do gì (và có rất nhiều lần đó là sự thật).

23

Câu trả lời của tôi sẽ không phổ biến.

Tôi tin rằng Lambda là 99% luôn là lựa chọn tốt hơn vì ba lý do.

Trước tiên, có TUYỆT ĐỐI là không có gì sai với giả định các nhà phát triển của bạn thông minh. Câu trả lời khác có một tiền đề cơ bản mà mọi nhà phát triển nhưng bạn là ngu ngốc. Không phải vậy.

Thứ hai, Lamdas (et al) là một cú pháp hiện đại - và ngày mai chúng sẽ phổ biến hơn so với hiện tại. Mã của dự án của bạn nên chảy từ các quy ước hiện tại và đang nổi lên.

Thứ ba, viết mã "theo cách cũ" có vẻ dễ dàng hơn cho bạn, nhưng trình biên dịch không dễ dàng hơn. Điều này là quan trọng, phương pháp tiếp cận di sản có ít cơ hội để được cải thiện khi trình biên dịch được rev'ed. Lambdas (et al) dựa vào trình biên dịch để mở rộng chúng có thể được hưởng lợi khi trình biên dịch giao dịch với chúng tốt hơn theo thời gian.

Tóm lại:

  1. phát triển có thể xử lý nó
  2. Mọi người đều đang làm việc đó
  3. Có tương lai tiềm năng

lần nữa, tôi biết điều này sẽ không có một câu trả lời phổ biến. Và tôi tin rằng "Đơn giản là tốt nhất" cũng là câu thần chú của tôi. Bảo trì là một khía cạnh quan trọng đối với bất kỳ nguồn nào. Tôi hiểu rồi. Nhưng tôi nghĩ chúng ta đang làm lu mờ thực tế với một số quy tắc nhỏ nhặt.

// Jerry

+9

Điều duy nhất sai với câu trả lời này là sai lầm trong câu đầu tiên :) –

+3

Tôi không thể tin rằng tôi đã không đề cập đến việc giảm dòng mã làm tăng khả năng đọc của nguồn của bạn. Tôi có thể hấp thụ 2.700 dòng dễ dàng hơn 27.000. Lambdas giúp giảm dòng mã. –

+4

Câu trả lời này có hai vấn đề, một câu hỏi mà bạn không trả lời câu hỏi, câu hỏi là khoảng 1%. Hai, bạn ngay lập tức bắt đầu với "tốt hơn", nhưng tốt hơn so với những gì? Nếu bạn có ý nghĩa so với từ khóa 'đại biểu', tôi đồng ý. Nếu bạn có ý nghĩa hơn là viết các phương pháp có tên, thì tôi không đồng ý – nawfal

2

Biểu thức Lambda rất tuyệt. Cú pháp cũ hơn chúng có một số ưu điểm như, chúng có thể được chuyển đổi thành một trong hai chức năng ẩn danh hoặc biểu thức cây, kiểu tham số được suy ra từ khai báo, chúng sạch hơn và ngắn gọn hơn. biểu thức lambda khi bạn cần một hàm ẩn danh. Một lợi thế không lớn như vậy phong cách trước đó là bạn có thể bỏ qua việc khai báo tham số hoàn toàn nếu chúng không được sử dụng. Giống như

Action<int> a = delegate { }; //takes one argument, but no argument specified 

này rất hữu ích khi bạn phải khai báo một đại biểu có sản phẩm nào mà không làm gì, nhưng nó không phải là một mạnh lý do đủ để không sử dụng lambdas.

Lambdas cho phép bạn viết các phương thức ẩn danh nhanh. Bây giờ làm cho lambdas vô nghĩa ở khắp mọi nơi, nơi các phương thức vô danh đi vô nghĩa, tức là nơi các phương thức được đặt tên có ý nghĩa hơn. Over tên phương pháp, phương pháp vô danh có thể bất lợi (không phải là một biểu thức lambda cho mỗi gia nhập điều, nhưng kể từ những ngày lambdas đại diện rộng rãi phương pháp vô danh đó là có liên quan):

  1. vì nó có xu hướng dẫn đến trùng lắp logic (thường không, tái sử dụng là khó khăn)

  2. khi nó là cần thiết để viết thư cho một, như:

    //this is unnecessary 
    Func<string, int> f = x => int.Parse(x); 
    
    //this is enough 
    Func<string, int> f = int.Parse; 
    
  3. từ viết khối iterator nặc danh là i mpossible.

    Func<IEnumerable<int>> f =() => { yield return 0; }; //impossible 
    
  4. từ lambdas đệ quy đòi hỏi nhiều một dòng quirkiness, như

    Func<int, int> f = null; 
    f = x => (x <= 1) ? 1 : x * f(x - 1); 
    
  5. tốt, vì phản ánh là kinda Messier, nhưng đó là tranh luận đúng không?

Ngoài điểm 3, phần còn lại là không mạnh lý do không sử dụng lambdas.

Cũng thấy điều này thread về những điều bất lợi về Func/Action đại biểu, vì chúng thường được sử dụng cùng với biểu thức lambda.

Các vấn đề liên quan