2012-03-23 20 views
6

Tôi có một tình huống mà tôi cần phải tạo ra một vài đại biểu vô danh tương tự. Dưới đây là ví dụ:Kỹ thuật C# để tạo các đại biểu ẩn danh chia sẻ cùng một biến đóng cửa

public void Foo(AnotherType theObj) 
{ 
    var shared = (SomeType)null; 

    theObj.LoadThing +=() => 
    { 
     if(shared == null) 
      shared = LoadShared(); 

     return shared.Thing; 
    }; 

    theObj.LoadOtherThing +=() => 
    { 
     if(shared == null) 
      shared = LoadShared(); 

     return shared.OtherThing; 
    }; 

    // more event handlers here... 
} 

Sự cố tôi gặp phải là mã của tôi không phải là DRY. Nội dung của mỗi trình xử lý sự kiện tương tự như EXTREMELY và có thể dễ dàng được tham số hóa thành phương thức nhà máy. Điều duy nhất ngăn cản tôi làm điều đó là mỗi đại biểu cần chia sẻ tham chiếu đến biến số shared. Tôi không thể chuyển shared vào phương thức nhà máy với từ khóa ref, dưới dạng you can't create a closure around a ref varaiable. Bất kỳ ý tưởng?

+0

Bạn đang trả lại thứ gì đó từ bộ xử lý ngay cả? Tôi nghĩ đó là một điều thực sự không bình thường, đặc biệt là bạn có thể thêm nhiều người xử lý hơn vào một sự kiện và chỉ giá trị của người cuối cùng mới được sử dụng. Có lẽ một tài sản đại biểu sẽ phù hợp hơn cho bạn? – svick

+0

Một thuộc tính ủy nhiệm có thể được gọi ra bên ngoài lớp, đó không phải là thứ tôi muốn.Ngữ nghĩa 'sự kiện', nơi mọi người có thể đính kèm, nhưng chỉ có lớp có thể gọi, là những gì tôi cần trong trường hợp này. – FMM

+0

Tôi nghĩ rằng một tài sản ủy quyền chỉ viết phù hợp hơn. Mặc dù nó vẫn còn khá lạ. – svick

Trả lời

18

Không có vấn đề mà không thể được giải quyết bằng cách thêm trừu tượng hơn. (*)

Mẫu bạn lặp đi lặp lại nhiều lần là mẫu "tải chậm". Đó là mô hình rất có khả năng bị bắt trong một loại, và trên thực tế, nó đã được, trong phiên bản 4 của khuôn khổ. Tài liệu ở đây:

http://msdn.microsoft.com/en-us/library/dd642331.aspx

Sau đó bạn có thể làm điều gì đó như:

public void Foo(AnotherType theObj) 
{ 
    var shared = new Lazy<SomeType>(()=>LoadShared()); 
    theObj.LoadThing +=() => shared.Value.Thing; 
    theObj.LoadOtherThing +=() => shared.Value.OtherThing; 
    // more event handlers here... 
} 

Và có bạn đi. Lần đầu tiên shared.Value được truy cập giá trị được tải; mỗi lần tiếp theo, giá trị được lưu trong bộ nhớ cache được sử dụng. Tiền thưởng thêm: đây thậm chí là threadsafe nên giá trị chia sẻ được truy cập trên nhiều chủ đề. (Xem tài liệu để biết chi tiết về chính xác những gì chúng tôi đảm bảo về an toàn luồng.)


(*) Ngoại trừ tất nhiên cho vấn đề "Tôi có quá trừu tượng".

+0

Nice .. không biết về Lazy! – Arcturus

+0

Ngoạn mục! TIL về lớp 'Lazy'. – FMM

+0

(**) và cũng ngoại trừ, tất nhiên, sự cố tạm dừng. – phoog

1

Thêm một lớp bổ sung là vô hướng. Tạo một lớp chỉ là một wrapper cho các dữ liệu mà bạn muốn giữ:

public class MyPointer<T> 
{ 
    public T Value{get;set;} 
} 

New lập một MyPointer<SomeType> vào lúc bắt đầu của phương pháp này và vượt qua nó vào nhà máy. Bây giờ tham chiếu MyPointer được sao chép theo giá trị, do đó bạn không thể thay đổi phiên bản MyPointer, nhưng bạn có thể thay đổi Value trong từng phương pháp nhà máy và được phản ánh ở nơi khác.

1

Làm thế nào về:

public void Foo(AnotherType theObj) 
{ 
    var shared = (SomeType)null; 

    Action handler =() => 
    { 
     if(shared == null) 
      shared = LoadShared(); 

     return Shared.Thing; 
    }; 

    theObj.LoadThing += handler; 
    theObj.LoadOtherThing += handler; 

    // more event handlers here... 
} 

Bạn cũng có thể đặt các hành động trong một phương pháp và thông qua một tham số:

public void Foo(AnotherType theObj) 
{ 
    var shared = (SomeType)null; 

    theObj.LoadThing +=() => Handle("LoadThing"); 
    theObj.LoadOtherThing +=() => Handle("LoadOtherThing"); 

    // more event handlers here... 
} 

private T Handle<T>(T returnParameter) 
{ 
    if(shared == null) 
     shared = LoadShared(); 

    return returnParameter; 
} 
+0

Điều đó trả về 'Shared.Thing' cho cả hai trình xử lý. – recursive

+0

Thay đổi ví dụ thứ hai để phản ánh rằng – Arcturus

+0

Tôi vẫn không nghĩ rằng nó hoạt động. 'T' luôn luôn là' chuỗi', và 'returnParameter' chỉ là' "LoadThing" 'và' "LoadOtherThing" ', tương ứng, không phải là một thuộc tính từ' shared'. – recursive

1

Tôi thích phương pháp của Eric tốt hơn, nhưng đây là một cách tiếp cận khác được sinh ra từ một số loại mê sảng nhiên liệu lisp mà tôi nghĩ.

var LoaderMaker = (Func<SomeType, int> thingGetter) => { 
    return() => { 
     if(shared == null) shared = LoadShared(); 
     return thingGetter(shared); 
    }; 
}; 

theObj.LoadThing = LoaderMaker(t => t.Thing); 
theObj.LoadOtherThing = LoaderMaker(t => t.OtherThing); 
+0

Vấn đề ở đây là: nếu Thing và OtherThing không phải là số nguyên? –

+0

Bạn thay thế 'int' bằng bất kỳ loại nào chúng thực sự là. – recursive

+0

và nếu một là một int và một là một chuỗi? –

1

Tôi đề nghị đặt mã lặp lại trong một hàm riêng biệt.

public void Foo(AnotherType theObj) 
{ 
    var shared = (SomeType)null; 

    Func<SomeType> getShared =() => 
    { 
     if(shared == null) 
      shared = LoadShared(); 
     return shared; 
    }; 

    // Or more compact 
    Func<SomeType> getShared =() => shared ?? (shared = LoadShared()); 

    theObj.LoadThing +=() => getShared().Thing; 

    theObj.LoadOtherThing +=() => getShared().OtherThing; 

    // more event handlers here... 
} 
Các vấn đề liên quan