You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中如何通过接口结合回调实现WebSocket消息的发布与接收

解决方案:通过接口+回调实现WebSocket消息的发布与接收

Hey there, let's break this down step by step—this is a common pattern for decoupling publishers and subscribers using interfaces and callbacks in C#, perfect for your WebSocket scenario. We'll make sure class B only interacts with class A via the contract interface I, keeping everything loosely coupled and maintainable.

1. Define the Contract Interfaces & Delegates

First, we need to lay out the core contracts: a callback signature (to define how messages are received) and the main interface that class A will implement. This ensures class B never depends on class A directly—only on the interface.

// Delegate that defines the shape of our message-received callback
public delegate void WebSocketMessageReceivedHandler(string message);

// The contract interface: this is the only way class B can communicate with class A
public interface IWebSocketPublisher
{
    // Method for subscribers to register their callback
    void RegisterMessageCallback(WebSocketMessageReceivedHandler callback);
    
    // Optional but recommended: Method to unregister callbacks when done
    void UnregisterMessageCallback(WebSocketMessageReceivedHandler callback);
}

2. Implement Class A (WebSocket Publisher)

Class A will handle the WebSocket connection logic, implement IWebSocketPublisher, and trigger all registered callbacks whenever a new message is published/received.

using System.Net.WebSockets;
using System.Text;

public class WebSocketPublisher : IWebSocketPublisher
{
    // Thread-safe list to store registered callbacks (supports multiple subscribers)
    private readonly List<WebSocketMessageReceivedHandler> _callbacks = new();
    private readonly object _lock = new();

    // Example method to start the WebSocket connection and listen for messages
    public async Task StartWebSocketClient(string wsUrl)
    {
        using var client = new ClientWebSocket();
        await client.ConnectAsync(new Uri(wsUrl), CancellationToken.None);

        var buffer = new byte[1024 * 4];
        while (client.State == WebSocketState.Open)
        {
            var receiveResult = await client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
            
            if (receiveResult.MessageType == WebSocketMessageType.Text)
            {
                string incomingMessage = Encoding.UTF8.GetString(buffer, 0, receiveResult.Count);
                // Trigger all registered callbacks with the new message
                TriggerMessageCallbacks(incomingMessage);
            }
        }
    }

    private void TriggerMessageCallbacks(string message)
    {
        // Lock to avoid race conditions if callbacks are added/removed mid-execution
        lock (_lock)
        {
            // Use ToList() to iterate over a copy, preventing errors if the list changes during iteration
            foreach (var callback in _callbacks.ToList())
            {
                callback.Invoke(message);
            }
        }
    }

    // Implement interface methods to manage callbacks
    public void RegisterMessageCallback(WebSocketMessageReceivedHandler callback)
    {
        if (callback == null) throw new ArgumentNullException(nameof(callback));
        lock (_lock)
        {
            _callbacks.Add(callback);
        }
    }

    public void UnregisterMessageCallback(WebSocketMessageReceivedHandler callback)
    {
        lock (_lock)
        {
            _callbacks.Remove(callback);
        }
    }
}

3. Implement Class B (Message Subscriber)

Class B will depend solely on IWebSocketPublisher, register its own custom callback logic to process incoming messages, and clean up when it's done.

public class MessageSubscriber : IDisposable
{
    private readonly IWebSocketPublisher _publisher;

    // Inject the interface (dependency injection is ideal here for even looser coupling)
    public MessageSubscriber(IWebSocketPublisher publisher)
    {
        _publisher = publisher;
        // Register our callback when the subscriber is initialized
        _publisher.RegisterMessageCallback(HandleIncomingMessage);
    }

    // The callback method that runs when a message is received
    private void HandleIncomingMessage(string message)
    {
        Console.WriteLine($"Class B processed message: {message}");
        // Add your custom business logic here (e.g., parse the message, update UI, etc.)
    }

    // Clean up the callback to prevent memory leaks
    public void Dispose()
    {
        _publisher.UnregisterMessageCallback(HandleIncomingMessage);
    }
}

4. Putting It All Together

Here's how you'd use these classes in your application:

// Initialize the publisher (class A) behind the interface
IWebSocketPublisher publisher = new WebSocketPublisher();

// Initialize the subscriber (class B) with the interface instance
var subscriber = new MessageSubscriber(publisher);

// Start the WebSocket connection (run in a background thread to avoid blocking)
_ = publisher.StartWebSocketClient("wss://your-websocket-endpoint.com");

// When you're done with the subscriber, dispose it to clean up
// subscriber.Dispose();

Alternative: Interface-Based Callbacks

If your team prefers using interfaces over delegates for stronger typing, you can adjust the pattern like this:

// Callback interface that subscribers must implement
public interface IWebSocketMessageHandler
{
    void OnMessageReceived(string message);
}

// Updated contract interface
public interface IWebSocketPublisher
{
    void RegisterHandler(IWebSocketMessageHandler handler);
    void UnregisterHandler(IWebSocketMessageHandler handler);
}

// Updated Class A implementation
public class WebSocketPublisher : IWebSocketPublisher
{
    private readonly List<IWebSocketMessageHandler> _handlers = new();
    private readonly object _lock = new();

    // ... (WebSocket logic remains mostly the same)
    private void TriggerMessageCallbacks(string message)
    {
        lock (_lock)
        {
            foreach (var handler in _handlers.ToList())
            {
                handler.OnMessageReceived(message);
            }
        }
    }

    public void RegisterHandler(IWebSocketMessageHandler handler)
    {
        if (handler == null) throw new ArgumentNullException(nameof(handler));
        lock (_lock)
        {
            _handlers.Add(handler);
        }
    }

    public void UnregisterHandler(IWebSocketMessageHandler handler)
    {
        lock (_lock)
        {
            _handlers.Remove(handler);
        }
    }
}

// Updated Class B that implements the callback interface
public class MessageSubscriber : IWebSocketMessageHandler, IDisposable
{
    private readonly IWebSocketPublisher _publisher;

    public MessageSubscriber(IWebSocketPublisher publisher)
    {
        _publisher = publisher;
        _publisher.RegisterHandler(this);
    }

    public void OnMessageReceived(string message)
    {
        Console.WriteLine($"Class B processed message: {message}");
    }

    public void Dispose()
    {
        _publisher.UnregisterHandler(this);
    }
}

This approach is just as valid and feels more "interface-first" if that aligns with your coding standards.


内容的提问来源于stack exchange,提问作者Joseph

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 08:37:51