Chuỗi là các loại tham chiếu, nhưng chúng không thay đổi. Điều này cho phép chúng được interned bởi trình biên dịch; ở khắp mọi nơi cùng một chuỗi chữ xuất hiện, cùng một đối tượng có thể được tham chiếu.Tại sao các biểu thức lambda không "interned"?
Đại biểu cũng là loại tham chiếu không thay đổi. (Thêm một phương thức cho một đại biểu multicast bằng cách sử dụng toán tử +=
cấu thành gán; đó không phải là khả năng biến đổi.) Và, giống như, chuỗi, có một cách "theo nghĩa đen" để đại diện cho một đại biểu trong mã, sử dụng một biểu thức lambda, ví dụ:
Func<int> func =() => 5;
Phía bên phải của tuyên bố đó là biểu thức có loại Func<int>
; nhưng không nơi nào tôi gọi một cách rõ ràng các nhà xây dựng Func<int>
(cũng không phải là một chuyển đổi tiềm ẩn xảy ra). Vì vậy, tôi xem chủ đề này thực chất là chữ số. Tôi có nhầm lẫn về định nghĩa của tôi về "chữ" ở đây không?
Bất kể, đây là câu hỏi của tôi. Nếu tôi có hai biến cho, nói rằng, các loại Func<int>
, và tôi gán biểu thức lambda giống hệt nhau cho cả hai:
Func<int> x =() => 5;
Func<int> y =() => 5;
... gì đang ngăn cản các trình biên dịch từ điều trị này như là cùng một đối tượng Func<int>
?
Tôi hỏi vì mục 6.5.1 của C# 4.0 language specification nêu rõ:
Chuyển đổi của ngữ nghĩa giống hệt chức năng ẩn danh với cùng (có thể rỗng) bộ ngoài trường biến bị bắt với cùng đại biểu các loại được phép (nhưng không được yêu cầu ) để trả lại cùng một cá nhân đại diện . Thuật ngữ ngữ nghĩa giống hệt nhau được sử dụng ở đây để ngụ ý rằng thực hiện các hàm ẩn danh sẽ, trong mọi trường hợp, tạo ra cùng một hiệu ứng cho cùng một đối số.
Điều này làm tôi ngạc nhiên khi đọc; nếu hành vi này rõ ràng là cho phép, tôi đã mong đợi nó sẽ được triển khai. Nhưng có vẻ như không. Điều này trong thực tế đã nhận được rất nhiều nhà phát triển gặp rắc rối, đặc biệt. khi các biểu thức lambda đã được sử dụng để gắn các trình xử lý sự kiện thành công mà không có khả năng loại bỏ chúng. Ví dụ:
class EventSender
{
public event EventHandler Event;
public void Send()
{
EventHandler handler = this.Event;
if (handler != null) { handler(this, EventArgs.Empty); }
}
}
class Program
{
static string _message = "Hello, world!";
static void Main()
{
var sender = new EventSender();
sender.Event += (obj, args) => Console.WriteLine(_message);
sender.Send();
// Unless I'm mistaken, this lambda expression is semantically identical
// to the one above. However, the handler is not removed, indicating
// that a different delegate instance is constructed.
sender.Event -= (obj, args) => Console.WriteLine(_message);
// This prints "Hello, world!" again.
sender.Send();
}
}
Có bất kỳ lý do tại sao điều này vi-một thể hiện ủy nhiệm cho anonymous ngữ nghĩa giống hệt phương pháp-không được thực hiện?
Ngay cả khi nó hoạt động, tôi vẫn nghĩ rằng một ý tưởng tồi là tách một trình xử lý sự kiện bằng cách sao chép mã trong lambda. –
Tôi nghi ngờ rằng đây sẽ là một trong số đó "bởi vì ngân sách không có sẵn để thiết kế, triển khai, kiểm tra, ghi chép, vận chuyển và duy trì" những điều thú vị. Có lẽ Eric Lippert có thể kêu vang với một số thông tin nội bộ. – LukeH
Câu hỏi hay. Tôi sẽ thừa nhận rằng một lambda như trong mã có thể trông giống hệt nhau, nhưng vì sự khác biệt tham chiếu như đóng cửa bên ngoài nó sẽ không biên dịch cho cùng một MSIL ở cả hai nơi. Trình biên dịch phải biên dịch nó thành MSIL để phát hiện sự khác biệt, so với việc có thể phát hiện một chuỗi chữ "đang chạy" từ mã nguồn. Vì đây là một bước bổ sung cần thiết cho bất kỳ lambda nào, và chỉ cung cấp một khoản tiết kiệm kích thước nhỏ và ít nếu đạt được hiệu suất, họ có thể chỉ bỏ qua nó. – KeithS