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

如何避免蓝牙设备交互类中的时间耦合问题?

解决蓝牙设备交互类的API时间耦合问题

问题背景

我设计了一个用于蓝牙设备交互的Device类,但当前API存在时间耦合问题:客户端必须严格先调用ConnectAsync,才能调用SendMessageAsync;同时因为需要提前建立连接接收MessageReceived事件,无法在SendMessageAsync里延迟执行连接逻辑。现有代码如下:

class Device
{
    private bool _isConnected;
    private readonly ulong _bluetoothAddress;

    public event EventHandler<Message> MessageReceived;

    public Device(ulong bluetoothAddress)
    {
        _bluetoothAddress = bluetoothAddress;
    }

    public async Task ConnectAsync()
    {
        // 异步连接逻辑...

        _isConnected = true;
    }

    public async Task SendMessageAsync(Message message)
    {
        if (!_isConnected)
        {
            throw new InvalidOperationException("Not connected");
        }

        // 发送逻辑...
    }
}

方案1:工厂方法创建已连接实例

将连接逻辑封装到静态工厂方法中,强制客户端只能拿到已完成连接、初始化好事件监听的Device实例,从根源上消除顺序依赖:

class Device
{
    private bool _isConnected;
    private readonly ulong _bluetoothAddress;

    public event EventHandler<Message> MessageReceived;

    // 私有构造函数,禁止直接实例化
    private Device(ulong bluetoothAddress)
    {
        _bluetoothAddress = bluetoothAddress;
    }

    // 静态工厂方法,返回已连接的Device实例
    public static async Task<Device> CreateConnectedDeviceAsync(ulong bluetoothAddress)
    {
        var device = new Device(bluetoothAddress);
        await device.ConnectAndSetupListeningAsync();
        return device;
    }

    // 内部执行连接+事件初始化逻辑
    private async Task ConnectAndSetupListeningAsync()
    {
        // 异步连接蓝牙设备的逻辑...
        
        // 初始化消息接收监听,确保连接后能触发MessageReceived事件
        RegisterMessageListener();
        
        _isConnected = true;
    }

    private void RegisterMessageListener()
    {
        // 在这里完成蓝牙消息接收的回调注册
    }

    public async Task SendMessageAsync(Message message)
    {
        // 无需再检查连接状态,实例一定是已连接的
        // 发送消息逻辑...
    }
}

客户端调用示例:

var device = await Device.CreateConnectedDeviceAsync(bluetoothAddr);
device.MessageReceived += (sender, msg) => { /* 处理收到的消息 */ };
await device.SendMessageAsync(new Message("test"));

方案2:内部自动维护连接状态

在类内部实现懒加载连接逻辑,不管客户端先调用ConnectAsync还是直接调用SendMessageAsync,都自动完成连接和事件初始化,同时用锁处理并发场景:

class Device
{
    private bool _isConnected;
    private readonly ulong _bluetoothAddress;
    private readonly SemaphoreSlim _connectionLock = new SemaphoreSlim(1, 1);
    public event EventHandler<Message> MessageReceived;

    public Device(ulong bluetoothAddress)
    {
        _bluetoothAddress = bluetoothAddress;
    }

    // 内部确保连接完成的方法
    private async Task EnsureConnectedAsync()
    {
        if (_isConnected) return;
        
        await _connectionLock.WaitAsync();
        try
        {
            if (_isConnected) return; // 双重检查,避免并发重复连接
            
            // 异步连接逻辑...
            
            // 初始化消息监听
            RegisterMessageListener();
            
            _isConnected = true;
        }
        finally
        {
            _connectionLock.Release();
        }
    }

    private void RegisterMessageListener()
    {
        // 注册消息接收回调
    }

    // 保留主动连接方法,供客户端提前触发连接
    public async Task ConnectAsync()
    {
        await EnsureConnectedAsync();
    }

    public async Task SendMessageAsync(Message message)
    {
        await EnsureConnectedAsync();
        
        // 发送消息逻辑...
    }
}

这个方案既保留了客户端主动提前连接的能力,又允许直接调用SendMessageAsync自动完成连接,同时避免了并发场景下的重复连接问题。


方案3:分离设备元数据与连接会话

把"设备信息"和"已连接交互"拆分为两个类,用Device存储蓝牙地址等元数据,DeviceSession专门处理已连接状态下的消息收发和事件监听:

// 仅存储设备元数据的类
class Device
{
    public ulong BluetoothAddress { get; }

    public Device(ulong bluetoothAddress)
    {
        BluetoothAddress = bluetoothAddress;
    }
}

// 已连接的会话类,负责交互逻辑
class DeviceSession
{
    private readonly Device _device;
    public event EventHandler<Message> MessageReceived;

    private DeviceSession(Device device)
    {
        _device = device;
    }

    public static async Task<DeviceSession> CreateAsync(Device device)
    {
        var session = new DeviceSession(device);
        await session.ConnectAndSetupListening();
        return session;
    }

    private async Task ConnectAndSetupListening()
    {
        // 连接蓝牙设备逻辑...
        // 初始化消息监听逻辑...
    }

    public async Task SendMessageAsync(Message message)
    {
        // 发送消息逻辑...
    }
}

客户端调用示例:

var device = new Device(bluetoothAddr);
var session = await DeviceSession.CreateAsync(device);
session.MessageReceived += (sender, msg) => { /* 处理消息 */ };
await session.SendMessageAsync(new Message("test"));

这种模式语义更清晰,明确区分"设备存在"和"设备已连接"两个状态,不会出现未连接的会话实例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 10:00:58