场景一:方法需要返回结果
一般而言写代码总是会期望一个方法有放返回值,因为调用者不常关心任务执行的细节,但总会关心任务执行的结果。于是我们会产生一个最原始的想法:设计方法调用返回bool值;
public bool DoSomething()
{
//your work...
if(...){
return true;
}else{
return false;
}
}
场景二:方法不应只返回结果,还须返回数据
场景一做法简单粗暴,如果现在不只要求我们返回结果,还要我们返回一些特定的信息,比如说我们读取文件,成功的话我们对调用者返回数据,失败的话,我们对调用者返回失败原因。此时我们可以这样做:
//定义Result类型,其中 Success表示结果,Message在失败的时候写入失败信息,Data在成功的时候写入填充数字
public class Results<T> {
public bool Success { get; set; }
public string Message { get; set; }
public T Data { get; set; }
}
//定义以下 读取文件的方法
private Results<string> ReadFile(string fileName)
{
//读取文件
try
{
//假设真的在读取文件,此处简化,不做读取文件演示
return new Results<string>()
{
Success = true,
Data = "文件读取成功"
};
//throw new Exception("打开文件失败");
}
catch (Exception ex)
{
return new Results<string>()
{
Success = false,
Message = ex.Message
};
}
}
//客户端调用如下
private void main()
{
try
{
Results<string> result = ReadFile("D://笑死我了.txt");
string msg = string.Empty;
if (result.Success)
{
//严谨的代码必须在此处做一次非空校验
if (result.Data!=null)
{
msg = "成功" + result.Data;
}
else{
msg="成功,但没有返回任何数据"
}
}
else {
//严谨的代码必须在此处做一次非空校验
if (result.Message != null)
{
msg="失败" + result.Message;
}
else{
msg="失败,但不知道失败原因";
}
}
Console.WriteLine(msg);
}
catch (Exception ex)
{
Console.WriteLine(msg);
}
}
现在我们使用一个泛型Result<T> 来包装结果,可以保障既返回结果又返回数据。
场景三:优雅地返回结果和数据
但是,场景二的解决方案仍然具有以下痛点:
- 客户端代码调用需要通过Result.Success判断结果
- 客户端还要知道成功的时候应该从Result.Data拿到数据,否则从Result.Message拿失败信息,
- 客户端需要Result.Data和Result.Message进行非空校验,显得过于繁琐了。
好了,现在让我们有请今天的主角。
第一步,定义以下功能类型
public abstract class Result<TData, TError>
{
/// <summary>
/// 创建一个OK结果
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static Result<TData, TError> OK(TData data) => new OK<TData, TError>(data);
/// <summary>
/// 返回一个Error结果
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static Result<TData, TError> Error(TError error) => new Error<TData, TError>(error);
/// <summary>
/// 开箱:如果结果是ok则返回data 如果是失败则会把失败信息当作异常抛出 具体实现看OK类型与Error类型的实现
/// </summary>
/// <returns></returns>
public abstract TData Unwrap();
/// <summary>
/// 窥视:如果结果是OK返回true并且把Data当作使用out 对外暴露;如果结果是NG,返回false,并且Error信息对外暴露 具体实现看OK类型与Error类型的实现
/// </summary>
/// <param name="result"></param>
/// <returns></returns>
public abstract bool Peek(out object result);
}
public class OK<TData, TError> : Result<TData, TError>
{
private TData _data;
public OK(TData data)
{
if (data==null)
throw new ArgumentNullException();
_data = data;
}
public override bool Peek(out object target)
{
target = _data;
return true;
}
public override TData Unwrap() => _data;
}
public class Error<TData,TError> : Result<TData, TError>
{
private TError _error;
public Error(TError error)
{
if (error == null)
throw new ArgumentNullException();
_error = error;
}
public override bool Peek(out object target)
{
target = _error;
return false;
}
public override TData Unwrap()
{
if (_error is Exception)
throw _error as Exception;
else
throw new Exception(_error.ToString());
}
第二步,使用以下类型
private void main()
{
try
{
//旧方案代码
//Results<string> result = ReadFile_Old("D://笑死我了.txt");
//string msg = string.Empty;
//if (result.Success)
//{
// if (result.Data!=null)
// {
// msg = "成功" + result.Data;
// }
//}
//else {
// if (result.Message != null)
// {
// msg="失败" + result.Message;
// }
//}
//新方案代码
string msg = ReadFile("D://笑死我了.txt").Peek(out object val) ? "成功:" + val.ToString() : "失败:" + val.ToString();
MessageBox.Show(msg);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private Result<string, Exception> ReadFile(string fileName)
{
//读取文件
try
{
return Result<string, Exception>.OK(fileName);
//throw new Exception("打开文件失败");
}
catch (Exception ex)
{
return Result<string, Exception>.Error(ex);
}
}
大家对比新方案与旧方案的代码 可以惊喜地发现 客户端代码(main方法)非常非常非常非常简洁!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
好的,让我来解释一下,新方案是如何解决旧方案的痛点的。首先让我们回顾一下旧方案的痛点。
- 客户端需要通过Result.Success判断结果
- 客户端还要知道成功的时候应该从Result.Data拿到数据,否则从Result.Message拿失败信息,
- 客户端需要Result.Data和Result.Message进行非空校验,显得过于繁琐了。
在新方案中 ,客户端通过Peek方法获取结果;通过统一的Peek方法,无论结果为OK还是Error都对外暴露数据,客户端代码不再需要根据结果分别从Result.Data和Result.Message获取数据;在OK和Error实例新建的时候,会进行非空判断,解放了客户端的校验压力。