List.ForEach(Console.WriteLine);
List.ForEach(s => Console.WriteLine(s));
Đối với tôi, sự khác biệt hoàn toàn là mỹ phẩm, nhưng có lý do tinh tế nào khiến người ta có thể được ưu tiên hơn người kia không?
List.ForEach(Console.WriteLine);
List.ForEach(s => Console.WriteLine(s));
Đối với tôi, sự khác biệt hoàn toàn là mỹ phẩm, nhưng có lý do tinh tế nào khiến người ta có thể được ưu tiên hơn người kia không?
Câu trả lời:
Nhìn vào mã được biên dịch thông qua ILSpy, thực sự có một sự khác biệt trong hai tài liệu tham khảo. Đối với một chương trình đơn giản như thế này:
namespace ScratchLambda
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
internal class Program
{
private static void Main(string[] args)
{
var list = Enumerable.Range(1, 10).ToList();
ExplicitLambda(list);
ImplicitLambda(list);
}
private static void ImplicitLambda(List<int> list)
{
list.ForEach(Console.WriteLine);
}
private static void ExplicitLambda(List<int> list)
{
list.ForEach(s => Console.WriteLine(s));
}
}
}
ILSpy dịch ngược nó thành:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ScratchLambda
{
internal class Program
{
private static void Main(string[] args)
{
List<int> list = Enumerable.Range(1, 10).ToList<int>();
Program.ExplicitLambda(list);
Program.ImplicitLambda(list);
}
private static void ImplicitLambda(List<int> list)
{
list.ForEach(new Action<int>(Console.WriteLine));
}
private static void ExplicitLambda(List<int> list)
{
list.ForEach(delegate(int s)
{
Console.WriteLine(s);
}
);
}
}
}
Nếu bạn nhìn vào ngăn xếp cuộc gọi IL cho cả hai, thì việc triển khai Rõ ràng có nhiều cuộc gọi hơn (và tạo ra một phương thức được tạo):
.method private hidebysig static
void ExplicitLambda (
class [mscorlib]System.Collections.Generic.List`1<int32> list
) cil managed
{
// Method begins at RVA 0x2093
// Code size 36 (0x24)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldsfld class [mscorlib]System.Action`1<int32> ScratchLambda.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
IL_0006: brtrue.s IL_0019
IL_0008: ldnull
IL_0009: ldftn void ScratchLambda.Program::'<ExplicitLambda>b__0'(int32)
IL_000f: newobj instance void class [mscorlib]System.Action`1<int32>::.ctor(object, native int)
IL_0014: stsfld class [mscorlib]System.Action`1<int32> ScratchLambda.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
IL_0019: ldsfld class [mscorlib]System.Action`1<int32> ScratchLambda.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
IL_001e: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::ForEach(class [mscorlib]System.Action`1<!0>)
IL_0023: ret
} // end of method Program::ExplicitLambda
.method private hidebysig static
void '<ExplicitLambda>b__0' (
int32 s
) cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x208b
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call void [mscorlib]System.Console::WriteLine(int32)
IL_0006: ret
} // end of method Program::'<ExplicitLambda>b__0'
trong khi việc thực hiện ngầm định ngắn gọn hơn:
.method private hidebysig static
void ImplicitLambda (
class [mscorlib]System.Collections.Generic.List`1<int32> list
) cil managed
{
// Method begins at RVA 0x2077
// Code size 19 (0x13)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldnull
IL_0002: ldftn void [mscorlib]System.Console::WriteLine(int32)
IL_0008: newobj instance void class [mscorlib]System.Action`1<int32>::.ctor(object, native int)
IL_000d: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::ForEach(class [mscorlib]System.Action`1<!0>)
IL_0012: ret
} // end of method Program::ImplicitLambda
Tôi thích cú pháp lambda nói chung . Khi bạn thấy điều đó, thì nó sẽ cho bạn biết loại đó là gì. Khi bạn thấy Console.WriteLine
, bạn phải hỏi IDE đó là loại gì. Tất nhiên, trong ví dụ tầm thường này, điều đó là hiển nhiên, nhưng trong trường hợp chung, nó có thể không quá nhiều.
với hai ví dụ bạn đưa ra, chúng khác nhau ở chỗ khi bạn nói
List.ForEach(Console.WriteLine)
bạn thực sự đang nói với ForEach Loop để sử dụng phương thức WriteLine
List.ForEach(s => Console.WriteLine(s));
đang thực sự xác định một phương thức mà foreach sẽ gọi và sau đó bạn đang nói với nó những gì cần xử lý ở đó.
Vì vậy, đối với một lớp lót đơn giản nếu phương thức bạn định gọi mang chữ ký giống như phương thức đã được gọi, tôi không muốn xác định lambda, tôi nghĩ nó dễ đọc hơn một chút.
đối với các phương pháp với lambdas không tương thích chắc chắn là một cách tốt để đi, giả sử chúng không quá phức tạp.
Có một lý do rất mạnh mẽ để thích dòng đầu tiên.
Mỗi đại biểu có một thuộc Target
tính, cho phép các đại biểu tham chiếu đến các phương thức thể hiện, ngay cả khi thể hiện đã vượt quá phạm vi.
public class A {
public int Data;
public void WriteData() {
Console.WriteLine(this.Data);
}
}
var a1 = new A() {Data=4};
Action action = a1.WriteData;
a1 = null;
Chúng tôi không thể gọi a1.WriteData();
vì không a1
có giá trị. Tuy nhiên, chúng ta có thể gọi ra action
ủy nhiệm mà không gặp vấn đề gì và nó sẽ in 4
, vì action
giữ một tham chiếu đến thể hiện mà phương thức sẽ được gọi.
Khi các phương thức ẩn danh được truyền dưới dạng đại biểu trong ngữ cảnh cá thể, đại biểu vẫn sẽ giữ một tham chiếu đến lớp chứa, mặc dù điều đó không rõ ràng:
public class Container {
private List<int> data = new List<int>() {1,2,3,4,5};
public void PrintItems() {
//There is an implicit reference to an instance of Container here
data.ForEach(s => Console.WriteLine(s));
}
}
Trong trường hợp cụ thể này, thật hợp lý khi giả định rằng .ForEach
không lưu trữ nội bộ của đại biểu, điều đó có nghĩa là trường hợp Container
và tất cả dữ liệu của nó vẫn được giữ lại. Nhưng không có sự bảo đảm về điều đó; phương thức tiếp nhận đại biểu có thể giữ cho đại biểu và trường hợp vô thời hạn.
Mặt khác, các phương thức tĩnh không có ví dụ để tham chiếu. Sau đây sẽ không có một tham chiếu ngầm đến trường hợp Container
:
public class Container {
private List<int> data = new List<int>() {1,2,3,4,5};
public void PrintItems() {
//Since Console.WriteLine is a static method, there is no implicit reference
data.ForEach(Console.WriteLine);
}
}