2010-01-02 32 views
16

Một số câu hỏi C# trên StackOverflow hỏi cách đặt đại biểu/lambdas ẩn danh với thông số out hoặc ref. Xem, ví dụ:Tại sao các loại suy luận/tham số lambdas ẩn danh trên các tham số out/ref?

Để làm như vậy, bạn chỉ cần phải xác định kiểu của tham số, như trong:

public void delegate D(out T p); 
// ... 
D a = (out T t) => { ... };  // Lambda syntax. 
D b = delegate(out T t) { ... }; // Anonymous delegate syntax. 

gì Tôi tò mò về lý do tại sao loại được yêu cầu một cách rõ ràng. Có lý do cụ thể nào cho trường hợp này không? Tức là, từ quan điểm của trình biên dịch/ngôn ngữ, tại sao không được cho phép?

D a = (out t) => { ... };  // Lambda syntax -- implicit typing. 
D b = delegate(out t) { ... }; // Anonymous delegate syntax -- implicit typing. 

hoặc thậm chí tốt hơn, chỉ cần:

D a = (t) => { ... };  // Lambda syntax -- implicit typing and ref|out-ness. 
D b = delegate(t) { ... }; // Anonymous delegate syntax -- implicit typing and ref|out-ness. 
+3

Tôi rất tò mò về điều này. Hãy hy vọng Eric Lippert nhận thấy bài đăng này ... anh ấy là người thích nhất để có thể đưa ra một câu trả lời có ý nghĩa cho điều này. – LBushkin

+2

LBushkin, nếu bạn muốn thu hút sự chú ý của mình, bạn luôn có thể gửi cho tôi qua liên kết liên hệ trên blog của tôi. –

+0

Đối với các phương thức nặc danh với từ khóa 'delegate', quy tắc là bạn không xác định gì (bỏ qua cả dấu ngoặc đơn'() ') hoặc chỉ định chữ ký đầy đủ bao gồm các kiểu tham số và biến tố như' ref'/'out'. Vì vậy, 'ref' /' out' không là ngoại lệ. Đối với lambdas, ý tưởng của bạn có ý nghĩa nhưng tôi không chắc chắn nếu tôi nghĩ rằng đó là một ý tưởng tốt. Cú pháp '(out t) => {...}' làm cho nó trông giống như "out" là kiểu tham số. Sau đó 't => {...}' là tốt hơn, nhưng nó có thể là "nguy hiểm" nếu ai đó quên ý nghĩa của 'D' và chỉnh sửa nội dung' {...} 'của lambda mà không nhận ra' out' bản chất của 't'. –

Trả lời

17

Thú vị câu hỏi.

Trước tiên, hãy xem xét sự khác biệt giữa các phương thức ẩn danh và lambdas. Từ quan điểm của nhà biên dịch trình biên dịch, sự khác biệt quan trọng nhất là lambdas có thể yêu cầu trình biên dịch suy ra loại tham số từ đích mà lambda đang được gán; Các phương thức ẩn danh C# 2 không có tính năng này. Tính năng này có vẻ như một sự khác biệt nhỏ nhưng trên thực tế nó có các nhánh chính về việc thực hiện trình biên dịch. Xem loạt blog của tôi về vấn đề này đối với một số suy nghĩ về lý do tại sao đó là:

http://blogs.msdn.com/ericlippert/archive/2007/01/10/lambda-expressions-vs-anonymous-methods-part-one.aspx

Vì vậy, bây giờ hãy đến với câu hỏi thực tế của bạn: tại sao chúng ta không thể suy ra outness/refness từ loại mục tiêu để các thông số của lambda. Tức là, nếu chúng ta có đại biểu void D (out int x) thì chắc chắn D d = x => {x = 10; } có thể suy ra rằng x là "out int".

Không có lý do kỹ thuật nào tôi biết tại sao chúng tôi không thể làm điều đó. Bên trong trình biên dịch, các kiểu ra/ref được biểu diễn dưới dạng các kiểu khác.

Tuy nhiên, các tính năng không được thực hiện chỉ vì chúng có thể được thực hiện; chúng được thực hiện bởi vì có một lý do thuyết phục để làm như vậy.Đối với lambdas, lý do thuyết phục để làm suy luận kiểu ở nơi đầu tiên là LINQ; chúng ta muốn có thể làm một phép biến đổi cú pháp đơn giản trên một truy vấn hiểu vào một lời gọi phương thức với lambdas, và để cho phương thức suy luận kiểu phương thức làm việc ra các kiểu của tất cả các tham số lambda. Không có phương thức LINQ nào được tạo ra có đại biểu với tham số out hoặc ref.

Vì vậy, chúng tôi không có lý do thuyết phục để thực hiện tính năng này. Các đại biểu có tham số out/ref khá hiếm. Và phân công lambdas cho những đại biểu đó vẫn còn hiếm hơn. Vì vậy, đây là một tính năng mà chúng ta không cần, và điều đó hầu như không có lợi ích gì cả.

C# 3 là "cực dài" trên lịch biểu của Visual Studio; chúng tôi đã có số ngày làm việc nhiều nhất theo lịch trình của bất kỳ đội nào vận chuyển một thành phần trong VS. Điều đó có nghĩa là mỗi ngày chúng tôi đã xóa lịch biểu, toàn bộ sự phân chia được phân chia bị trượt. Đó là một sự nản chí mạnh mẽ để dành thời gian cho những tính năng không cần thiết mà không có ai hưởng lợi. Vì vậy, công việc không bao giờ được thực hiện.

Tôi đồng ý rằng sẽ tốt hơn nếu phù hợp hơn ở đây, nhưng điều này không xảy ra. Chúng tôi có nhiều ưu tiên cao hơn.

+1

Tôi nghĩ mọi người nhận thức sâu sắc về việc các bạn bận rộn như thế nào, nhưng chắc chắn là tốt khi biết rằng cánh cửa không bị đóng vì lý do kỹ thuật, và về mặt lý thuyết có thể làm điều này một ngày nào đó. Cảm ơn cho đầu vào, Eric! –

2

Từ Eric Lippert's comment về việc tại sao khai và phân công của một biến var không thể được chia:

Tôi đồng ý rằng về nguyên tắc này có thể được thực hiện , nhưng nó phức tạp hơn trong thực tế so với bản phác thảo nhanh của bạn. var không chỉ yêu cầu rằng có một initializer, nó cũng đòi hỏi rằng initializer không tham khảo biến. Nếu bạn có int M (out int) thì bạn có thể nói "int x = M (out x);" nhưng bạn không thể nói "var x = M (out x);" bởi vì để thực hiện độ phân giải quá tải trên M, chúng ta cần phải biết loại x, đó là những gì chúng tôi đang cố gắng tìm ra. Nó sẽ là hợp pháp để nói "var s; if (b) M (out s); else s = 0;" ?

Tôi đoán câu trả lời cho câu hỏi của bạn là tương tự, xem xét, ví dụ,

D a = (out var x) => x = M(out x); 
+2

Tôi không chắc lắm. Làm thế nào để bạn giải thích thực tế rằng nếu bạn loại bỏ từ khóa 'out' trình biên dịch có thể làm cho suy luận? Kiểu ủy nhiệm 'D' hoàn toàn xác định kiểu' x'. – LBushkin

+1

Tôi không tin rằng đây là trường hợp. Ở đây, chúng tôi biết rằng chúng tôi đang cố gắng để có được một cái gì đó trông giống như một 'D'. Nếu kết quả ở bên phải không phù hợp, điều đó có rõ ràng không? Với ví dụ 'var', nó có thể là bất cứ điều gì, do đó trình biên dịch không có bất kỳ gợi ý nào về" câu trả lời đúng "sẽ trông như thế nào. [Rất tiếc! LBushkin đánh tôi với cú đấm!] –

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