C# 在反应式编程中使用主题的替代方案?

C# 在反应式编程中使用主题的替代方案?,c#,system.reactive,C#,System.reactive,在反应式编程中,通常不赞成使用主题类型。在以下情况下,我使用主题来允许在创建通知的基础源之前订阅通知。是否有其他方法不使用主题 using System; using System.Reactive; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Threading.Tasks; using Windows.Networking.Sockets; using Windows.Storage.Str

在反应式编程中,通常不赞成使用
主题
类型。在以下情况下,我使用
主题
来允许在创建通知的基础源之前订阅通知。是否有其他方法不使用
主题

using System;
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;

class Program
{
    public static void Main()
    {
        var socket = new ObservableMessageWebSocket();

        socket.Messages.Subscribe(Print); // Caller is allowed to subscribe before connect

        var uri = new Uri("ws://mydomain.com/messages");
        socket.ConnectAsync(uri).Wait(); // Caller is allowed to connect after subscribe

        Console.ReadLine();
    }

    public static void Print(string message)
    {
        Console.WriteLine(message);
    }
}

class ObservableMessageWebSocket
{
    // Is there a way to get rid of this Subject?
    private readonly Subject<string> subject = new Subject<string>();

    private MessageWebSocket webSocket;

    public IObservable<string> Messages => subject;

    public async Task ConnectAsync(Uri uri)
    {
        webSocket = new MessageWebSocket();

        webSocket.Control.MessageType = SocketMessageType.Utf8;

        Observable
            .FromEventPattern<MessageWebSocketMessageReceivedEventArgs>(webSocket, nameof(webSocket.MessageReceived))
            .Select(ReadString)
            .Subscribe(subject);

        await webSocket.ConnectAsync(uri);
    }

    private static string ReadString(EventPattern<MessageWebSocketMessageReceivedEventArgs> pattern)
    {
        using (var reader = pattern.EventArgs.GetDataReader())
        {
            reader.UnicodeEncoding = UnicodeEncoding.Utf8;

            return reader.ReadString(reader.UnconsumedBufferLength);
        }
    }
}
下面的代码也不起作用。同样的症状

class ObservableMessageWebSocket
{
    private MessageWebSocket WebSocket { get; }

    public IObservable<string> Messages { get; }

    public ObservableMessageWebSocket()
    {
        WebSocket = new MessageWebSocket();
        WebSocket.Control.MessageType = SocketMessageType.Utf8;
        Messages = Observable.Create<string>(o => Observable
            .FromEventPattern<MessageWebSocketMessageReceivedEventArgs>(WebSocket, nameof(WebSocket.MessageReceived))
            .Select(ReadString)
            .Subscribe(o));
    }

    private static string ReadString(EventPattern<MessageWebSocketMessageReceivedEventArgs> pattern)
    {
        using (var reader = pattern.EventArgs.GetDataReader())
        {
            reader.UnicodeEncoding = UnicodeEncoding.Utf8;

            return reader.ReadString(reader.UnconsumedBufferLength);
        }
    }

    public async Task ConnectAsync(Uri uri)
    {
        await WebSocket.ConnectAsync(uri);
    }
}
类ObservaleMessageWebSocket
{
私有消息WebSocket WebSocket{get;}
公共IObservable消息{get;}
公共可观察消息WebSocket()
{
WebSocket=新消息WebSocket();
WebSocket.Control.MessageType=SocketMessageType.Utf8;
Messages=Observable.Create(o=>Observable
.FromEventPattern(WebSocket,名称(WebSocket.MessageReceived))
.选择(读取字符串)
.认购(o));
}
私有静态字符串ReadString(EventPattern模式)
{
使用(var reader=pattern.EventArgs.GetDataReader())
{
reader.unicodeincode=unicodeincode.Utf8;
返回reader.ReadString(reader.unsumedbufferlength);
}
}
公共异步任务ConnectAsync(Uri)
{
等待WebSocket.ConnectAsync(uri);
}
}
不知何故,下面的代码是有效的

class ObservableMessageWebSocket
{
    private MessageWebSocket WebSocket { get; }

    private event EventHandler<string> StringReceived;

    public IObservable<string> Messages { get; }

    public ObservableMessageWebSocket()
    {
        WebSocket = new MessageWebSocket();
        WebSocket.Control.MessageType = SocketMessageType.Utf8;
        WebSocket.MessageReceived += HandleEvent;
        Messages = Observable
            .FromEventPattern<string>(this, nameof(StringReceived))
            .Select(p => p.EventArgs);
    }

    private void HandleEvent(MessageWebSocket sender, MessageWebSocketMessageReceivedEventArgs args)
    {
        var handler = StringReceived;
        if (handler == null) return;
        string message;
        using (var reader = args.GetDataReader())
        {
            reader.UnicodeEncoding = UnicodeEncoding.Utf8;
            message= reader.ReadString(reader.UnconsumedBufferLength);
        }
        handler.Invoke(this, message);
    }

    public async Task ConnectAsync(Uri uri)
    {
        await WebSocket.ConnectAsync(uri);
    }
}
类ObservaleMessageWebSocket
{
私有消息WebSocket WebSocket{get;}
接收到私有事件EventHandler字符串;
公共IObservable消息{get;}
公共可观察消息WebSocket()
{
WebSocket=新消息WebSocket();
WebSocket.Control.MessageType=SocketMessageType.Utf8;
WebSocket.MessageReceived+=HandleEvent;
信息=可观察
.FromEventPattern(此,名称为(StringReceived))
.Select(p=>p.EventArgs);
}
私有void HandleEvent(MessageWebSocket发送者、MessageWebSocket MessageReceiveDeventargs参数)
{
var handler=StringReceived;
if(handler==null)返回;
字符串消息;
使用(var reader=args.GetDataReader())
{
reader.unicodeincode=unicodeincode.Utf8;
message=reader.ReadString(reader.unsumedbufferlength);
}
调用(这个,消息);
}
公共异步任务ConnectAsync(Uri)
{
等待WebSocket.ConnectAsync(uri);
}
}

对我来说,这三个似乎都很相似。为什么只有最后一个有效呢?

主题并不普遍都是坏的,我也不认为你使用它的方式有什么严重的问题。我将参考和对它们及其使用进行一些合理的讨论

鉴于此,我建议如下(同时删除所有字段和公开属性):

公共异步任务ConnectAsync(Uri) { webSocket=新消息webSocket(); webSocket.Control.MessageType=SocketMessageType.Utf8; var toReturn=可观测 .FromEventPattern(webSocket,名称(webSocket.MessageReceived)) .选择(读取字符串); 等待webSocket.ConnectAsync(uri); 回归回归; }

这样,如果有人调用
ConnectAsync
两次,他们可以得到单独的观察值

科目并不是普遍都不好,而且我认为你使用它的方式没有什么大问题。我将参考和对它们及其使用进行一些合理的讨论

鉴于此,我建议如下(同时删除所有字段和公开属性):

公共异步任务ConnectAsync(Uri) { webSocket=新消息webSocket(); webSocket.Control.MessageType=SocketMessageType.Utf8; var toReturn=可观测 .FromEventPattern(webSocket,名称(webSocket.MessageReceived)) .选择(读取字符串); 等待webSocket.ConnectAsync(uri); 回归回归; }

这样,如果有人调用
ConnectAsync
两次,他们可以得到单独的观察值

避开主题通常是个好主意。在代码中,您将主题直接暴露给调用代码。任何执行
((主题)socket.Messages).OnCompleted()的使用者将停止代码工作

您还将新建一个
WebSocket
,以后应该将其处理掉

有一种方法可以避开这个主题,让它表现得更好

试试这个:

public IObservable<string> Connect(Uri uri)
{
    return
        Observable
            .Using(
                () =>
                {
                    var webSocket = new MessageWebSocket();
                    webSocket.Control.MessageType = SocketMessageType.Utf8;
                    return webSocket;
                },
                webSocket =>
                    Observable
                        .FromAsync(() => webSocket.ConnectAsync(uri))
                        .SelectMany(u =>
                            Observable
                                .FromEventPattern<MessageWebSocketMessageReceivedEventArgs>(webSocket, nameof(webSocket.MessageReceived))
                                .SelectMany(pattern =>
                                    Observable
                                        .Using(
                                            () =>
                                            {
                                                var reader = pattern.EventArgs.GetDataReader();
                                                reader.UnicodeEncoding = UnicodeEncoding.UTF8;
                                                return reader;
                                            },
                                            reader => Observable.Return(reader.ReadString(reader.UnconsumedBufferLength))))));

}
公共IObservable连接(Uri) { 返回 可观察 .使用( () => { var webSocket=新消息webSocket(); webSocket.Control.MessageType=SocketMessageType.Utf8; 返回网袋; }, webSocket=> 可观察 .FromAsync(()=>webSocket.ConnectAsync(uri)) .选择多个(u=> 可观察 .FromEventPattern(webSocket,名称(webSocket.MessageReceived)) .SelectMany(模式=> 可观察 .使用( () => { var reader=pattern.EventArgs.GetDataReader(); reader.unicodeincode=unicodeincode.UTF8; 返回读取器; }, reader=>Observable.Return(reader.ReadString(reader.unsumedbufferlength()()())); }

以下是如何使用现有代码样式避免主题:

public IObservable<string> ConnectAsync(Uri uri)
{
    return
        Observable
            .Create<string>(async o =>
            {
                var webSocket = new MessageWebSocket();

                webSocket.Control.MessageType = SocketMessageType.Utf8;

                var subscription = Observable
                    .FromEventPattern<MessageWebSocketMessageReceivedEventArgs>(webSocket, nameof(webSocket.MessageReceived))
                    .Select(ReadString)
                    .Subscribe(o);

                await webSocket.ConnectAsync(uri);

                return subscription;
            });
}
public IObservable ConnectAsync(Uri)
{
返回
可观察
.Create(异步o=>
{
var webSocket=新消息webSocket();
public IObservable<string> Connect(Uri uri)
{
    return
        Observable
            .Using(
                () =>
                {
                    var webSocket = new MessageWebSocket();
                    webSocket.Control.MessageType = SocketMessageType.Utf8;
                    return webSocket;
                },
                webSocket =>
                    Observable
                        .FromAsync(() => webSocket.ConnectAsync(uri))
                        .SelectMany(u =>
                            Observable
                                .FromEventPattern<MessageWebSocketMessageReceivedEventArgs>(webSocket, nameof(webSocket.MessageReceived))
                                .SelectMany(pattern =>
                                    Observable
                                        .Using(
                                            () =>
                                            {
                                                var reader = pattern.EventArgs.GetDataReader();
                                                reader.UnicodeEncoding = UnicodeEncoding.UTF8;
                                                return reader;
                                            },
                                            reader => Observable.Return(reader.ReadString(reader.UnconsumedBufferLength))))));

}
public IObservable<string> ConnectAsync(Uri uri)
{
    return
        Observable
            .Create<string>(async o =>
            {
                var webSocket = new MessageWebSocket();

                webSocket.Control.MessageType = SocketMessageType.Utf8;

                var subscription = Observable
                    .FromEventPattern<MessageWebSocketMessageReceivedEventArgs>(webSocket, nameof(webSocket.MessageReceived))
                    .Select(ReadString)
                    .Subscribe(o);

                await webSocket.ConnectAsync(uri);

                return subscription;
            });
}
void Main()
{
    Connect(new Uri("https://stackoverflow.com/")).Subscribe(x => Console.WriteLine(x.Substring(0, 24)));
}

public IObservable<string> Connect(Uri uri)
{
    return
        Observable
            .Create<string>(async o =>
            {
                var webClient = new WebClient();

                webClient.UseDefaultCredentials = true;

                var subscription =
                    Observable
                        .Using(
                            () => new CompositeDisposable(webClient, Disposable.Create(() => Console.WriteLine("Disposed!"))),
                            _ =>
                                Observable
                                    .FromEventPattern<DownloadStringCompletedEventHandler, DownloadStringCompletedEventArgs>(
                                        h => webClient.DownloadStringCompleted += h, h => webClient.DownloadStringCompleted -= h)
                                    .Take(1))
                    .Select(x => x.EventArgs.Result)
                    .Subscribe(o);

                await webClient.DownloadStringTaskAsync(uri);

                return subscription;
            });
}