2009-01-05 45 views
7

Một lợi thế của biểu thức lambda là bạn phải đánh giá một hàm chỉ khi bạn cần kết quả của nó.C# biểu thức lambda và đánh giá lười biếng

Trong như sau (đơn giản) Ví dụ, chức năng Văn bản chỉ đánh giá khi một nhà văn có mặt:

public static void PrintLine(Func<string> text, TextWriter writer) 
{ 
    if (writer != null) 
    { 
     writer.WriteLine(text()); 
    } 
} 

Thật không may, điều này làm cho sử dụng mã một chút xấu xí. Bạn không thể gọi nó với một hằng hoặc biến như

PrintLine("Some text", Console.Out); 

và phải gọi nó theo cách này:

PrintLine(() => "Some text", Console.Out); 

Trình biên dịch không có khả năng "suy ra" một hàm parameterless từ hằng trôi qua. Có kế hoạch để cải thiện điều này trong các phiên bản tương lai của C# hay tôi thiếu một cái gì đó?

UPDATE:

tôi chỉ tìm thấy một bẩn Hack bản thân mình:

public class F<T> 
    { 
     private readonly T value; 
     private readonly Func<T> func; 

     public F(T value) { this.value = value; } 
     public F(Func<T> func) {this.func = func; } 

     public static implicit operator F<T>(T value) 
     { 
      return new F<T>(value); 
     } 

     public static implicit operator F<T>(Func<T> func) 
     { 
      return new F<T>(func); 
     } 

     public T Eval() 
     { 
      return this.func != null ? this.func() : this.value; 
     } 
} 

Bây giờ tôi chỉ có thể xác định các chức năng như:

public static void PrintLine(F<string> text, TextWriter writer) 
{ 
    if (writer != null) 
    { 
     writer.WriteLine(text.Eval()); 
    } 
} 

và gọi nó là cả với một chức năng hoặc một giá trị.

Trả lời

1

Hai báo cáo này hoàn toàn khác nhau. Một là định nghĩa một hàm, trong khi câu lệnh kia là một câu lệnh. Việc khó hiểu cú pháp sẽ phức tạp hơn nhiều.

() => "SomeText" //this is a function 

"SomeText" //this is a string 
+0

Tôi đồng ý. Trong trường hợp thêm quá tải, sẽ không thể nói sự khác biệt [nếu như một phím tắt được hỗ trợ]. – Krisc

3

Bạn có thể sử dụng một quá tải: -

public static void PrintLine(string text, TextWriter writer) 
{ 
    PrintLine(() => text, writer); 
} 
+1

Điều đó đánh bại mục đích. Các đối số cho quá tải của PrintLine bạn đã xác định được đánh giá không có vấn đề gì. Việc thêm lambda bên trong chỉ phục vụ cho việc ngăn chặn cuộc gọi không cần thiết. –

+0

Không chỉ vậy, bạn phải tạo quá tải 2^n cho tham số n. – Rauhotz

+2

Tôi nghĩ rằng tôi hiểu lầm các OP cần. Hóa ra, việc thêm quá tải có thể thực sự là điều đúng đắn để làm, mặc dù tôi nghĩ rằng tôi sẽ đảo ngược chúng (có một người lấy một đại biểu gọi người đó lấy một giá trị, chứ không phải là cách khác). –

1

Bạn có thể viết một phương pháp mở rộng trên String để dán nó trong Bạn sẽ có thể viết "Một số văn bản" .PrintLine (Console.Out.); và có nó làm việc cho bạn.

Thật kỳ lạ, tôi đã thực hiện một số chơi với đánh giá lười biếng về biểu thức lambda vài tuần trước và blogged about it here.

+2

Trong blog của bạn, bạn đang nói rằng không có Func . Điều gì về System.Action? – Rauhotz

1

Trình biên dịch rất tốt khi phỏng đoán các loại, việc suy luận ý định không tốt. Một trong những điều phức tạp về tất cả các đường cú pháp mới trong C# 3 là chúng có thể dẫn đến sự nhầm lẫn về chính xác trình biên dịch thực hiện với chúng.

Hãy xem xét ví dụ của bạn:

() => "SomeText" 

Trình biên dịch thấy điều này và hiểu rằng bạn có ý định tạo ra một chức năng vô danh mà mất không tham số và trả về một loại System.String. Đây là tất cả suy ra từ biểu thức lambda bạn đã cho nó. Trong thực tế, lambda của bạn được biên soạn cho điều này:

delegate { 
    return "SomeText"; 
}; 

và nó là đại biểu cho chức năng ẩn danh mà bạn đang gửi đến PrintLine để thực thi.Nó luôn luôn quan trọng trong quá khứ nhưng bây giờ với LINQ, lambdas, khối lặp, tự động thực hiện các thuộc tính, trong số những thứ khác, điều quan trọng nhất là sử dụng một công cụ như .NET Reflector để xem mã của bạn sau khi nó được biên dịch để xem điều gì thực sự làm cho các tính năng đó hoạt động.

3

Tôi nghi ngờ rằng C# sẽ nhận được tính năng này, nhưng D có tính năng này. Những gì bạn đã vạch ra là một cách phù hợp để thực hiện đánh giá đối số lười biếng trong C# và có thể biên dịch rất giống với lazy trong D và bằng các ngôn ngữ chức năng thuần túy hơn.

Mọi thứ được xem xét, bốn ký tự phụ, cộng với không gian trắng tùy chọn, không phải là một mức giá đặc biệt lớn để trả cho độ phân giải quá tải rõ ràng và tính biểu cảm trong những gì đang trở thành một ngôn ngữ có kiểu gõ mạnh.

2

Thật không may, cú pháp xấu là tất cả những gì bạn có trong C#.

"hack bẩn" từ bản cập nhật không hoạt động, bởi vì nó không trì hoãn việc đánh giá tham số chuỗi: chúng được đánh giá trước khi được chuyển đến operator F<T>(T value).

So sánh PrintLine(() => string.Join(", ", names), myWriter) đến PrintLine(string.Join(", ", names), myWriter) Trong trường hợp đầu tiên, các chuỗi chỉ được nối nếu chúng được in; trong trường hợp thứ hai, các chuỗi được nối không có vấn đề gì: chỉ in ấn là có điều kiện. Nói cách khác, việc đánh giá không hề lười biếng chút nào.

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