2009-10-20 32 views
21

Tôi có một phương pháp với chữ ký sau đây:Viết một phương pháp mà chấp nhận một biểu thức lambda

void MyMethod(Delegate d){}; 
void MyMethod(Expression exp){}; 
void MyMethod(object obj){}; 

Tuy nhiên, đây không biên dịch:

MyMethod((int a) => a) 

với các lỗi sau:

"Cannot convert lambda expression to type 'object' because it is not a delegate type" 

Tại sao tính năng này không hoạt động?

Chỉnh sửa: Tôi biết rằng công trình này hoạt động. Trình biên dịch biên dịch biểu thức lambda thành một delgate trong trường hợp này tôi nghĩ.

void MyMethod(Func<int, int> d){}; 

Trân trọng!

+3

Fyi, Bất cứ khi nào một cái gì đó không biên dịch, đọc (và đăng) thông báo lỗi là tốt. –

+0

@SharePoint Người mới: Vui lòng xem bài đăng cập nhật của tôi. Điều đó sẽ giải quyết lỗi của bạn ngay bây giờ. – Noldorin

Trả lời

17

Vì loại System.Delegate không phải là "Đại biểu". Nó chỉ là lớp cơ sở. Bạn phải sử dụng một loại đại biểu với chữ ký chính xác. Xác định phương pháp của bạn như sau:

void MyMethod(Func<int, int> objFunc) 

EDIT:

MyMethod (object) không làm việc vì một biểu thức lambda không có loại tại riêng của nó, nhưng loại được suy ra từ các loại vị trí đó được gán cho. Vì vậy, đối tượng không hoạt động. Bạn phải sử dụng một loại đại biểu với chữ ký chính xác.

+0

Tôi biết rằng điều này làm việc, tôi muốn biết tại sao các chữ ký khác không hoạt động. –

+0

Sau đó đọc những gì tôi đã viết. System.Delegate không phải là một đại biểu, một biểu thức lambda không thể được chuyển đổi thành System.Delegate. Trong thực tế, các biểu thức lambda không có kiểu nào, chúng lấy kiểu của chúng từ kiểu biến mà chúng được gán cho, vì vậy đối tượng cũng không hoạt động. –

+0

Nhưng tôi tự xác định loại. Nó không được suy diễn ngầm, "(int a) => a;". Bạn có thể vui lòng giải thích, rằng các biểu thức lambda không có loại chính nó? –

12
void MyMethod(Action<int> lambdaHereLol) 
{ 
    lambdaHereLol(2); 
} 

sử dụng:

var hurrDurr = 5; 
MyMethod(x => Console.Write(x * hurrDurr)); 

C# là một ngôn ngữ tĩnh gõ. Trình biên dịch cần phải biết Loại của tất cả mọi thứ nó đề với. Lambdas là một chút khó khăn để móng tay xuống, và đôi khi trình biên dịch không thể tìm ra nó. Trong ví dụ trên của tôi, nếu MyMethod lấy một đối tượng, trình biên dịch không thể tìm ra rằng x là một int (ví dụ của tôi là đơn giản, nhưng không có gì nói rằng nó không thể phức tạp hơn và khó xác định hơn). Vì vậy, tôi phải rõ ràng hơn trong việc xác định phương pháp lấy lambda của tôi.

+1

Bất kỳ ai khác luôn luôn trộn lẫn "cloture" và "closure"? – Will

+3

+1 cho ngữ nghĩa mã vạch troll – Filip

+1

"C# là một ngôn ngữ được gõ tĩnh. Trình biên dịch cần biết Loại mọi thứ mà nó thỏa thuận." Điều này đúng nhưng phần còn lại không theo. Có rất nhiều ngôn ngữ gõ tĩnh có thể suy ra chữ ký kiểu cho bạn. F # là một ví dụ. – rgrinberg

2

Hãy thử điều này:

void MyMethod(Action<int> func) { } 

Bạn cần một đại biểu mạnh mẽ, đánh máy như một tham số để phương pháp này. Lý do các cuộc gọi khác thất bại là vì trình biên dịch C# sẽ không cho phép bạn truyền một biểu thức lambda đến một phương thức mong đợi một Object vì một biểu thức lambda không nhất thiết phải luôn là một đại biểu trong mọi trường hợp. Quy tắc tương tự này áp dụng cho việc chuyển biểu thức lambda dưới dạng Delegate.

Khi bạn chuyển lambda vào một hàm như tôi đã trình bày ở trên, biên dịch có thể giả định rằng bạn muốn biểu thức lambda được chuyển đổi thành một loại đại biểu cụ thể và làm như vậy.

+0

Tôi biết rằng công trình này, tôi muốn biết tại sao các chữ ký khác không hoạt động. –

0

Nó chỉ đơn giản là bản chất của trình biên dịch mà bạn cần phải vẽ một đối tượng đại biểu một cách rõ ràng đến Delegate khi chuyển nó làm tham số kiểu Delegate. Trong thực tế, các biểu thức lambda phức tạp hơn nữa trong đó chúng không được chuyển đổi hoàn toàn cho các đại biểu trong trường hợp này.

Những gì bạn cần là một dàn diễn viên đôi, như vậy:

MyMethod((Delegate)(Func<int, int>)((int a) => a)); 

trong đó tất nhiên tương ứng với phương pháp chữ ký:

void MyMethod(Delegate d); 

Tùy thuộc vào tình hình của bạn, bạn có thể muốn xác định một tham số của loại Func<int> thay vì Delegate (mặc dù tôi sẽ ngần ngại thêm quá tải, chỉ vì nó làm tăng thêm sự phức tạp không cần thiết trong sự trung thực).

+0

Điều này cũng không hoạt động. –

+0

Vâng, bởi vì, như tôi đã viết, System.Delegate không phải là một đại biểu. Nó chỉ là loại cơ sở cho các đại biểu. –

+1

Woops, bạn nói đúng. Tôi đã bỏ lỡ chuyển đổi sang loại đại biểu cần phải xảy ra trước tiên. Loại này cung cấp một cái nhìn sâu sắc về cách trình biên dịch C# ngầm định chuyển đổi các biểu thức lambda thành các đại biểu và sau đó nó chuyển nó như một tham số. Những gì bạn thấy ở đây là làm mọi thứ "thủ công". – Noldorin

3

Một lambda như (int a) => a sẽ phù hợp với bất kỳ đại biểu nào có số int và trả lại int. Func<int,int> chỉ là một ví dụ duy nhất, và bạn có thể dễ dàng khai báo một mình với delegate int Foo(int x);. Trong thực tế, biểu thức lambda này thậm chí sẽ phù hợp với một đại biểu mất int và trả về một double, bởi vì kết quả của lambda (a) được chuyển đổi hoàn toàn thành double.

Để một lambda được gán cho tất cả các loại đại biểu mà nó sẽ phù hợp, bản thân lambda vốn không có kiểu. Thay vào đó, nó sẽ đưa vào loại đại biểu bạn đang sử dụng, miễn là có thể. ((int a) => a không thể được gán cho Func<byte, byte> tất nhiên.)

Trong khi cả hai Func<int, int>Foo đại biểu tôi xác định tất nhiên có thể được chuyển đổi sang Delegate, một lambda không thể chuyển đổi trực tiếp với Delegate vì nó là không rõ ràng những gì thực tế của nó chữ ký sẽ là. Sau Delegate d = (int a) => a, sẽ dFoo hoặc Func<int, int> hoặc thậm chí Func<int, double>? Tất cả đều là những khả năng hợp lệ, và trình biên dịch không biết bạn định làm gì. Nó có thể đoán tốt nhất, nhưng C# không phải là loại ngôn ngữ thực hiện loại phỏng đoán đó. Đây cũng là lý do tại sao bạn không thể làm điều gì đó như var = (int a) => a.

Tôi nghĩ rằng các thông báo lỗi rằng trình biên dịch cung cấp cho cho Delegate d = (int a) => a; là rất rõ ràng:

Cannot convert lambda expression to type 'System.Delegate' because it is not a delegate type

trực giác bạn sẽ nghĩ Delegate là một loại đại biểu, nhưng đó không phải là cách làm việc. :)

0

Lý do không thành công này là lý do giống như một biểu thức như "đối tượng del = (int a) => a" hoặc thậm chí "var del = (int a) => a" không thành công. Bạn có thể nghĩ rằng trình biên dịch có thể tìm ra loại biểu thức lambda của bạn kể từ khi bạn đưa ra kiểu đối số một cách rõ ràng, nhưng thậm chí biết rằng biểu thức lấy một int và trả về một int, có một số loại đại biểu mà nó có thể được chuyển đổi đến. Kiểu ủy nhiệm Func là loại được sử dụng nhiều nhất cho các hàm chung như thế này, nhưng đó chỉ là một quy ước và không có gì mà trình biên dịch biết.

Những gì bạn cần làm là đưa biểu thức lambda vào loại đại biểu cụ thể để trình biên dịch chọn quá tải Đại biểu, hoặc sử dụng cú pháp đúc bình thường (Func) ((int a) => a), hoặc sử dụng cú pháp hàm tạo của đại biểu mới Func ((int a) => a).

Ngoài ra, bạn thường không muốn sử dụng lớp Delegate chưa phân loại trừ khi bạn cần gọi một thứ gì đó khác nhau tùy thuộc vào số đối số mà nó chấp nhận. Nó gần như luôn luôn tốt hơn để chấp nhận một Func hoặc hành động cho những thứ như callbacks.

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