通常来说,如果方法的返回值是单一数据类型的,如果需要知道是否成功,可以用正常结果表示成功,用Null或者一些特殊的数值表示失败。但是,如果还想知道错误信息或者错误代号,就需要设计一个操作结果类,我们称之为操作结果基类。
除上面的场景外,可能还会返回多个结果值,如一个额外的string对象、Bitmap对象,甚至是自定义的对象,此时可以在操作结果基类的基础上,通过泛型扩展1个甚至多个自定义对象。
OperateResult是为了解决方法具有多个返回值的问题而设计的。首先创建一个OperateResult的基类,针对一些返回值为布尔型的或void返回类型的方法使用。OperateResult基类的设计如下。
该类主要的三个属性分别是IsSuccess、Message和ErrorCode。其中,IsSuccess表示是否成功。Message表示信息,如果IsSuccess为True,默认Message的值是"Success";如果IsSuccess为False,则可以通过Message获取到对应的错误信息。ErrorCode作为一个可选项,主要表示错误代号,这个在一些特定的场合中会使用到。
在OperateResult中增加一些构造方法,方便实例化对象进行传输,代码如下所示。
然而,仅仅使用OperateResult是不够的。举个例子,假设我们需要读取某个文件中的内容。若读取成功,则返回文件内容;若读取失败,则返回错误信息。在这种情况下,OperateResult无法满足需求。因为我们需要更具体的内容,而不仅仅是一个字符串,实际应用中,可能需要返回其他类型的数据,甚至可能是多个值。因此,我们需要使用泛型类来实现更灵活的操作结果。
首先以返回单个不确定数据类型的数据为例,设计第一个泛型类OperateResult<T>,代码如下所示。
观察OperateResult<T>,首先其是一个衍生类,继承自OperateResult,在OperateResult的基础上增加了一个Content属性,表示内容数据。其次就是增加了一个构造方法,该方法也继承自父类的构造方法,对于多个类型(如OperateResult<T1,T2>),也类似于这种架构。OperateResult<T>中的内容数据数量可以无限扩展,但是一般我们扩展到5个参数就够用了。
为了方便使用,快速返回一个成功或者失败的结果,在OperateResult中,增加CreateSuccessResult和CreateFailResult方法,这些方法为静态方法,可以直接通过父类OperateResult进行调用,具体实现代码如下所示。
OperateResult在日常开发及架构封装中应用非常广泛,在后续的章节中也会讲述其实际中的应用。为了让大家更好地初步理解OperateResult及其使用方法,下面通过两个实际的案例进行说明。
案例一: 以封装串口通信为例,我们正常操作串口,需要一个布尔类型的返回值,表示打开成功或打开失败。若打开成功,则可以进行后续的处理;若打开失败,则需要知道打开失败的原因。一般情况下,只能通过外部调用和异常捕获来实现,如果想在通信库中进行封装,那么我们可以借助OperateResult来实现,具体实现代码如下所示。
从上面的代码来看,我们在调用的时候会更加方便,不再需要加try-catch异常捕获,直接判断返回结果的IsSuccess属性。若为True,则表示串口打开成功;若为False,则可以通过Message属性获取打开失败的原因。
案例二: 以读取文件为例,我们正常读取文件,获取文件中的内容,需要的是一个字符串类型的返回值,但是文件操作是有可能出现异常情况的,比如文件不存在或者文件被占用等。此时,我们需要外部加异常,如果想封装成方法,我们可以借助OperateResult<T>来实现,具体实现代码如下所示。
我们在调用这个方法时,首先判断IsSuccess属性。若为True,通过Content属性获取具体结果;若为False,则通过Message属性获取错误信息。