我应该确保一个对象在Dispose被调用后不可用吗?



我有一个类BleScanner,它包装了一个内部BluetoothLEAdvertisementWatcher。它还实现了IDisposable,以确保在扫描仪被处理时监视器停止。

public sealed class BleScanner : IDisposable
{
public event AdvertisementReceivedHandler? AdvertisementReceived;
private readonly BluetoothLEAdvertisementWatcher m_Watcher;
public BleScanner() {
m_Watcher = new() {
// ...
};
// m_Watcher.Received += OnAdvertisementReceived;
}
// private void OnAdvertisementReceived(...) {
//    code elided for brevity
//    may eventually raise AdvertisementReceived
// }
public void Start() => m_Watcher.Start();
public void Stop() => m_Watcher.Stop();
public void Dispose() {
if (m_Watcher.Status == BluetoothLEAdvertisementWatcherStatus.Started) {
m_Watcher.Stop();
}
}
}

手表不是一次性的。因此,理论上,如果您在Dispose之后再次调用Start,扫描器仍然可以工作:

public async Task ScannerTest(CancellationToken token) {
using var scanner = new BleScanner();
scanner.AdvertisementReceived += OnAdvertisementReceived;
scanner.Start(); // will start the scan
await Task.Delay(3000, token); // raise events for 3 seconds
scanner.Stop(); // could be forgotten
scanner.Dispose(); // will stop the scan if indeed it was forgotten

scanner.Start(); // everything will work, despite "scanner" being disposed already
}

我应该确保Start(也许Stop)抛出ObjectDisposedExceptionDispose被调用?Dispose模式的指导方针只要求可以在没有异常的情况下多次调用Dispose,但没有说明在调用Dispose之后其他成员应该如何表现。使用IDisposable接口的一次性对象也不能说明调用已处置对象上的方法时会发生什么。

在您的问题中,您引用了IDisposable Guidelines。第一行说:"实现Dispose方法主要是为了释放非托管资源。"我觉得你来这不是为了这个。如果BluetoothLEAdvertisementWatcher是IDisposable,那么您可以在dispose()函数中处置它;但事实并非如此。所以,当你的对象超出作用域后,垃圾收集会在它的甜蜜时间里照顾你的对象;让它去做它该做的吧。

希望对你有帮助。

使用IDisposable来释放托管资源是完全可以的,这实际上是任何类型的作用域,需要在该作用域的末尾运行代码。

在这种情况下,我会说范围实际上在StartStop附近。所以我会让Start返回一个调用StopIDisposable(并使Stop私有)。你这种类型的人不是一次性的。例如,使用我的Nito的Disposable。一次性用品库:

public sealed class BleScanner
{
public event AdvertisementReceivedHandler? AdvertisementReceived;
private readonly BluetoothLEAdvertisementWatcher m_Watcher;
public BleScanner() {
m_Watcher = new() {
// ...
};
// m_Watcher.Received += OnAdvertisementReceived;
}
public IDisposable Start()
{
m_Watcher.Start();
return Disposable.Create(() => Stop());
}
private void Stop() => m_Watcher.Stop();
}
public async Task ScannerTest(CancellationToken token) {
var scanner = new BleScanner();
scanner.AdvertisementReceived += OnAdvertisementReceived;
using var scannerSubsctiption = scanner.Start();
await Task.Delay(3000, token); // raise events for 3 seconds
}

最新更新