2009-07-31 34 views

Trả lời

33

Bạn có thể sử dụng IObservable dưới dạng sự kiện, thay thế mã cho thấy sự kiện có thuộc tính loại IObservable, nhưng đó không thực sự là điểm.

Có hai điều quan trọng để hiểu về IObservable:

  1. Nó hợp nhất hai khái niệm mà chúng ta không biết làm thế nào để thống nhất trước khi: hoạt động không đồng bộ (mà thường trả về một giá trị duy nhất) và các sự kiện (thường đi mãi mãi).

  2. Nó có thể composable. Không giống như các sự kiện CLR, IAsyncResult, hoặc INotifyCollectionChanged nó cho phép chúng ta xây dựng các sự kiện cụ thể từ các sự kiện chung và các hoạt động không đồng bộ.

Đây là một ví dụ tôi chạy vào công việc vào chiều nay.

Trong Silverlight có một số hiệu ứng bạn có thể áp dụng cho điều khiển hình ảnh không thể áp dụng cho điều khiển thông thường. Để tránh những giới hạn này khi nội dung của một điều khiển được thay đổi, tôi có thể đợi cho hình ảnh trực quan của nó được cập nhật và chụp ảnh màn hình. Sau đó, tôi muốn ẩn biểu diễn trực quan của nó, thay thế bằng ảnh chụp nhanh và áp dụng hiệu ứng hình ảnh cho hình ảnh. Bây giờ tôi có thể áp dụng hiệu ứng hình ảnh cho một điều khiển (giả sử nó không tương tác).

Chương trình này sẽ không quan trọng nhưng thực tế là chương trình không đồng bộ. Tôi phải chờ cho hai hoạt động không đồng bộ liên tục để hoàn thành trước khi tôi có thể áp dụng hiệu ứng cho hình ảnh:

  1. nội dung của điều khiển được thay đổi
  2. xuất hiện hình ảnh của điều khiển được cập nhật

Đây là cách tôi muốn giải quyết vấn đề này bằng cách sử dụng Rx:

// A content control is a control that displays content. That content can be 
// anything at all like a string or another control. Every content control contains 
// another control: a ContentPresenter. The ContentPresenter's job is to generate 
// a visual representation of the Content property. For example, if the Content property 
// of the ContentControl is a string, the ContentPresenter creates a TextBlock and inserts 
// the string into it. On the other hand if the Content property is another control the 
// ContentPresenter just inserts it into the visual tree directly. 
public class MyContentControl : ContentControl 
{ 
    // A subject implements both IObservable and IObserver. When IObserver methods 
    // are called, it forwards those calls to all of its listeners. 
    // As a result it has roughly the same semantics as an event that we can "raise." 
    private Subject<object> contentChanged = new Subject<object>(); 

    // This is a reference to the ContentPresenter in the ContentControl's template 
    private ContentPresenter contentPresenter; 

    // This is a reference to the Image control within ContentControl's template. It is displayed on top of the ContentPresenter and has a cool blur effect applied to it. 
    private Image contentImageControl; 

    public MyContentControl() 
    { 
     // Using Rx we can create specific events from general events. 
     // In this case I want to create a specific event ("contentImageChanged") which 
     // gives me exactly the data I need to respond and update the UI. 
     var contentImageChanged = 
     // get the content from the content changed event 
     from content in contentChanged 
     where content != null 
     // Wait for the ContentPresenter's visual representation to update. 
     // ContentPresenter is data bound to the Content property, so it will 
     // update momentarily. 
     from _ in contentPresenter.GetLayoutUpdated().Take(1) 
     select new WritableBitmap(contentPresenter, new TranslateTransform()); 

     contentImageChanged.Subscribe(
     contentImage => 
     { 
      // Hide the content presenter now that we've taken a screen shot    
      contentPresenter.Visibility = Visibility.Collapsed; 

      // Set the image source of the image control to the snapshot 
      contentImageControl.ImageSource = contentImage; 
     }); 
    } 

    // This method is invoked when the Content property is changed. 
    protected override OnContentChanged(object oldContent, object newContent) 
    { 
     // show the content presenter before taking screenshot 
     contentPresenter.Visibility = Visibility.Visible; 

     // raise the content changed "event" 
     contentChanged.OnNext(newContent); 

     base.OnContentChanged(oldContent, newContent); 
    } 
} 

Ví dụ này đặc biệt đơn giản vì chỉ có hai thao tác liên tiếp đến chuỗi. Ngay cả trong ví dụ đơn giản này, chúng ta có thể thấy rằng Rx bổ sung giá trị. Không có nó, tôi sẽ phải sử dụng các biến trạng thái để đảm bảo các sự kiện được kích hoạt theo một thứ tự nhất định. Tôi cũng sẽ phải viết một số mã khá xấu xí để giải thích tách khỏi sự kiện LayoutUpdated.

Khi bạn lập trình với Rx, mẹo là suy nghĩ "Tôi muốn cung cấp khung làm việc nào cho sự kiện?" và sau đó tạo nó. Chúng tôi được đào tạo để suy nghĩ về các sự kiện đơn giản, điều khiển đầu vào ("di chuột", "mouseclick", "keyup", v.v ...). Tuy nhiên, không có sự kiện lý do nào không thể phức tạp và cụ thể đối với ứng dụng của bạn ("GoogleMsdnMashupStockDataArrived", "DragStarting" và "ImageContentChanged"). Khi bạn cấu trúc chương trình của bạn theo cách này (tạo chính xác sự kiện tôi cần và sau đó trả lời bằng cách thay đổi trạng thái), bạn sẽ thấy rằng chúng có ít lỗi trạng thái hơn, được sắp xếp nhiều hơn và hoàn toàn tự mô tả hơn.

OK? :-)

+0

ví dụ tuyệt vời (do đó +1) tuy nhiên tôi nghĩ rằng tôi thà nhìn thấy máy nhà nước, vì nó sẽ là một thời gian dài trước khi tất cả các lập trình viên hiểu Rx. Tôi không thể quyết định cho rằng các lập trình viên đăng bài không có một mức độ Comp Sci, nếu Rx là một bước quá xa ... –

3

Nó chỉ là phần mở rộng cho mô hình lập trình dựa trên sự kiện. Bạn tạo một cái gì đó mà thực hiện IObserver, và về cơ bản bạn đang nói "đây là những gì tôi muốn xảy ra khi một cái gì đó trong bộ sưu tập thay đổi". Theo cách đó, nó chỉ là một tiêu chuẩn hóa về những gì chúng ta đã làm với các sự kiện.

Chúng đang đẩy nó giống như là một khuôn mặt to lớn so với mẫu IEnumerable. IEnumerable là "pull", trong khi IObservable là "push".

Lợi thế duy nhất tôi thấy qua các sự kiện thẳng là giao diện chuẩn hóa. Tôi thấy một chồng chéo lớn với ObservableCollection ở đây mặc dù (và INotifyCollectionChanged). Có lẽ họ đang cố gắng áp dụng phương châm PERL với .NET: "có nhiều cách để làm điều đó".

+4

Scott câu hỏi là sau đó, Điều gì sẽ khung trông giống như nếu IObservable và IObserver đã được thực hiện trong phiên bản 1 của khuôn khổ này. Xử lý lỗi và lập trình sự kiện sẽ có thể được tổng hợp. Cách phổ biến để xử lý sự kiện Đồng bộ và đồng bộ. Tiện ích mở rộng song song sẽ sử dụng các loại IObservable. Nhưng điều chính là điều này sẽ có tất cả được composable từ đầu, mà sẽ có rất nhiều đơn giản hóa rất nhiều mặt hàng đó là khó khăn nếu không gần như không thể bây giờ. (Đơn vị kiểm tra Giao diện người dùng không đồng bộ là một trong số đó xuất hiện trong đầu của tôi) – DouglasH

+1

@Doughlas, nhưng chúng tôi không thể viết lại lịch sử, do đó, quistion không phải là "theOldWay hoặc Rx", nhưng thay vì "theOldWay OR (theOldWay và Rx) " –

+0

Ưu điểm chính của IObservable là khả năng kết hợp của nó. Vì bạn có một bộ sưu tập lớn các toán tử được xây dựng, dựa trên giao diện IObservable , bạn có thể xây dựng các tương tác rất phức tạp theo cách rất đơn giản. Với các sự kiện thô bạn sẽ phải sử dụng để có rất nhiều trạng thái để theo dõi tất cả các sự kiện invocations. – ionoy

3

Tôi không chắc chắn trong những lợi thế, nhưng tôi thấy những khác biệt sau sự kiện cổ điển NET:

lỗi thông báo

sự kiện cổ điển sẽ đòi hỏi một sự kiện riêng biệt cho điều này, hoặc một EventArgs với một thuộc tính Error cần được kiểm tra.

end-of-thông báo thông báo

sự kiện cổ điển sẽ đòi hỏi một sự kiện riêng biệt cho điều này hoặc một lớp EventArgs với một tài sản mà cần phải được kiểm tra Final.

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