Rx.NET库中IDisposable对象的用法

Rx.NET库中IDisposable对象的用法

IDisposable是.net中的主动资源释放接口,它是在编程过程中经常使用到的一个接口,本文介绍一下微软在Rx.NET中提供的一系列常用的Disposable类,通过它们可以简化我们的程序代码,提高代码质量。

IDisposable:

一个简单的IDisposable接口实现如下

class DisposableObject : IDisposable { private readonly string name = null; public DisposableObject(string name) { this.name = name; } public void Dispose() { Console.WriteLine("{0} - Disposed", this.name); } }

注: 这个例子并不是合适的实现,主要是为了后面的代码演示使用。

ICancelable

RX框架在Disposable接口上扩展了一下,增加了一个查看是否被释放的状态,定义了一个ICancelable接口

public interface ICancelable : IDisposable { bool IsDisposed { get; } }

除了Disposable.Empty外,RX框架提供的都是ICancelable接口对象。

Disposable.Empty

Disposable.Empty返回的是一个没有任何功能的IDisposable对象,是一个IDisposable的NullObject模式的实现。

它的Dispose函数可以使用,但没有任何行为。

Disposable.Create

Disposable.Create可以快速构建一个IDisposable对象,它接受一个委托,当Dispose函数调用时,执行该委托。

var d = Disposable.Create(() => { Console.WriteLine("Disposed"); }); d.Dispose(); BooleanDisposable

BooleanDisposable是一个简单的ICancelable的实现,它的Dispose操作也没有任何行为,但可以查看该函数是否执行过。

var d = new BooleanDisposable(); Console.WriteLine(d.IsDisposed); d.Dispose(); Console.WriteLine(d.IsDisposed);

由于不能控制该对象的Dispose行为,这个对象用得到不是很多,往往用来作为一个开关变量使用。

CancellationDisposable

CancellationDisposable可将一个CancellationTokenSource对象封装为Disposable对象

当Dispose函数调用时,调用Cancel函数。

查看IsPosed状态时,返回IsCancellationRequested状态

简单的示例如下:

var source = new CancellationTokenSource(); source.Token.Register(() => Console.WriteLine("Cancelled")); var d = new CancellationDisposable(source); Console.WriteLine(d.IsDisposed); Console.WriteLine(source.IsCancellationRequested); RefCountDisposable

RefCountDisposable也是一个封装型的Disposable对象,不过它是一个引用计数型的封装。

新建RefCountDisposable对象后,引用计数为1

调用GetDisposable可以生成一个新的引用Disposable封装,引用技术加1

调用RefCountDisposable对象和GetDisposable生成的Disposable对象时,引用计数都减1

引用计数为0时,释放封装的Disposable对象

这里需要注意的时,当调用RefCountDisposable. Dispose函数时,可能引用计数还不为0,此时还是可以继续调用GetDisposable函数增加引用计数的。

简单的示例如下:

var d = new RefCountDisposable(new DisposableObject("#1")); var ref1 = d.GetDisposable(); var ref2 = d.GetDisposable(); Console.WriteLine("ref2 - Dispose"); ref2.Dispose(); Console.WriteLine("ref1 - Dispose"); ref1.Dispose(); Console.WriteLine("Dispose"); d.Dispose();

这个类在释放共享资源的时候非常有用,例如我们做视频点播的时候,当有多个客户端点播视频时,每个客户端都需要维护引用计数,当所有客户端都退出的时候,会自动Dispose,注销视频源。

CompositeDisposable

CompositeDisposable用于聚合多个disposable对象,形成一个新的disposable对象,它主要有Add,Clear,Dispose三个函数。

Add函数将子Disposable对象添加到集合

Clear函数删除集合类所有的Disposable对象,同时调用子Disposable对象的Dispose函数

Dispose函数释放CompositeDisposable对象:释放所有子Disposable对象。

如果CompositeDisposable对象已经释放,再次调用Add函数时,不会添加到集合,而是直接调用其子对象的Dispose函数。

简单的示例如下:

var d = new CompositeDisposable(); d.Add(new DisposableObject("#1")); d.Add(new DisposableObject("#2")); d.Add(new DisposableObject("#3")); d.Clear(); Console.WriteLine("----------"); d.Add(new DisposableObject("#4")); d.Add(new DisposableObject("#5")); d.Add(new DisposableObject("#6")); d.Dispose(); Console.WriteLine("----------"); d.Add(new DisposableObject("#7")); ContextDisposable

ContextDisposable对象也是一个复合Disposable对象

它主要用于封装一个Disposable对象,将其与一个SynchronizationContext上下文关联,

封装后生成的ContextDisposable对象的Disposable函数会Post到上下文线程中执行。

简单的示例如下:

var context = SynchronizationContext.Current; var obj = new DisposableObject("#1"); var d = new ContextDisposable(context, obj); d.Dispose();

这个对象用于释放UI对象非常方便,另外,也可以实现用于将一些Dispose函数推送到指定的队列中串行执行,从而避免一些并发问题。

ScheduledDisposable

ScheduledDisposable和ContextDisposable类似,也是用于封装一个Disposable对象,不过它是将其Dispose函数推送到指定的IScheduler中执行。

var scheduler = ThreadPoolScheduler.Instance; var obj = new DisposableObject("#1"); var d = new ScheduledDisposable(scheduler, obj); d.Dispose(); SingleAssignmentDisposable

SingleAssignmentDisposable是一个关联型的Disposable对象

可以通过它的Disposable属性关联到子Dispose对象。

当执行Dispose函数时,执行的是子Dispose对象的Dispose函数,如果没有关联子Dispose对象,则不执行任何操作。

已经执行Dispose函数后,再次使用Disposable属性关联后无法关联,直接会调用子对象的Dispose函数。

SingleAssignmentDisposable的关联有一个Single特点,它主要体现为:

如果已经通过Disposable属性关联后,再次调用Disposable属性关联时会抛异常

简单的示例如下:

var d = new SingleAssignmentDisposable(); d.Disposable = new DisposableObject("#1"); try { d.Disposable = new DisposableObject("#2"); //--- 例外発生 } catch (InvalidOperationException ex) { Console.WriteLine(ex.Message); } d.Dispose(); d.Disposable = new DisposableObject("#3");

输出结果为:

Disposable has already been assigned.
#1 – Disposed
#3 - Disposed

MultipleAssignmentDisposable

MultipleAssignmentDisposable和SingleAssignmentDisposable类似,区别是其关联的multi特性,它的特点是:

如果已经通过Disposable属性关联后,再次调用Disposable属性是会覆盖关联

简单的示例如下:

var d = new MultipleAssignmentDisposable(); d.Disposable = new DisposableObject("#1"); d.Disposable = new DisposableObject("#2"); d.Dispose(); d.Disposable = new DisposableObject("#3");

输出结果为:

#2 – Disposed
#3 - Disposed

SerialDisposable

SerialDisposable和SingleAssignmentDisposable也类似,不过它的Disposable属性再次关联的策略是:

覆盖关联,同时先释放上一个Disposable对象。

简单的示例如下:

var d = new SerialDisposable(); Console.WriteLine("#1 - Set"); d.Disposable = new DisposableObject("#1"); Console.WriteLine("#2 - Set"); d.Disposable = new DisposableObject("#2"); Console.WriteLine("Dispose"); d.Dispose();

输出结果为:

#1 – Set
#2 – Set
#1 – Disposed
Dispose
#2 – Disposed
#3 – Set
#3 - Disposed

推荐阅读

    1.Netty 概述

    1.Netty 概述,协议,高性能,原生 NIO 存在的问题NIO 的类库和 API 繁杂, 使用麻烦: 需要熟练掌握 Selector, ServerSocketChannel, SocketC

    ctnet需要打开吗|CTNET什么意思

    ctnet需要打开吗|CTNET什么意思,,1. ctnet需要打开吗1、打开手机设置,选择网络和连接,点击移动网络。2、选择接入点名称(APN)进入之后选择中国

    联通uninet设置|联通接入点uninet

    联通uninet设置|联通接入点uninet,,1. 联通接入点uninet设置如下,手机不同,对应的菜单不同,楼主要自己找找。:第一步:在菜单(功能表)-工具-设置-

    net设置在哪里|.net设置

    net设置在哪里|.net设置,,net设置在哪里联通3gnet接入点参数设置如下:名称:中国联通3G设置;接入点名称:3gnet;代理:空白;端口:空白;用户名:空白;密码: