C#编程基础之序列化

一、序列化的含义

序列化是将对象处理为字节流以存储对象或传输到内存、数据库或文件。其主要目的是保存对象的状态,以便可以在需要时重新创建对象。相反的过程称为反序列化。

1.1 序列化的工作方式

此图显示序列化的整个过程。

915.jpg

对象被序列化为流。流传递的不仅是数据,还包括有关对象类型的信息,如对象的版本、区域性和程序集名称。通过该流,可以将对象存储在数据库、文件或内存中。

 

1.2 用于序列化

通过序列化,开发人员可以保存对象的状态,并在需要时重新创建该对象,从而提供对象的存储以及数据交换。通过序列化,开发人员还可以执行类似如下的操作:通过 Web 服务将对象发送到远程应用程序、将对象从一个域传递到另一个域、以 XML 字符串的形式跨防火墙传递对象,或者跨应用程序维护安全信息或用户特定信息。

 

1.3 使对象可序列化

若要序列化对象,您需要待序列化的对象、要包含序列化对象的流,以及一个 Formatter。 System.Runtime.Serialization包含序列化和反序列化对象所需的类。

将 SerializableAttribute 特性应用于一个类型可指示该类型的实例可以序列化。尝试序列化时,如果类型没有 SerializableAttribute 特性,将引发SerializationException 异常。

如果不希望类中的字段可序列化,请应用 NonSerializedAttribute 特性。如果可序列化类型的字段包含指针、句柄或其他一些专用于特定环境的数据结构,并且不能在不同的环境中以有意义的方式重建,则可能需要使该字段不可序列化。

如果已序列化的类包含对标记为 SerializableAttribute 的其他类的对象的引用,则也将序列化这些对象。

 

1.3.1 二进制序列化和 XML 序列化

可以使用二进制序列化或 XML 序列化。在二进制序列化中,会序列化所有成员(甚至包括那些只读成员),从而可以提高性能。XML 序列化提供了可读性更好的代码,并在对象共享和使用方面提供了更大的灵活性,以便实现互操作性。

 

1.3.2 二进制序列化

二进制序列化使用二进制编码来生成精简的序列化,以用于存储或基于套接字的网络流等。

 

1.3.3 XML 序列化

XML 序列化将对象的公共字段和属性或者方法的参数及返回值序列化为符合特定 XML 架构定义语言 (XSD) 文档的 XML 流。XML 序列化会生成具有转换为 XML 的公共属性和字段的强类型类。 System.Xml.Serialization 包含序列化和反序列化 XML 所需的类。

您可以将特性应用于类和类成员,以控制 XmlSerializer 序列化或反序列化类实例的方式。

 

1.3.4 SOAP 序列化

XML 序列化还可用于将对象序列化为符合 SOAP 规范的 XML 流。SOAP 是一种基于 XML 的协议,它是专门为使用 XML 来传输过程调用而设计的。如同常规的 XML 序列化,特性可用于控制 XML Web services 生成的文本样式的 SOAP 消息。

 

1.3.5 基本序列化和自定义序列化

可以通过两种方式执行序列化:基本序列化和自定义序列化。基本序列化使用 .NET Framework 来自动序列化对象。

 

1.3.5.1 基本序列化

基本序列化的唯一要求是对象必须应用 SerializableAttribute 特性。 

NonSerializedAttribute 可用于禁止序列化特定字段。

使用基本序列化时,对象的版本控制可能会产生问题,在这种情况下,自定义序列化可能更合适。基本序列化是执行序列化的最简单的方法,但对进程提供的控制并不多。

1.3.5.2 自定义序列化

在自定义序列化中,可以准确地指定将序列化哪些对象以及如何完成序列化。类必须标记为 SerializableAttribute,并实现 ISerializable 接口。

如果希望同样以自定义方式反序列化对象,则必须使用自定义构造函数。

1.3.6 设计器序列

设计器序列化是一种特殊形式的序列化,它涉及通常与开发工具关联的对象持久性的种类。设计器序列化是将对象图转换为以后可用于恢复对象图的源文件的过程。源文件可以包含代码、标记,甚至包含 SQL 表信息。有关更多信息,请参见Designer Serialization Overview。

二、通过序列化保存对象数据

虽然您可以在设计时将对象的属性设置为默认值,但是,如果该对象被损环,则在运行时输入的所有值均会丢失。 可以使用序列化在实例之间保持对象数据,从而能够存储值并且在下次实例化对象时检索这些值。

在本演练中,将创建一个简单的对象,并将该对象的数据保存到文件中。然后,当您重新创建对象时将从该文件检索数据。最后,将修改代码以使用 SOAP 格式保持对象。

2.1 使用序列化保存对象

   [Serializable]  //将类标记为可序列化    public class Coupon : INotifyPropertyChanged    {        public decimal Amount { get; set; }        public float InterestRate { get; set; }        public int Term { get; set; }        private string _name;        public string Name        {            get { return _name; }            set            {                _name = value;                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Customer"));            }        }        [field: NonSerialized()]    //将可序列化的类中的某字段标记为不被序列化        public event PropertyChangedEventHandler PropertyChanged;        public Coupon(decimal amount, float interestRate, int term, string name)        {            Amount = amount;            InterestRate = interestRate;            Term = term;            _name = name;        }    }static void Main(string[] args){            const string fileName = @"demo1.txt";            var coupon = new Coupon(10000, 0.2f, 1, "反骨仔");            using (var stream = File.Create(fileName))            {                var deserializer = new BinaryFormatter();  //二进制格式序列化器                deserializer.Serialize(stream, coupon);  //序列化对象到文件中            }}

916.jpg

现在尝试反序列化,看看与之前 Coupon 对象的值是否一致。

static void Main(string[] args){            const string fileName = @"demo1.txt";            //var coupon = new Coupon(10000, 0.2f, 1, "反骨仔");            //判断该文件是否存在            if (!File.Exists(fileName))            {                return;            }            using (var stream = File.OpenRead(fileName))            {                var deserializer = new BinaryFormatter();   //二进制序列化器                var coupon = deserializer.Deserialize(stream) as Coupon;    //反序列化                if (coupon == null)                {                    return;                }                Console.WriteLine($"{nameof(Coupon)}:");                Console.WriteLine($"    {nameof(coupon.Amount)}: {coupon.Amount}");                Console.WriteLine($"    {nameof(coupon.InterestRate)}: {coupon.InterestRate}%");                Console.WriteLine($"    {nameof(coupon.Term)}: {coupon.Term}");                Console.WriteLine($"    {nameof(coupon.Name)}: {coupon.Name}");            }            Console.Read();}

917.jpg

2.2 使用 SOAP 格式保存对象

static void Main(string[] args){            const string fileName = @"demo1.txt";            var coupon = new Coupon(10000, 0.2f, 1, "反骨仔");            using (var stream = File.Create(fileName))            {                var deserializer = new SoapFormatter(); //Soap 格式化器                deserializer.Serialize(stream, coupon); //序列化            }}

918.jpg

反序列化时也采用 SoapFormatter 即可

var deserializer = new SoapFormatter();   //Soap 格式化器var coupon = deserializer.Deserialize(stream) as Coupon;    //反序列化

【注意】本示例将数据存储到二进制或 SOAP 格式的文件中。不应将这些格式用于敏感数据,如密码或信用卡信息。

【备注】二进制格式对于大多数 Windows 应用程序均适用。但对于 Web 应用程序或 Web 服务,您可能希望使用 SOAP 格式将对象保存到 XML 文件中,以使对象易于共享。

同样,也可以通过 XmlSerializer 将对象序列化保存在 XML 文件。我们可以根据需求选择合适的序列化器,操作基本是一样的。

以上就是C#编程基础之序列化的内容,更多相关内容请关注PHP中文网(www.php.cn)!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1432100.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月17日 06:12:26
C#汉字转拼音(支持多音字)
下一篇 2025年12月17日 06:12:36

相关推荐

  • c++中的SFINAE技术是什么_c++模板编程中的SFINAE原理与应用

    SFINAE 是“替换失败不是错误”的原则,指模板实例化时若参数替换导致错误,只要存在其他合法候选,编译器不报错而是继续重载决议。它用于条件启用模板、类型检测等场景,如通过 decltype 或 enable_if 控制函数重载,实现类型特征判断。尽管 C++20 引入 Concepts 简化了部分…

    2026年5月10日
    000
  • c#文件怎么打开

    打开 C# 文件有三种方法:Visual Studio:启动 Visual Studio,通过“文件”菜单打开 C# 文件。文本编辑器:使用文本编辑器打开 C# 文件,将其视为普通文本。.NET Core 命令行工具:使用 csc.exe 命令行工具编译 C# 文件,生成可执行文件。 如何打开 C#…

    2026年5月10日
    000
  • c++如何实现UDP通信_c++基于UDP的网络通信示例

    UDP通信基于套接字实现,适用于实时性要求高的场景。1. 流程包括创建套接字、绑定地址(接收方)、发送(sendto)与接收(recvfrom)数据、关闭套接字;2. 服务端监听指定端口,接收客户端消息并回传;3. 客户端发送消息至服务端并接收响应;4. 跨平台需处理Winsock初始化与库链接,编…

    2026年5月10日
    000
  • 函数指针在 C++ 多态中的作用:揭示多态背后的真相

    函数指针在 C++ 多态中的作用:揭示多态背后的真相 简介 多态是面向对象编程的一项强大功能,它允许对象在运行时以不同的方式表现。C++ 中的多态实现依赖于函数指针。本文将深入探讨函数指针在多态中的作用,并通过一个实战案例展示如何利用它们。 函数指针 立即学习“C++免费学习笔记(深入)”; 函数指…

    2026年5月10日
    000
  • C++框架与Java框架在易用性方面的比较

    c++++ 框架的易用性低于 java 框架,具体原因如下:c++ 框架学习曲线陡峭,需要深入理解 c++ 语言。易出错且调试困难。而 java 框架具有以下易用性优势:学习曲线低,尤其适合 java 初学者。提供丰富的库和工具,简化开发。运行时异常处理,简化异常处理。 C++ 框架与 Java 框…

    2026年5月10日
    000
  • c++中头文件和源文件的区别_c++头文件与源文件作用对比

    头文件声明接口,源文件实现逻辑。头文件含类、函数声明及宏定义,通过#include被多文件共享,用include守卫防重;源文件实现具体功能,编译为目标文件后由链接器合并。声明与实现分离提升模块化与编译效率,模板和内联函数因需编译时可见故常置于头文件,命名空间避免符号冲突,整体结构使项目更清晰易维护…

    2026年5月10日
    000
  • C++ 函数重载在事件驱动的编程中的应用

    在事件驱动的编程中,函数重载可创建具有不同参数签名的相似功能,为单一函数名提供多样化功能。它包含以下优点:代码可读性:使用单一函数名表示相关任务。可维护性:避免重复编写类似逻辑。可重用性:跨项目和应用程序 reutilizar。 C++ 函数重载在事件驱动的编程中的应用 在事件驱动的编程中,函数重载…

    2026年5月10日
    000
  • C++ 函数性能优化对系统稳定性的影响

    标题:C++ 函数性能优化对系统稳定性的影响 简介 函数性能优化是 C++ 程序员提高程序效率的关键技术。本文将探讨函数性能优化对系统稳定性的影响,并提供实战案例来证明这一点。 性能优化对稳定性的作用 立即学习“C++免费学习笔记(深入)”; 函数性能优化不仅可以提升程序速度,还可以提高系统的稳定性…

    2026年5月10日
    000
  • WebAssembly中导入JavaScript函数:无胶水代码集成指南

    本文深入探讨了在WebAssembly模块中直接导入和使用JavaScript函数的机制,特别是当使用Emscripten的STANDALONE_WASM和SIDE_MODULE编译模式时。文章详细分析了TypeError: import object field ‘GOT.mem&#8…

    2026年5月10日
    000
  • C++如何编译和链接_C++从源码到可执行文件的过程解析

    c++kquote>预处理展开宏和头文件,编译生成汇编代码,汇编转为机器码,链接合并目标文件与库生成可执行程序。 当你写完一段C++代码,比如一个简单的hello world程序,最终能运行起来,背后其实经历了一系列步骤:预处理、编译、汇编和链接。这个过程将人类可读的源码转换成机器可以执行的程…

    2026年5月10日
    000
  • c++中sizeof运算符的用法和常见陷阱 _c++ sizeof使用技巧及陷阱解析

    sizeof运算符在编译时计算类型或对象的字节大小,返回size_t类型,常用于获取数据大小、数组元素个数及内存操作;但存在数组传参退化为指针导致失效、对指针无法获知动态内存大小、表达式不求值、结构体因对齐产生填充等常见陷阱;需结合模板、显式传参、对齐控制等方式规避问题,提升代码可移植性和安全性。 …

    2026年5月10日
    000
  • C#如何进行网络编程?Socket与TCP/IP通信编程实例详解

    C#通过Socket类实现TCP通信,首先服务器绑定IP和端口并监听,客户端发起连接,双方通过Send/Receive收发数据,最后关闭连接。 C# 进行网络编程主要依赖于 System.Net 和 System.Net.Sockets 命名空间,其中最核心的是使用 Socket 类实现基于 TCP…

    2026年5月10日
    000
  • C++ 函数递归详解:递归查找列表中的元素

    递归查找列表元素的步骤如下:递归基础条件:如果列表为空,则元素不存在。递归过程:使用递归调用查找列表的剩余部分,并调整返回的索引。检查列表的第一个元素:如果第一个元素与所查找的元素相等,则元素位于索引 0 处。找不到:如果递归和第一个元素检查都没有找到,则元素不存在。 C++ 函数递归详解:递归查找…

    2026年5月10日
    000
  • C++怎么使用C++17的并行算法库_C++ std::execution与多核性能优化

    c++kquote>C++17通过std::execution策略引入并行算法支持,需编译器(如GCC 8+)和线程库(如TBB)配合;提供seq、par、par_unseq三种策略控制执行模式;可用于sort、for_each等算法提升大数据性能,但需避免数据竞争,推荐使用reduce等安全…

    2026年5月10日
    000
  • c++ lambda表达式怎么写 c++匿名函数用法详解

    答案是lambda表达式可简洁定义匿名函数,用于STL算法等场景。其语法包含捕获列表、参数列表、mutable、返回类型和函数体,如[=](int x) { return x > 0; }可值捕获外部变量并用于判断正数。 在C++中,lambda表达式是一种创建匿名函数的简洁方式,常用于需要传…

    2026年5月10日
    200
  • C++框架的Unlicense许可类型简介

    unlicense 许可证类型为免费且宽松,允许用户在不附加任何限制的情况下使用、修改和分发软件。它旨在最大限度地减少限制和允许最大的自由度,具有以下好处:简洁易懂高度开放无保证 C++ 框架的 Unlicense 许可证类型简介 了解 Unlicense Unlicense 是一个自由和宽松的软件…

    2026年5月10日
    000
  • 利用日志记录增强 C++ 函数的调试能力

    如何利用日志记录增强 c++++ 函数的调试能力?使用 glog 库进行日志记录: 安装 glog,并在代码中使用 glog 头文件和 initgooglelogging() 初始化日志记录。添加日志记录语句: 使用 log() 宏在要记录的代码块中添加日志记录语句,以记录函数开始、结束或其他重要事…

    2026年5月10日
    000
  • C++ 函数模板如何使用并在实际场景中应用?

    函数模板允许您定义可以处理不同类型参数的函数的通用版本。语法为:template,其中 t 是类型参数。要使用函数模板,请指定所需的参数类型,例如:max(10, 20)。函数模板在排序等实际应用中很有用,例如:template void sort(t arr[], int size)。它们具有通用…

    2026年5月10日
    000
  • C++ 并发编程中内存访问问题及解决方法?

    在 c++++ 并发编程中,共享内存访问问题包括数据竞争、死锁和饥饿。解决方案有:原子操作:确保对共享数据的访问是原子性的。互斥锁:一次只允许一个线程访问临界区。条件变量:线程等待某个条件满足。读写锁:允许多个线程并发读取,但只能允许一个线程写入。 C++ 并发编程中的内存访问问题及解决方案 在多线…

    2026年5月10日
    000
  • c++如何实现函数的重载_c++函数重载实现方法

    函数重载通过参数列表差异实现,如类型、数量或顺序不同,编译器根据实参选择对应函数,返回类型不同不能单独用于重载。 在C++中,函数重载允许在同一作用域内定义多个同名函数,只要它们的参数列表不同(参数个数、类型或顺序不同),编译器会根据调用时传入的实参来选择匹配的函数。函数重载不能仅通过返回类型的不同…

    2026年5月10日
    000

发表回复

登录后才能评论
关注微信