在C#中方法如何优雅地对调用者返回

   场景一:方法需要返回结果

     一般而言写代码总是会期望一个方法有放返回值,因为调用者不常关心任务执行的细节,但总会关心任务执行的结果。于是我们会产生一个最原始的想法:设计方法调用返回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> 来包装结果,可以保障既返回结果又返回数据。

场景三:优雅地返回结果和数据

但是,场景二的解决方案仍然具有以下痛点:

  1. 客户端代码调用需要通过Result.Success判断结果
  2. 客户端还要知道成功的时候应该从Result.Data拿到数据,否则从Result.Message拿失败信息,
  3. 客户端需要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方法)非常非常非常非常简洁!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

好的,让我来解释一下,新方案是如何解决旧方案的痛点的。首先让我们回顾一下旧方案的痛点。

  1. 客户端需要通过Result.Success判断结果
  2. 客户端还要知道成功的时候应该从Result.Data拿到数据,否则从Result.Message拿失败信息,
  3. 客户端需要Result.Data和Result.Message进行非空校验,显得过于繁琐了。

在新方案中 ,客户端通过Peek方法获取结果;通过统一的Peek方法,无论结果为OK还是Error都对外暴露数据,客户端代码不再需要根据结果分别从Result.Data和Result.Message获取数据;在OK和Error实例新建的时候,会进行非空判断,解放了客户端的校验压力。           

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值