Tại sao Chủ đề không được khuyến khích trong Tiện ích mở rộng phản ứng .NET?


111

Tôi hiện đang hiểu rõ về khung Reactive Extensions cho .NET và tôi đang làm việc theo cách của mình thông qua các tài nguyên giới thiệu khác nhau mà tôi đã tìm thấy (chủ yếu là http://www.introtorx.com )

Ứng dụng của chúng tôi liên quan đến một số giao diện phần cứng phát hiện các khung mạng, đây sẽ là IObservables của tôi, sau đó tôi có nhiều thành phần sẽ sử dụng các khung đó hoặc thực hiện một số cách biến đổi trên dữ liệu và tạo ra một loại khung mới. Cũng sẽ có các thành phần khác cần hiển thị mọi khung hình thứ n chẳng hạn. Tôi tin rằng Rx sẽ hữu ích cho ứng dụng của chúng tôi, tuy nhiên tôi đang gặp khó khăn với các chi tiết triển khai cho giao diện IObserver.

Hầu hết (nếu không phải tất cả) các tài nguyên tôi đã đọc đều nói rằng tôi không nên tự triển khai giao diện IObservable mà hãy sử dụng một trong các hàm hoặc lớp được cung cấp. Từ nghiên cứu của tôi, có vẻ như việc tạo một Subject<IBaseFrame>sẽ cung cấp cho tôi những gì tôi cần, tôi sẽ có một luồng duy nhất của mình đọc dữ liệu từ giao diện phần cứng và sau đó gọi hàm OnNext của phiên bản của tôi Subject<IBaseFrame>. Sau đó, các thành phần IObserver khác nhau sẽ nhận được thông báo từ Chủ thể đó.

Sự bối rối của tôi đến từ lời khuyên đưa ra trong phụ lục của hướng dẫn này , nơi nó nói:

Tránh sử dụng các loại chủ đề. Rx thực sự là một mô hình lập trình chức năng. Sử dụng các chủ thể có nghĩa là chúng ta hiện đang quản lý trạng thái, trạng thái có khả năng thay đổi. Việc đối phó với cả trạng thái đột biến và lập trình không đồng bộ cùng một lúc là rất khó để làm đúng. Hơn nữa, nhiều toán tử (phương thức gia hạn) đã được viết cẩn thận để đảm bảo duy trì thời gian tồn tại chính xác và nhất quán của các đăng ký và trình tự; khi bạn giới thiệu các môn học, bạn có thể phá vỡ điều này. Các bản phát hành trong tương lai cũng có thể bị giảm hiệu suất đáng kể nếu bạn sử dụng các đối tượng một cách rõ ràng.

Ứng dụng của tôi khá quan trọng về hiệu suất, rõ ràng là tôi sẽ kiểm tra hiệu suất của việc sử dụng các mẫu Rx trước khi nó đi vào mã sản xuất; tuy nhiên, tôi lo lắng rằng tôi đang làm điều gì đó đi ngược lại tinh thần của khung Rx bằng cách sử dụng lớp Chủ đề và phiên bản trong tương lai của khung sẽ ảnh hưởng đến hiệu suất.

Có cách nào tốt hơn để làm những gì tôi muốn không? Chuỗi thăm dò phần cứng sẽ chạy liên tục cho dù có bất kỳ người quan sát nào hay không (bộ đệm HW sẽ sao lưu ngược lại), vì vậy đây là một chuỗi rất nóng. Sau đó, tôi cần chuyển các khung đã nhận được cho nhiều người quan sát.

Bất kỳ lời khuyên sẽ được đánh giá rất cao.


1
Nó thực sự giúp ích cho sự hiểu biết của tôi về chủ đề này, tôi chỉ đang tìm hiểu kỹ về cách sử dụng nó trong ứng dụng của mình. Tôi biết rằng chúng là điều đúng đắn - tôi có một hệ thống các thành phần được định hướng đẩy và tôi cần thực hiện tất cả các loại lọc và gọi trên chuỗi giao diện người dùng để hiển thị trong GUI cũng như đệm khung nhận cuối cùng, v.v. vv - Tôi chỉ cần đảm bảo rằng tôi làm điều đó ngay lần đầu tiên!
Anthony

Câu trả lời:


70

Ok, Nếu chúng ta bỏ qua những cách giáo điều của tôi và bỏ qua "đối tượng là tốt / xấu" cùng nhau. Chúng ta hãy nhìn vào không gian vấn đề.

Tôi cá là bạn có 1 trong 2 kiểu hệ thống mà bạn cần nhập vào.

  1. Hệ thống đưa ra một sự kiện hoặc cuộc gọi lại khi có tin nhắn
  2. Bạn cần thăm dò ý kiến ​​của hệ thống để xem có thông báo nào cần xử lý không

Đối với tùy chọn 1, dễ dàng, chúng tôi chỉ cần bọc nó bằng phương thức FromEvent thích hợp và chúng tôi đã hoàn tất. Đến quán rượu!

Đối với phương án 2, bây giờ chúng ta cần xem xét cách chúng ta thăm dò ý kiến ​​này và cách thực hiện điều này một cách hiệu quả. Ngoài ra khi chúng tôi nhận được giá trị, chúng tôi sẽ xuất bản nó như thế nào?

Tôi sẽ tưởng tượng rằng bạn sẽ muốn một chuỗi chuyên dụng để thăm dò ý kiến. Bạn sẽ không muốn một số lập trình viên khác đập vào ThreadPool / TaskPool và khiến bạn rơi vào tình trạng đói ThreadPool. Ngoài ra, bạn không muốn gặp rắc rối khi chuyển đổi ngữ cảnh (tôi đoán vậy). Vì vậy, giả sử chúng ta có chủ đề của riêng mình, chúng ta có thể sẽ có một số loại vòng lặp Trong khi / Ngủ mà chúng ta ngồi vào để thăm dò ý kiến. Khi kiểm tra tìm thấy một số thông báo chúng tôi xuất bản chúng. Tất cả những điều này nghe có vẻ hoàn hảo cho Observable.Create. Bây giờ chúng ta có lẽ không thể sử dụng vòng lặp While vì điều đó sẽ không cho phép chúng ta trả lại Đồ dùng một lần để cho phép hủy. May mắn thay, bạn đã đọc toàn bộ cuốn sách và hiểu rõ về lập lịch đệ quy!

Tôi tưởng tượng một cái gì đó như thế này có thể hoạt động. #NotTested

public class MessageListener
{
    private readonly IObservable<IMessage> _messages;
    private readonly IScheduler _scheduler;

    public MessageListener()
    {
        _scheduler = new EventLoopScheduler();

        var messages = ListenToMessages()
                                    .SubscribeOn(_scheduler)
                                    .Publish();

        _messages = messages;
        messages.Connect();
    }

    public IObservable<IMessage> Messages
    {
        get {return _messages;}
    }

    private IObservable<IMessage> ListenToMessages()
    {
        return Observable.Create<IMessage>(o=>
        {
                return _scheduler.Schedule(recurse=>
                {
                    try
                    {           
                        var messages = GetMessages();
                        foreach (var msg in messages)
                        {
                            o.OnNext(msg);
                        }   
                        recurse();
                    }
                    catch (Exception ex)
                    {
                        o.OnError(ex);
                    }                   
                });
        });
    }

    private IEnumerable<IMessage> GetMessages()
    {
         //Do some work here that gets messages from a queue, 
         // file system, database or other system that cant push 
         // new data at us.
         // 
         //This may return an empty result when no new data is found.
    }
}

Lý do tôi thực sự không thích Chủ đề, đó là trường hợp nhà phát triển thường không thực sự có thiết kế rõ ràng về vấn đề. Hack một chủ đề, chọc phá nó ở đây và ở khắp mọi nơi, và sau đó để cho các nhà phát triển hỗ trợ kém đoán tại WTF đang diễn ra. Khi bạn sử dụng các phương pháp Tạo / Tạo, v.v., bạn đang bản địa hóa các hiệu ứng trên chuỗi. Bạn có thể thấy tất cả trong một phương pháp và bạn biết không ai khác đang gây ra tác dụng phụ khó chịu. Nếu tôi nhìn thấy một trường chủ đề, bây giờ tôi phải tìm kiếm tất cả các vị trí trong một lớp mà nó đang được sử dụng. Nếu một số MFer tiết lộ một cách công khai, thì tất cả các cược sẽ tắt, ai biết được trình tự này đang được sử dụng như thế nào! Async / Concurrency / Rx là khó. Bạn không cần phải làm khó hơn bằng cách cho phép các tác dụng phụ và lập trình quan hệ nhân quả quay đầu bạn nhiều hơn.


10
Tôi chỉ đang đọc câu trả lời này bây giờ nhưng tôi cảm thấy tôi phải chỉ ra rằng tôi sẽ không bao giờ xem xét việc để lộ giao diện Chủ đề! Tôi đang sử dụng nó để cung cấp triển khai IObservable <> trong một lớp được niêm phong (để lộ IObservable <>). Tôi chắc chắn có thể thấy tại sao lộ đề <> giao diện sẽ là một Bad Thing ™
Anthony

này, xin lỗi vì phải dày, nhưng tôi thực sự không hiểu mã của bạn. ListenToMessages () và GetMessages () đang làm gì và quay lại?
user10479

1
Đối với dự án cá nhân của bạn @jeromerg, điều này có thể ổn. Tuy nhiên, theo kinh nghiệm của tôi, các nhà phát triển phải vật lộn với WPF, MVVM, thiết kế GUI đơn vị thử nghiệm và sau đó đưa vào Rx có thể khiến mọi thứ phức tạp hơn. Tôi đã thử mẫu BehaviourSubject-as-a-property. Tuy nhiên, tôi thấy rằng nó có thể được chấp nhận nhiều hơn đối với những người khác nếu chúng tôi sử dụng các thuộc tính INPC tiêu chuẩn và sau đó sử dụng một phương pháp Mở rộng đơn giản để chuyển đổi nó thành IObservable. Ngoài ra, bạn sẽ cần các ràng buộc WPF tùy chỉnh để làm việc với các đối tượng hành vi của mình. Bây giờ nhóm nghèo của bạn phải học WPF, MVVM, Rx và cả khuôn khổ mới của bạn.
Lee Campbell

2
@LeeCampbell, để nói về ví dụ mã của bạn, cách thông thường sẽ là MessageListener được xây dựng bởi hệ thống (bạn có thể đăng ký tên lớp bằng cách nào đó) và bạn được thông báo rằng hệ thống sau đó sẽ gọi OnCreate () và OnGoodbye (), và sẽ gọi message1 (), message2 () và message3 () khi tin nhắn được tạo. Có vẻ như messageX [123] sẽ gọi OnNext theo một chủ đề, nhưng có cách nào tốt hơn không?
James Moore

1
@JamesMoore vì những điều này dễ giải thích hơn nhiều với các ví dụ cụ thể. Nếu bạn biết một ứng dụng Android Mã nguồn mở sử dụng Rx và Chủ đề, thì có lẽ tôi có thể dành thời gian để xem liệu tôi có thể đưa ra cách tốt hơn hay không. Tôi hiểu rằng không có ích gì khi đứng trên bệ và nói Chủ thể là xấu. Nhưng tôi nghĩ những thứ như IntroToRx, RxCookbook và ReactiveTrader đều đưa ra nhiều mức độ ví dụ khác nhau về cách sử dụng Rx.
Lee Campbell

38

Nói chung, bạn nên tránh sử dụng Subject, tuy nhiên đối với công việc bạn đang làm ở đây, tôi nghĩ chúng hoạt động khá tốt. Tôi đã hỏi một câu hỏi tương tự khi tôi bắt gặp thông báo "tránh đối tượng" trong hướng dẫn Rx.

Để trích dẫn Dave Sexton (của Rxx)

"Chủ thể là các thành phần trạng thái của Rx. Chúng hữu ích khi bạn cần tạo một sự kiện giống như sự kiện có thể quan sát được dưới dạng một trường hoặc một biến cục bộ."

Tôi có xu hướng sử dụng chúng như là điểm vào Rx. Vì vậy, nếu tôi có một số mã cần nói 'điều gì đó đã xảy ra' (như bạn có), tôi sẽ sử dụng a Subjectvà call OnNext. Sau đó, hiển thị nó như một IObservableđể người khác đăng ký (bạn có thể sử dụng AsObservable()trên chủ đề của mình để đảm bảo không ai có thể truyền đến Chủ đề và làm mọi thứ rối tung lên).

Bạn cũng có thể đạt được điều này với sự kiện .NET và sử dụng FromEventPattern, nhưng nếu tôi chỉ biến sự kiện thành một sự kiện IObservable, tôi không thấy lợi ích của việc có một sự kiện thay vì một sự kiện Subject(có thể có nghĩa là tôi đang thiếu có gì đó ở đây)

Tuy nhiên, những gì bạn nên tránh khá mạnh là đăng ký IObservablevới a Subject, tức là không chuyển a Subjectvào IObservable.Subscribephương thức.


Tại sao bạn cần trạng thái? Như đã trình bày trong câu trả lời của tôi, nếu bạn chia nhỏ vấn đề thành các phần riêng biệt, bạn không thực sự phải quản lý trạng thái. Chủ đề không nên được sử dụng trong trường hợp này.
casperOne

8
@casperOne Bạn không cần trạng thái bên ngoài Chủ đề <T> hoặc sự kiện (cả hai đều có bộ sưu tập những thứ cần gọi, người quan sát hoặc người xử lý sự kiện). Tôi chỉ thích sử dụng Chủ đề hơn nếu lý do duy nhất để thêm sự kiện là kết hợp nó với FromEventPattern. Ngoài sự thay đổi trong sơ đồ ngoại lệ, điều này có thể quan trọng đối với bạn, tôi không thấy lợi ích khi tránh Chủ đề theo cách này. Một lần nữa, tôi có thể thiếu một thứ khác ở đây mà sự kiện thích hợp hơn với Chủ đề. Việc đề cập đến trạng thái chỉ là một phần của câu trích dẫn, và có vẻ tốt hơn nếu để nó vào trong. Có lẽ nó rõ ràng hơn nếu không có phần đó?
Wilka

@casperOne - nhưng bạn cũng không nên tạo một sự kiện chỉ để kết thúc nó với FromEventPattern. Đó rõ ràng là một ý tưởng khủng khiếp.
James Moore

3
Tôi đã giải thích câu trích dẫn của mình sâu hơn trong bài đăng blog này .
Dave Sexton

Tôi có xu hướng sử dụng chúng như là điểm vào Rx. Điều này đã đánh vào đầu tôi. Tôi gặp tình huống có một API khi được gọi sẽ tạo ra các sự kiện mà tôi muốn chuyển qua một đường ống xử lý phản ứng. Đối tượng là câu trả lời cho tôi, vì FromEventPattern dường như không tồn tại trong RxJava AFAICT.
scorpiodawg

31

Thông thường, khi bạn đang quản lý một Chủ đề, bạn thực sự chỉ đang hoàn thiện lại các tính năng đã có trong Rx và có lẽ không phải là cách mạnh mẽ, đơn giản và có thể mở rộng.

Khi bạn đang cố gắng điều chỉnh một số luồng dữ liệu không đồng bộ thành Rx (hoặc tạo luồng dữ liệu không đồng bộ từ luồng dữ liệu hiện không đồng bộ), các trường hợp phổ biến nhất thường là:

  • Nguồn dữ liệu là một sự kiện : Như Lee nói, đây là trường hợp đơn giản nhất: sử dụng FromEvent và đi đến quán rượu.

  • Nguồn dữ liệu là từ một hoạt động đồng bộ và bạn muốn cập nhật được thăm dò ý kiến , (ví dụ: dịch vụ web hoặc lệnh gọi cơ sở dữ liệu): Trong trường hợp này, bạn có thể sử dụng cách tiếp cận được đề xuất của Lee hoặc đối với các trường hợp đơn giản, bạn có thể sử dụng một cái gì đó như Observable.Interval.Select(_ => <db fetch>). Bạn có thể muốn sử dụng DistinctUntilChanged () để ngăn xuất bản các bản cập nhật khi không có gì thay đổi trong dữ liệu nguồn.

  • Nguồn dữ liệu là một số loại api không đồng bộ gọi lệnh gọi lại của bạn : Trong trường hợp này, hãy sử dụng Observable.Create để kết nối lệnh gọi lại của bạn để gọi OnNext / OnError / OnComplete trên trình quan sát.

  • Nguồn dữ liệu là lệnh gọi chặn cho đến khi có dữ liệu mới (ví dụ: một số thao tác đọc socket đồng bộ): Trong trường hợp này, bạn có thể sử dụng Observable.Create để bọc mã bắt buộc đọc từ socket và xuất bản lên Observer. khi dữ liệu được đọc. Điều này có thể tương tự như những gì bạn đang làm với Chủ đề.

Việc sử dụng Observable.Create so với việc tạo một lớp quản lý Chủ thể tương đối tương đương với việc sử dụng từ khóa lợi nhuận so với việc tạo toàn bộ lớp thực hiện IEnumerator. Tất nhiên, bạn có thể viết IEnumerator để trở thành một công dân sạch sẽ và tốt như mã lợi nhuận, nhưng cái nào được đóng gói tốt hơn và cho cảm giác thiết kế gọn gàng hơn? Điều này cũng đúng với Observable.Create và quản lý Đối tượng.

Observable.Create cung cấp cho bạn một mô hình rõ ràng để thiết lập lười biếng và chia nhỏ gọn gàng. Làm thế nào để bạn đạt được điều này với một lớp học bao gồm một Chủ đề? Bạn cần một số loại phương thức Bắt đầu ... làm thế nào để bạn biết khi nào gọi nó? Hay bạn chỉ luôn bắt đầu nó, ngay cả khi không có ai đang nghe? Và khi bạn hoàn thành, làm thế nào để bạn ngừng đọc từ socket / thăm dò cơ sở dữ liệu, v.v.? Bạn phải có một số loại phương thức Dừng và bạn vẫn phải có quyền truy cập không chỉ vào IObservable mà bạn đã đăng ký, mà còn cả lớp đã tạo Chủ đề ngay từ đầu.

Với Observable.Create, tất cả được gói gọn ở một nơi. Phần thân của Observable.Create không chạy cho đến khi ai đó đăng ký, vì vậy nếu không có ai đăng ký, bạn sẽ không bao giờ sử dụng tài nguyên của mình. Và Observable.Create trả về một Disposable có thể tắt sạch tài nguyên / lệnh gọi lại của bạn, v.v. - điều này được gọi khi Người quan sát hủy đăng ký. Vòng đời của các tài nguyên bạn đang sử dụng để tạo ra có thể quan sát được gắn liền với vòng đời của chính có thể quan sát.


1
Giải thích rất rõ ràng về Observable.Create. Cảm ơn bạn!
Evan Moran

1
Tôi vẫn gặp trường hợp khi tôi sử dụng một chủ thể, trong đó một đối tượng môi giới hiển thị đối tượng có thể quan sát được (giả sử nó chỉ là một thuộc tính có thể thay đổi). Các thành phần khác nhau sẽ gọi trình môi giới cho biết khi thuộc tính đó thay đổi (với một cuộc gọi phương thức) và phương thức đó thực hiện một OnNext. Người tiêu dùng đăng ký. Tôi nghĩ rằng tôi sẽ sử dụng BehaviorSubject trong trường hợp này, điều đó có phù hợp không?
Frank Schwieterman,

1
Nó phụ thuộc vào tình hình. Thiết kế Rx tốt có xu hướng chuyển đổi hệ thống theo kiến ​​trúc không đồng bộ / phản ứng. Có thể khó tích hợp một cách rõ ràng các thành phần nhỏ của mã phản ứng với một hệ thống có thiết kế bắt buộc. Giải pháp hỗ trợ băng tần là sử dụng Chủ thể để biến các hành động mệnh lệnh (lệnh gọi hàm, tập thuộc tính) thành các sự kiện có thể quan sát được. Sau đó, bạn sẽ có rất ít mã phản ứng và không có "aha!" chốc lát. Thay đổi thiết kế để mô hình hóa luồng dữ liệu và phản ứng với nó thường mang lại một thiết kế tốt hơn, nhưng đó là một sự thay đổi có tính lan tỏa và đòi hỏi sự thay đổi tư duy và sự tham gia của nhóm.
Niall Connaughton

1
Tôi sẽ nói ở đây (vì Rx chưa có kinh nghiệm) rằng: Bằng cách sử dụng Đối tượng, bạn có thể bước vào thế giới của Rx trong một ứng dụng bắt buộc đã phát triển và từ từ chuyển đổi nó. Ngoài ra, để có được những kinh nghiệm đầu tiên .... và chắc chắn sau này hãy thay đổi mã của bạn về cách nó đáng lẽ phải có từ đầu (lol). Nhưng để bắt đầu, tôi nghĩ rằng nó có thể đáng giá khi sử dụng các đối tượng.
Robetto

9

Văn bản khối được trích dẫn giải thích khá nhiều lý do tại sao bạn không nên sử dụng Subject<T>, nhưng nói một cách đơn giản hơn, bạn đang kết hợp các chức năng của trình quan sát và có thể quan sát, đồng thời đưa một số loại trạng thái vào giữa (cho dù bạn đang đóng gói hay mở rộng).

Đây là nơi bạn gặp rắc rối; những trách nhiệm này nên tách biệt và khác biệt với nhau.

Điều đó nói rằng, trong trường hợp cụ thể của bạn , tôi khuyên bạn nên chia mối quan tâm của mình thành các phần nhỏ hơn.

Đầu tiên, bạn có luồng của mình đang nóng và luôn theo dõi phần cứng để biết các tín hiệu để nâng thông báo. Làm thế nào bạn sẽ làm điều này bình thường? Sự kiện . Vì vậy, hãy bắt đầu với điều đó.

Hãy xác định EventArgssự kiện của bạn sẽ kích hoạt.

// The event args that has the information.
public class BaseFrameEventArgs : EventArgs
{
    public BaseFrameEventArgs(IBaseFrame baseFrame)
    {
        // Validate parameters.
        if (baseFrame == null) throw new ArgumentNullException("IBaseFrame");

        // Set values.
        BaseFrame = baseFrame;
    }

    // Poor man's immutability.
    public IBaseFrame BaseFrame { get; private set; }
}

Bây giờ, lớp sẽ kích hoạt sự kiện. Lưu ý, đây có thể là một lớp tĩnh (vì bạn luôn có một luồng đang chạy theo dõi bộ đệm phần cứng) hoặc một thứ gì đó bạn gọi theo yêu cầu đăng ký với lớp đó . Bạn sẽ phải sửa đổi điều này khi thích hợp.

public class BaseFrameMonitor
{
    // You want to make this access thread safe
    public event EventHandler<BaseFrameEventArgs> HardwareEvent;

    public BaseFrameMonitor()
    {
        // Create/subscribe to your thread that
        // drains hardware signals.
    }
}

Vì vậy, bây giờ bạn có một lớp hiển thị một sự kiện. Có thể quan sát hoạt động tốt với các sự kiện. Nhiều đến mức có hỗ trợ hạng nhất để chuyển đổi các luồng sự kiện (hãy nghĩ về một luồng sự kiện như nhiều lần xuất hiện của một sự kiện) thành các IObservable<T>triển khai nếu bạn tuân theo mẫu sự kiện chuẩn, thông qua phương thức tĩnhFromEventPattern trên Observablelớp .

Với nguồn của các sự kiện và FromEventPatternphương thức của bạn, chúng ta có thể tạo một lớpIObservable<EventPattern<BaseFrameEventArgs>> dễ dàng ( EventPattern<TEventArgs>lớp thể hiện những gì bạn thấy trong sự kiện .NET, đáng chú ý là một thể hiện có nguồn gốc từ EventArgsvà một đối tượng đại diện cho người gửi), như sau:

// The event source.
// Or you might not need this if your class is static and exposes
// the event as a static event.
var source = new BaseFrameMonitor();

// Create the observable.  It's going to be hot
// as the events are hot.
IObservable<EventPattern<BaseFrameEventArgs>> observable = Observable.
    FromEventPattern<BaseFrameEventArgs>(
        h => source.HardwareEvent += h,
        h => source.HardwareEvent -= h);

Tất nhiên, bạn muốn một IObservable<IBaseFrame>, nhưng điều đó thật dễ dàng, bằng cách sử dụng Selectphương thức mở rộng trên Observablelớp để tạo một phép chiếu (giống như bạn làm trong LINQ và chúng ta có thể tổng hợp tất cả những điều này trong một phương pháp dễ sử dụng):

public IObservable<IBaseFrame> CreateHardwareObservable()
{
    // The event source.
    // Or you might not need this if your class is static and exposes
    // the event as a static event.
    var source = new BaseFrameMonitor();

    // Create the observable.  It's going to be hot
    // as the events are hot.
    IObservable<EventPattern<BaseFrameEventArgs>> observable = Observable.
        FromEventPattern<BaseFrameEventArgs>(
            h => source.HardwareEvent += h,
            h => source.HardwareEvent -= h);

    // Return the observable, but projected.
    return observable.Select(i => i.EventArgs.BaseFrame);
}

7
Cảm ơn bạn đã phản hồi @casperOne, đây là cách tiếp cận ban đầu của tôi nhưng tôi cảm thấy "sai" khi thêm một sự kiện để tôi có thể kết thúc nó bằng Rx. Tôi hiện đang sử dụng ủy quyền (và vâng, tôi biết đó chính xác là sự kiện!) Để phù hợp với mã được sử dụng để tải và lưu cấu hình, điều này phải có thể xây dựng lại các đường ống thành phần và hệ thống ủy quyền đã cho tôi nhiều nhất Uyển chuyển. Rx đang khiến tôi đau đầu trong lĩnh vực này nhưng sức mạnh của mọi thứ khác trong khuôn khổ đang khiến việc giải quyết vấn đề cấu hình trở nên rất đáng giá.
Anthony

@Anthony Nếu bạn có thể làm cho mẫu mã của anh ấy hoạt động, thật tuyệt, nhưng như tôi đã nhận xét, điều đó chẳng có nghĩa lý gì. Đối với cảm giác "sai", tôi không biết tại sao việc chia nhỏ mọi thứ thành các phần hợp lý có vẻ "sai", nhưng bạn đã không cung cấp đủ chi tiết trong bài đăng gốc của mình để chỉ ra cách dịch điều này tốt nhất IObservable<T>thành không có thông tin về cách bạn. hiện đang báo hiệu với thông tin đó được đưa ra.
casperOne

@casperOne Theo ý kiến ​​của bạn, việc sử dụng Chủ thể có thích hợp cho Xe buýt tin nhắn / Công cụ tổng hợp sự kiện không?
kitsune

1
@kitsune Không, tôi không hiểu tại sao họ lại làm như vậy. Nếu bạn đang nghĩ "tối ưu hóa", bạn phải đặt câu hỏi liệu đó có phải là vấn đề hay không, bạn đã đo lường Rx là nguyên nhân của vấn đề chưa?
casperOne

2
Tôi đồng ý ở đây với casperOne rằng chia nhỏ các mối quan tâm là một ý kiến ​​hay. Tôi muốn chỉ ra rằng nếu bạn sử dụng mẫu Hardware to Event thành Rx, bạn sẽ mất ngữ nghĩa lỗi. Bất kỳ kết nối hoặc phiên bị mất nào, v.v. sẽ không được tiết lộ cho người tiêu dùng. Giờ đây, người tiêu dùng không thể quyết định xem họ muốn thử lại, ngắt kết nối, đăng ký chuỗi khác hay thứ gì khác.
Lee Campbell

0

Nói một cách tổng quát rằng Chủ thể không tốt khi sử dụng cho giao diện công cộng. Mặc dù chắc chắn là đúng, nhưng đây không phải là cách mà cách tiếp cận lập trình phản ứng nên giống như vậy, nó chắc chắn là một tùy chọn cải tiến / tái cấu trúc tốt cho mã cổ điển của bạn.

Nếu bạn có một thuộc tính bình thường với một trình truy cập tập hợp công khai và bạn muốn thông báo về những thay đổi, thì không có gì chống lại việc thay thế nó bằng BehaviorSubject. INPC hoặc các sự kiện bổ sung khác không được sạch sẽ như vậy và nó làm tôi kiệt sức. Với mục đích này, bạn có thể và nên sử dụng BehaviorSubjects làm thuộc tính chung thay vì thuộc tính bình thường và bỏ INPC hoặc các sự kiện khác.

Ngoài ra, giao diện Chủ đề làm cho người dùng giao diện của bạn nhận thức rõ hơn về chức năng của các thuộc tính của bạn và có nhiều khả năng đăng ký hơn thay vì chỉ nhận được giá trị.

Nó là tốt nhất để sử dụng nếu bạn muốn người khác lắng nghe / đăng ký các thay đổi của tài sản.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.