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

如何向签名固定的事件传递含密钥的Device对象?

问题:向固定签名的事件传递自定义对象(蓝牙配对场景)

我需要向一个签名固定的事件传递对象,但无法修改该事件签名(也不能自定义EventArgs)。具体场景是蓝牙配对事件需要传入密钥,这是个通用问题。

当前订阅事件的代码如下:

public async Task MyPairAsync(Device device)
{
    var bluetoothLEDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(device.Id);
    DeviceInformationCustomPairing customPairing = bluetoothLEDevice.DeviceInformation.Pairing.Custom;
    customPairing.PairingRequested += CustomPairing_PairingRequested;
    var result = await customPairing.PairAsync(DevicePairingKinds.ProvidePin);
    // 后续处理(比如评估配对结果等)...
}

Device类包含BLE设备的ID和密钥:

public class Device
{
    public ulong Id { get; }
    public string Passkey { get; private set; }

    public Device(ulong id, string passkey)
    {
        Id = id;
        Passkey = passkey;
    }
}

PairingRequested事件的签名是固定的,因此事件处理方法必须遵循该签名:

private void CustomPairing_PairingRequested(DeviceInformationCustomPairing sender, DevicePairingRequestedEventArgs args)
{
    args.Accept(device.Passkey);  // 这里无法直接访问device,因为上下文里没有这个对象
}

DevicePairingRequestedEventArgs是密封类,无法继承来扩展携带Device实例。我不想用全局变量来传递Device对象,有没有更优雅的方案?


可行方案

方案1:利用闭包捕获当前Device实例

直接在订阅事件时使用lambda表达式作为处理方法,借助C#闭包特性捕获当前方法内的device变量,同时记得配对完成后及时取消订阅避免内存泄漏。

修改后的代码:

public async Task MyPairAsync(Device device)
{
    var bluetoothLEDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(device.Id);
    DeviceInformationCustomPairing customPairing = bluetoothLEDevice.DeviceInformation.Pairing.Custom;

    // 定义局部处理方法,捕获当前device
    void pairingHandler(DeviceInformationCustomPairing s, DevicePairingRequestedEventArgs args)
    {
        args.Accept(device.Passkey);
        // 处理完成后立即取消订阅
        s.PairingRequested -= pairingHandler;
    }

    customPairing.PairingRequested += pairingHandler;
    var result = await customPairing.PairAsync(DevicePairingKinds.ProvidePin);
    
    // 保险起见,无论配对结果如何都取消订阅
    customPairing.PairingRequested -= pairingHandler;
    
    // 后续处理...
}

这种方法简洁直接,无需额外存储结构,通过及时取消订阅避免内存泄漏,是单设备配对场景下的最优解。

方案2:用字典映射设备标识与Device实例

如果需要处理多设备并发配对,可以维护一个线程安全的字典,将设备唯一标识(如蓝牙地址)与Device实例关联。在事件处理方法中,从sender获取设备标识,再从字典中取出对应Device对象。

示例代码:

// 用ConcurrentDictionary保证多线程场景下的安全性
private readonly ConcurrentDictionary<ulong, Device> _deviceMap = new ConcurrentDictionary<ulong, Device>();

public async Task MyPairAsync(Device device)
{
    var bluetoothLEDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(device.Id);
    DeviceInformationCustomPairing customPairing = bluetoothLEDevice.DeviceInformation.Pairing.Custom;
    
    // 将设备ID和实例存入字典
    _deviceMap.TryAdd(device.Id, device);
    
    customPairing.PairingRequested += CustomPairing_PairingRequested;
    var result = await customPairing.PairAsync(DevicePairingKinds.ProvidePin);
    
    // 配对完成后清理字典并取消订阅
    customPairing.PairingRequested -= CustomPairing_PairingRequested;
    _deviceMap.TryRemove(device.Id, out _);
    
    // 后续处理...
}

private void CustomPairing_PairingRequested(DeviceInformationCustomPairing sender, DevicePairingRequestedEventArgs args)
{
    // 从sender提取设备ID并获取对应的Device实例
    if (ulong.TryParse(sender.DeviceInformation.Id, out ulong deviceId) 
        && _deviceMap.TryGetValue(deviceId, out Device device))
    {
        args.Accept(device.Passkey);
    }
}

该方案适合批量处理设备配对的场景,通过标识映射关联对象,但需注意及时清理字典内容,防止内存泄漏。

方案3:使用弱引用字典(进阶)

如果担心字典的强引用导致Device实例无法被GC回收,可以改用WeakReference<Device>存储,进一步降低内存泄漏风险:

private readonly ConcurrentDictionary<ulong, WeakReference<Device>> _weakDeviceMap = new ConcurrentDictionary<ulong, WeakReference<Device>>();

public async Task MyPairAsync(Device device)
{
    var bluetoothLEDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(device.Id);
    DeviceInformationCustomPairing customPairing = bluetoothLEDevice.DeviceInformation.Pairing.Custom;
    
    _weakDeviceMap.TryAdd(device.Id, new WeakReference<Device>(device));
    
    customPairing.PairingRequested += CustomPairing_PairingRequested;
    var result = await customPairing.PairAsync(DevicePairingKinds.ProvidePin);
    
    customPairing.PairingRequested -= CustomPairing_PairingRequested;
    _weakDeviceMap.TryRemove(device.Id, out _);
    
    // 后续处理...
}

private void CustomPairing_PairingRequested(DeviceInformationCustomPairing sender, DevicePairingRequestedEventArgs args)
{
    if (ulong.TryParse(sender.DeviceInformation.Id, out ulong deviceId) 
        && _weakDeviceMap.TryGetValue(deviceId, out var weakRef)
        && weakRef.TryGetTarget(out Device device))
    {
        args.Accept(device.Passkey);
    }
}

这种方案适合长期运行的应用,避免因强引用导致的内存泄漏,但代码复杂度相对较高。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 01:40:31