2009-09-10 28 views
11

Làm cách nào để triển khai cơ chế thực thi hoãn lại của riêng tôi trong C#?Thực thi hoãn lại trong C#

Vì vậy, ví dụ tôi có:

string x = DoFoo(); 

Có thể thực hiện một số phép thuật để DoFoo không thực hiện cho đến khi tôi "sử dụng" x?

Trả lời

17

Bạn có thể sử dụng lambdas/đại biểu:

Func<string> doit =() => DoFoo(); 
// - or - 
Func<string> doit = DoFoo; 

Sau đó bạn có thể gọi doit giống như một phương pháp:

string x = doit(); 

Tôi nghĩ gần nhất bạn có thể nhận được là một cái gì đó như thế này:

Lazy<string> x = DoFoo; 

string y = x; // "use" x 

Với một định nghĩa của Lazy<T> tương tự như sau (chưa được kiểm tra):

public class Lazy<T> 
{ 
    private readonly Func<T> func; 
    private bool hasValue; 
    private T value; 

    public Lazy(Func<T> func) 
    { 
     this.func = func; 
     this.hasValue = false; 
    } 

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

    public static implicit operator T(Lazy<T> lazy) 
    { 
     if (!lazy.hasValue) 
     { 
      lazy.value = lazy.func(); 
      lazy.hasValue = true; 
     } 
     return lazy.value; 
    } 
} 

Thật không may, có vẻ như các thuật toán kiểu suy luận của trình biên dịch có thể không tự động suy ra các loại của Func<T> và do đó không thể kết hợp nó với toán tử chuyển đổi ngầm. Chúng ta cần phải tuyên bố rõ ràng loại của đại biểu, mà làm cho câu lệnh gán tiết hơn:

// none of these will compile... 
Lazy<string> x = DoFoo; 
Lazy<string> y =() => DoFoo(); 
Lazy<string> z = delegate() { return DoFoo(); }; 

// these all work... 
Lazy<string> a = (Func<string>)DoFoo; 
Lazy<string> b = (Func<string>)(() => DoFoo()); 
Lazy<string> c = new Func<string>(DoFoo); 
Lazy<string> d = new Func<string>(() => DoFoo()); 
Lazy<string> e = new Lazy<string>(DoFoo); 
Lazy<string> f = new Lazy<string>(() => DoFoo); 
+0

Nhưng làm thế nào là "chuỗi x = doit()' "bất kỳ khác nhau từ" chuỗi 'x = DoFoo()' ", ngoài các lớp bổ sung của indirection? – LukeH

+0

Nó không phải là. Tôi đã không đọc được câu hỏi. Sẽ cải thiện câu trả lời trong một giây ... – dtb

+0

Điều này vẫn gọi là 'DoFoo()' trước khi x được "sử dụng". –

0

Tại sao không chỉ không gọi 'DoFoo()' cho đến khi bạn muốn?

- Chỉnh sửa

Ý tôi là, những gì bạn có nghĩa là "sử dụng"

Ví dụ, nếu bạn muốn nó được gọi khi 'ToString()' gọi, bạn có thể luôn luôn kế thừa lớp và thực hiện chức năng của bạn ở đó (nhưng đây sẽ là IMHO khá không trực quan).

1

Thay vì đi qua một chuỗi x, vượt qua một đại biểu rằng procures bạn một chuỗi

Func<String> fooFunc=()=>DoFoo(); 
0

Bạn khá mô tả nhiều LINQ trong hành động. Truy vấn LINQ mô tả cách lấy dữ liệu, nhưng dữ liệu được lấy ra (DoFunc được gọi) chỉ khi truy vấn được lặp lại. Cân nhắc xem bạn có thể thay đổi thiết kế của mình để chấp nhận IQueryable<string> nơi bạn cần một số string hay không.

3

Trong khi nó hơi bẩn bạn luôn có thể sử dụng từ khóa năng suất:

public IEnumerable<int> DoFoo() { 
    Console.WriteLine("doing foo"); 
    yield return 10; 
} 

[Test] 
public void TestMethod() 
{ 
    var x = DoFoo(); 
    Console.WriteLine("foo aquired?"); 
    Console.WriteLine(x.First()); 
} 
6

Một lựa chọn là sử dụng lớp Lazy<T>, trước đây từ thư viện mở rộng song song bây giờ là một phần của .Net Framework 4.0.

Nó cho phép bạn làm chậm xử lý dữ liệu một cách ý thức chủ đề.

+0

Tôi thích sử dụng một lớp hệ thống để làm như vậy. Tuy nhiên, hệ thống lớp không thực hiện điều hành ngầm, mà nhắc tôi tìm kiếm lý do tại sao. Tôi tìm thấy [this] (http://netvignettes.wordpress.com/2011/04/24/implicit-conversion-operators-are-bad/), loại giải thích tại sao. –

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