2013-04-07 26 views
10

LINQPad dụ:Tại sao lambdas có thể chuyển đổi thành các biểu thức nhưng các nhóm phương pháp thì không?

void Main() 
{ 
    One(i => PrintInteger(i)); 
    One(PrintInteger); 

    Two(i => PrintInteger(i)); 
    // Two(PrintInteger); - won't compile 
} 

static void One(Action<int> a) 
{ 
    a(1); 
} 

static void Two(Expression<Action<int>> e) 
{ 
    e.Compile()(2); 
} 

static void PrintInteger(int i) 
{ 
    Console.WriteLine(i); 
} 

uncommenting các Two(PrintInteger); kết quả dòng trong một lỗi: "tại sao"

cannot convert from 'method group' to 'System.Linq.Expressions.Expression<System.Action<int>>'

này tương tự như Convert Method Group to Expression, nhưng tôi quan tâm đến Tôi hiểu rằng Features cost money, time and effort; Tôi tự hỏi nếu có một lời giải thích thú vị hơn.

+0

Có lẽ nó liên quan đến thực tế là trình biên dịch chỉ có thể chuyển * biểu hiện lambdas * thành cây biểu thức. Báo cáo và lambdas đa dòng không được hỗ trợ. Xem: [Cây biểu hiện] (http://msdn.microsoft.com/en-us/library/bb397951.aspx) –

Trả lời

8

Bởi vì, để có được cây biểu thức, chúng ta cần trình bày phương thức trong (chưa biên soạn) nguồn mẫu. Biểu thức lambda có sẵn cục bộ trong mã nguồn và do đó, luôn là có sẵn chưa được biên dịch. Nhưng các phương thức có thể không phải từ bên trong assembly hiện tại, và do đó có thể chỉ có sẵn ở dạng biên dịch.

Được cấp, trình biên dịch C# có thể mã IL của hội đồng để truy xuất cây biểu thức nhưng như bạn đã đề cập,

+0

Trình biên dịch không cần phải dịch ngược phương thức đang được gọi. Nó chỉ cần tổng hợp lambda mà luôn luôn có thể. Vì vậy, đối số của câu trả lời này không giữ. – usr

+0

@usr Ý bạn là gì bởi “nó cần tổng hợp lambda”? –

+0

Trình biên dịch có thể dễ dàng chuyển từ 'Một (PrintInteger)' sang 'Một (i => PrintInteger (i))' bằng một phép biến đổi đơn giản trên cây cú pháp của nó. Điều này có nghĩa là không có lý do cơ bản nào tại sao nó không thể được thực hiện. – usr

2

Không có nguyên nhân nào về nguyên tắc. Nó có thể được thực hiện theo cách này. Trình biên dịch chỉ có thể tạo lambda bằng chính nó trước khi chuyển đổi nó (điều này rõ ràng là luôn luôn có thể - nó biết chính xác phương thức được gọi để nó chỉ có thể tạo ra một lambda từ các tham số của nó).

Tuy nhiên, chỉ có một điểm bắt. Tên của tham số lambda của bạn thường được mã hóa cứng vào IL được tạo ra. Nếu không có lambda, không có tên. Nhưng trình biên dịch có thể tạo tên giả hoặc sử dụng lại tên của phương thức được gọi (chúng luôn có sẵn ở định dạng lắp ráp .NET).

Tại sao nhóm C# không quyết định bật tính năng này? Lý do duy nhất mà tôi nghĩ là họ muốn dành thời gian ở nơi khác. Tôi hoan nghênh họ vì quyết định đó. Tôi muốn có LINQ hoặc async hơn tính năng tối nghĩa này.

0

Trong ví dụ One, bạn đang tạo một đại diện Action<int> đại diện. Nó giống như:

One(new Action<int>(PrintInteger)); 

Tôi tin rằng đây là ngôn ngữ để cải thiện cú pháp đăng ký sự kiện.

Điều tương tự cũng không xảy ra với Expression<T>, đó là lý do tại sao ví dụ thứ hai của bạn không biên dịch.


EDIT:

Nó được gọi là một "nhóm chuyển đổi phương pháp". Trong thông số C# - phần 6.6

An implicit conversion (§6.1) exists from a method group (§7.1) to a compatible delegate type

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