首页 > 其他分享 >如何正确实现一个自定义可序列化的 Exception

如何正确实现一个自定义可序列化的 Exception

时间:2024-05-20 10:20:16浏览次数:18  
标签:Exception 自定义 BinaryFormatter MyException ErrorCode 序列化 public

最近在公司的项目中,编写了几个自定义的 Exception 类。提交 PR 的时候,sonarqube 提示这几个自定义异常不符合 ISerializable patten. 花了点时间稍微研究了一下,把这个问题解了。今天在此记录一下,可能大家都会帮助到大家。

自定义异常#

编写一个自定义的异常,继承自 Exception,其中定义一个 ErrorCode 来存储异常编号。平平无奇的一个类,太常见了。大家觉得有没有什么问题?

    [Serializable]
    public class MyException : Exception
    {
        public string ErrorCode { get;}

        public MyException(string message, string errorCode) : base(message)
        {
            ErrorCode = errorCode;
        }
    }

如我们对这个异常编写一个简单的单元测试。步骤如下:

        [TestMethod()]
        public void MyExceptionTest()
        {
            // arrange
            var orignalException = new MyException("Hi", "1000");
            var bf = new BinaryFormatter();
            var ms = new MemoryStream();

            // act
            bf.Serialize(ms, orignalException);
            ms.Seek(0, 0);
            var newException = bf.Deserialize(ms) as MyException;

            // assert
            Assert.AreEqual(orignalException.Message, newException.Message);
            Assert.AreEqual(orignalException.ErrorCode, newException.ErrorCode);
        }

这个测试主要是对一个 MyException 的实例使用 BinaryFormatter 进行序列化,然后反序列化成一个新的对象。将新旧两个对象的 ErrorCodeMessage 字段进行断言,也很简单。
让我们运行一下这个测试,很可惜失败了。测试用例直接抛了一个异常,大概是说找不到序列化构造器。

Designing Custom Exceptions Guideline#

简单的搜索了一下,发现微软有对于自定义 Exception 的
Designing Custom Exceptions

总结一下大概有以下几点:

  • 一定要从 System.Exception 或其他常见基本异常之一派生异常。

  • 异常类名称一定要以后缀 Exception 结尾。

  • 应使异常可序列化。 异常必须可序列化才能跨越应用程序域和远程处理边界正确工作。

  • 一定要在所有异常上都提供(至少是这样)下列常见构造函数。 确保参数的名称和类型与在下面的代码示例中使用的那些相同。

public class NewException : BaseException, ISerializable
{
    public NewException()
    {
        // Add implementation.
    }
    public NewException(string message)
    {
        // Add implementation.
    }
    public NewException(string message, Exception inner)
    {
        // Add implementation.
    }

    // This constructor is needed for serialization.
   protected NewException(SerializationInfo info, StreamingContext context)
   {
        // Add implementation.
   }
}

按照上面的 guideline 重新改一下我们的 MyException,主要是添加了几个构造器。修改后的代码如下:

    [Serializable]
    public class MyException : Exception
    {
        public string ErrorCode { get; }

        public MyException()
        {
        }

        public MyException(string message, string errorCode) : base(message)
        {
            ErrorCode = errorCode;
        }

        public MyException(string message, Exception inner): base(message, inner)
        {
        }

        protected MyException(SerializationInfo info, StreamingContext context)
        {
        }

    }

很可惜按照微软的 guideline 单元测试还是没通过。获取 Message 字段的时候会直接 throw 一个 Exception。

那么到底该怎么实现呢?

正确的方式#

我们还是按照微软 guideline 进行编写,但是在序列化构造器的上调用 base 的构造器。并且 override 基类的 GetObjectData 方法。

    [Serializable]
    public class MyException : Exception
    {
        public string ErrorCode { get; }

        public MyException()
        {
        }

        public MyException(string message, string errorCode) : base(message)
        {
            ErrorCode = errorCode;
        }

        public MyException(string message, Exception inner): base(message, inner)
        {
        }

        protected MyException(SerializationInfo info, StreamingContext context): base(info, context)
        {
            // Set the ErrorCode value from info dictionary.
            ErrorCode = info.GetString("ErrorCode");
        }

        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if (!string.IsNullOrEmpty(ErrorCode))
            {
                // Add the ErrorCode to the SerializationInfo dictionary.
                info.AddValue("ErrorCode", ErrorCode);
            }
            base.GetObjectData(info, context);
        }
    }

在序列化构造器里从 SerializationInfo 对象里恢复 ErrorCode 的值。调用 base 的构造可以确保基类的 Message 字段被正确的还原。这里与其说是序列化构造器不如说是反序列化构造器,因为这个构造器会在反序列化恢复成对象的时候被调用。

   protected MyException(SerializationInfo info, StreamingContext context): base(info, context)
        {
            // Set the ErrorCode value from info dictionary.
            ErrorCode = info.GetString("ErrorCode");
        }

这个 GetObjectData 方法是 ISerializable 接口提供的方法,所以基类里肯定有实现。我们的子类需要 override 它。把自己需要序列化的字段添加到 SerializationInfo 对象中,这样在上面反序列化的时候确保可以把字段的值给恢复回来。记住不要忘记调用 base.GetObjectData(info, context), 确保基类的字段数据能正确的被序列化。

    public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if (!string.IsNullOrEmpty(ErrorCode))
            {
                // Add the ErrorCode to the SerializationInfo dictionary.
                info.AddValue("ErrorCode", ErrorCode);
            }
            base.GetObjectData(info, context);
        }

再次运行单元测试,这次顺利的通过了

标签:Exception,自定义,BinaryFormatter,MyException,ErrorCode,序列化,public
From: https://www.cnblogs.com/mq0036/p/18201321

相关文章

  • 『手撕Vue-CLI』添加自定义指令
    前言经上篇『手撕Vue-CLI』添加帮助和版本号的介绍之后,已经可以在控制台中输入nue--help来查看帮助信息了,但是在帮助信息中只有--version,--help这两个指令,而vue-cli中还有很多指令,例如create,serve,build等等,所以本章将继续添加自定义指令,例如create指令。添加create......
  • redis存储之序列化问题
    1.问题描述:在SpringBoot集成Redis过程中,添加进redisf的内容如下2.出现这种情况的原因(1) 键和值都是通过Spring提供的Serializer序列化到数据库的(2) RedisTemplate默认使用的是JdkSerializationRedisSerializer,StringRedisTemplate默认使用的是StringRedisSerializer3.解......
  • 百度 Apollo 自定义安装第三方库(以 libtorch 为例)_apollo 使用自定义库
    CSDN搬家失败,手动导出markdown后再导入博客园百度Apollo是一个非常优秀的自动驾驶框架,但我们平时在开发中也会遇到各种原repo没有处理的问题。笔者近期想用pytorch的C++前端推理模型,但是遇到了libtorch版本与pytorch版本不匹配的问题,因此想自己安装一个新版本的li......
  • 百度 Apollo 自定义模块发布——使用 Python 语言(bazel 编译 Python 模块)_bazel-bin b
    CSDN搬家失败,手动导出markdown后再导入博客园BinaryvsComponent首先说明下,Apollo的核心概念是组件,通过组件可以实现资源的自动管理和调度。CyberRT中只能使用C++语言实现Component,Python版的API只能用来写传统的二进制可执行文件,参考官方文档中这两种方式的区别:B......
  • delphi cxgrid自定义画焦点框,把自带的虚线框去掉
    参考资料将FocusRect从虚线更改为实线或更改FocusRect的颜色|DevExpress支持如何在TableView网格中的整个选定/聚焦行周围绘制边框?|DevExpress支持 procedureTcxGridTableView.DoCustomDrawCell(ACanvas:TcxCanvas;AViewInfo:TcxGridTableDataCellViewInfo;v......
  • delphi cxgrid 自定义画焦点框
    procedureTMyTable.MyBandedTableViewCustomDrawCell(Sender:TcxCustomGridTableView;ACanvas:TcxCanvas;AViewInfo:TcxGridTableDataCellViewInfo;varADone:Boolean);varbounds:TRect;beginifAViewInfo.Focusedthenbeginbounds:=A......
  • Weblogic T3反序列化漏洞(CVE-2018-2628)
    目录前言T3协议概述漏洞复现修复方案前言WebLogicServer是一个企业级的应用服务器,由Oracle公司开发,支持完整的JavaEE规范,包括EJB、JSP、Servlet、JMS等,适合大型分布式应用和高负载场景。T3协议概述T3协议(Two-TierTCP/IPProtocol),是WebLogic中的一种专有协议,建立在TCP/IP协......
  • 基于uniapp+vue3自定义增强版table表格组件「兼容H5+小程序+App端」
    vue3+uniapp多端自定义table组件|uniapp加强版综合表格组件uv3-table:一款基于uniapp+vue3跨端自定义手机端增强版表格组件。支持固定表头/列、边框、斑马纹、单选/多选,自定义表头/表体插槽、左右固定列阴影高亮显示。支持编译兼容H5+小程序端+App端。如下图:H5+小程序+App端,多端......
  • Django自定义模板标签与过滤器
    title:Django自定义模板标签与过滤器date:2024/5/1718:00:02updated:2024/5/1718:00:02categories:后端开发tags:Django模版自定义标签过滤器开发模板语法Python后端前端集成Web组件Django模板系统基础1.Django模板语言概述Django模板语言(DTL)是一种用......
  • 【jmeter】.SampleException: Mismatch between expected number of columns: 生成报
    1、问题现象Causedby:org.apache.jmeter.report.core.SampleException:Consumerfailedwithmessage:Consumerfailedwithmessage:Mismatchbetweenexpectednumberofcolumns:17andcolumnsinCSVfile:3,checkyourjmeter.save.saveservice.*configurationor......