2010-10-01 38 views
19

Khi giao dịch với một cái gì đó giống như một List<string> bạn có thể viết như sau:Có lợi ích gì khi sử dụng nhóm phương pháp C# nếu có?

list.ForEach(x => Console.WriteLine(x)); 

hoặc bạn có thể sử dụng một nhóm phương pháp để làm các hoạt động tương tự:

list.ForEach(Console.WriteLine); 

Tôi thích dòng thứ hai của mã vì nó trông sạch hơn với tôi, nhưng có lợi ích gì cho điều này không?

+3

Vâng, ReSharper khuyến cáo các phiên bản thứ hai. Vì vậy, nó phải là một trong những quyền ... –

+4

"Một người thông minh hơn nói rằng đúng" không thực sự là một lời giải thích của * TẠI SAO *. Nó có thể đúng. Nó có thể là câu trả lời tốt nhất. Nhưng điều đó không trả lời câu hỏi "tại sao". – WernerCD

Trả lời

23

Vâng, hãy xem và xem điều gì sẽ xảy ra.

static void MethodGroup() 
{ 
    new List<string>().ForEach(Console.WriteLine); 
} 

static void LambdaExpression() 
{ 
    new List<string>().ForEach(x => Console.WriteLine(x)); 
} 

Điều này được biên dịch thành IL sau đây.

.method private hidebysig static void MethodGroup() cil managed 
{ 
    .maxstack 8 
    L_0000: newobj instance void [mscorlib]System.Collections.Generic.List`1<string>::.ctor() 
    L_0005: ldnull 
    L_0006: ldftn void [mscorlib]System.Console::WriteLine(string) 
    L_000c: newobj instance void [mscorlib]System.Action`1<string>::.ctor(object, native int) 
    L_0011: call instance void [mscorlib]System.Collections.Generic.List`1<string>::ForEach(class [mscorlib]System.Action`1<!0>) 
    L_0016: ret 
} 

.method private hidebysig static void LambdaExpression() cil managed 
{ 
    .maxstack 8 
    L_0000: newobj instance void [mscorlib]System.Collections.Generic.List`1<string>::.ctor() 
    L_0005: ldsfld class [mscorlib]System.Action`1<string> Sandbox.Program::CS$<>9__CachedAnonymousMethodDelegate1 
    L_000a: brtrue.s L_001d 
    L_000c: ldnull 
    L_000d: ldftn void Sandbox.Program::<LambdaExpression>b__0(string) 
    L_0013: newobj instance void [mscorlib]System.Action`1<string>::.ctor(object, native int) 
    L_0018: stsfld class [mscorlib]System.Action`1<string> Sandbox.Program::CS$<>9__CachedAnonymousMethodDelegate1 
    L_001d: ldsfld class [mscorlib]System.Action`1<string> Sandbox.Program::CS$<>9__CachedAnonymousMethodDelegate1 
    L_0022: call instance void [mscorlib]System.Collections.Generic.List`1<string>::ForEach(class [mscorlib]System.Action`1<!0>) 
    L_0027: ret 
} 

Chú ý cách tiếp cận nhóm phương pháp tạo ra một đại biểu Action<T> cho một thời gian sử dụng và các phương pháp biểu thức lambda tạo ra một lĩnh vực đại biểu vô danh ẩn và làm một khởi inline của nó nếu cần thiết. Thông báo hướng dẫn brtrue tại IL_000a.

+0

cách tôi có thể xem IL đã biên dịch không? –

+0

@ M.H .: Sử dụng ILDASM hoặc Reflector. –

+0

@ M.H bạn có thể sử dụng LINQPad để xem IL đã biên dịch – mbx

3

Có; đầu tiên thực sự có thể gây ra một cuộc gọi tạm thời, không cần thiết để xảy ra; chuyển số x vào phương thức chỉ cần gọi Console.WriteLine(x); Bạn không cần thực hiện thao tác đầu tiên vì Console.WriteLine đã là phương thức khớp với chữ ký mà ForEach đang tìm kiếm.

0

Cá nhân tôi cũng thích thứ hai vì nó ít gây nhầm lẫn để gỡ lỗi, nhưng trong trường hợp này, tôi nghĩ rằng đó chỉ là vấn đề về phong cách vì cả hai đều kết thúc việc thực hiện tương tự.

0

Không có lợi ích hữu hình nào ngoài việc làm cho nó dễ chịu hơn với những người thích nhóm phương pháp, và làm phiền những người không thích chúng [xin vui lòng bạn.] Ngoài ra, nó làm cho mã của bạn không tương thích với các trình biên dịch trước đó.

-Oisin

+0

Trình biên dịch nào chấp nhận thứ hai và không phải là trình biên dịch đầu tiên? –

7

Tôi tin rằng có lợi ích. Trong trường hợp đầu tiên, bạn đang tạo một phương thức ẩn danh gọi hàm Console.Writeline(string) trong trường hợp khác, bạn chỉ chuyển tham chiếu đến hàm hiện có.

+3

Yup, đó cũng là cảm giác của tôi. Tôi tưởng tượng nó có thể là trình tối ưu hóa có thể nhận ra điều này và loại bỏ các cuộc gọi không cần thiết, nhưng khi nó thực sự dễ dàng hơn để viết nó theo cách 'tốt hơn', có nghĩa là làm như vậy, IMO. Tôi đã viết một bài đăng blog về chủ đề này (không cần thiết khi sử dụng các biểu thức Lambda như sau: http://www.andrewbarber.com/post/When-to-Avoid-Lambda-Expressions-or-What-Anonymous-Methods.aspx) –

7

Có thêm mức độ gián đoạn khi sử dụng biểu thức lambda. Với một biểu thức không đóng cửa như thế, bạn sẽ chỉ có một cuộc gọi phương thức bổ sung ở giữa, như được đề cập bởi những người khác.

Có một vài điểm khác biệt thú vị. Trong trường hợp thứ hai, một cá thể ủy nhiệm mới đang được tạo trên mỗi cuộc gọi. Đối với trước đây, đại biểu được tạo một lần và được lưu vào bộ nhớ cache dưới dạng trường ẩn, vì vậy nếu bạn đang gọi rất nhiều, bạn sẽ tiết kiệm được phân bổ. Ngoài ra, nếu bạn giới thiệu một biến cục bộ vào biểu thức lambda, nó sẽ trở thành một đóng và thay vì chỉ là một phương thức cục bộ được tạo ra, một lớp mới sẽ được tạo để chứa thông tin này, có nghĩa là phân bổ thêm ở đó.

8

Như những người khác đã lưu ý, có một lớp không cần thiết của indirection gây ra bởi lambda. Tuy nhiên, cũng có những khác biệt về ngôn ngữ tinh tế. Ví dụ, trong suy luận kiểu chung C# 3 hoạt động khác nhau trên M(F) hơn trên M(x=>F(x)) khi cố gắng thực hiện suy luận kiểu trả về.

Để biết chi tiết xem:

http://blogs.msdn.com/b/ericlippert/archive/2007/11/05/c-3-0-return-type-inference-does-not-work-on-member-groups.aspx

và theo dõi:

http://blogs.msdn.com/b/ericlippert/archive/2008/05/28/method-type-inference-changes-part-zero.aspx

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