用序列化与反序列化实现C#中对Class类型参数的传值调用
2016-08-28 12:13
204 查看
众所周知,函数参数的调用是几乎所有语言中都会涉及到的问题。C#和大部分语言一样,有传值调用和传引用调用。其中对于一般的int,float,bool等基本类型以及struct类型参数,C#是默认传值调用。而对于Class类型以及加了关键字ref,out之类的参数,则使用传引用调用。而我在最近就遇到这样一种情况:使用了Class类型参数,但是并不想传引用调用,我就是想“强行”传值调用(倔强boy脸)。在一番挣扎后,终于还是找到了一种比较简单快捷的方法,那就是序列化与反序列化。
首先先看一段代码
这里我定义一个类Person,有两个public属性name和age,并且还定义了一个函数changeName用来尝试修改person的name属性。程序执行结果如下图
我尝试给参数person构造一个拥有相同值的副本,并对这个副本进行操作,所以在changeName里new了一个Person类p。但是如果直接将传进来的参数赋值给p的话,它们使用的其实还是同一个内存地址,修改p还是会修改外面传进来的参数。所以结果是外面的Person A的name还是被修改了。
那么如果我不想让A被修改,我只想在函数里让传进来的参数当做一个副本来执行各种操作,这样即使再怎么修改都不会对外面的值产生影响。那应该怎么做呢?第一种最直接的方法就是给你的类写一个拷贝构造函数,这样就不用使用new新的类再赋值的方法构造副本了,但是如果你的类设计的过于复杂,拥有超多的属性或者还继承自别的类,那么光是写这个拷贝构造函数就有些繁琐了。
所以这时候就可以考虑序列化与反序列化的方法来实现了。
这里的基本思路就是先将源数据写进一个数据流中,再读取它并赋值给你所创建的副本数据,这样的话系统调用副本数据的时候的就不会是和源数据相同的内存地址了,使用起来也比拷贝构造函数简单许多。结果如下图
当然,这里我使用的是文件流来读入,也可以使用内存流这样就不用在本地创建一份文件了。
首先先看一段代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Csharp_Test { class Program { class Person { public string name; public int age; public Person(string Name, int Age) { this.name = Name; this.age = Age; } public person(){} } static void changeName(Person person,string newName) { Person p = new Person(); p = person; p.name = newName; } static void Main(string[] args) { Person A = new Person("Peter",20); Console.WriteLine("Person a name :"+A.name); changeName(A,"Jack"); Console.WriteLine("Person a name :" + A.name); Console.ReadLine(); } } }
这里我定义一个类Person,有两个public属性name和age,并且还定义了一个函数changeName用来尝试修改person的name属性。程序执行结果如下图
我尝试给参数person构造一个拥有相同值的副本,并对这个副本进行操作,所以在changeName里new了一个Person类p。但是如果直接将传进来的参数赋值给p的话,它们使用的其实还是同一个内存地址,修改p还是会修改外面传进来的参数。所以结果是外面的Person A的name还是被修改了。
那么如果我不想让A被修改,我只想在函数里让传进来的参数当做一个副本来执行各种操作,这样即使再怎么修改都不会对外面的值产生影响。那应该怎么做呢?第一种最直接的方法就是给你的类写一个拷贝构造函数,这样就不用使用new新的类再赋值的方法构造副本了,但是如果你的类设计的过于复杂,拥有超多的属性或者还继承自别的类,那么光是写这个拷贝构造函数就有些繁琐了。
所以这时候就可以考虑序列化与反序列化的方法来实现了。
using System; using System.IO; using System.Runtime.Serialization.Formatters.Binary; namespace Csharp_Test { class Program { [Serializable] //必须添加序列化特性 class Person { public string name; public int age; public Person(string Name, int Age) { this.name = Name; this.age = Age; } public Person() { } } static void changeName(Person person,string newName) { Person p = new Person(); p = person; string fileName = @"D:Programmers.dat";//文件名称与路径 Stream fStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite); BinaryFormatter BF = new BinaryFormatter();//创建二进制序列化器 BF.Serialize(fStream, person); fStream.Position = 0;//重置流位置 p = (Person)BF.Deserialize(fStream);//反序列化对象 p.name = newName; } static void Main(string[] args) { Person A = new Person("Peter",20); Person B = new Person(); Console.WriteLine("Person a name :"+A.name); changeName(A,"Jack"); Console.WriteLine("Person a name :" + A.name); Console.ReadLine(); } } }
这里的基本思路就是先将源数据写进一个数据流中,再读取它并赋值给你所创建的副本数据,这样的话系统调用副本数据的时候的就不会是和源数据相同的内存地址了,使用起来也比拷贝构造函数简单许多。结果如下图
当然,这里我使用的是文件流来读入,也可以使用内存流这样就不用在本地创建一份文件了。
相关文章推荐
- C#实现对象的Xml格式序列化及反序列化
- C#实现对象的Xml格式序列化及反序列化
- C#调用VC的DLL的接口函数参数类型转换一览表
- C#调用Oracle的存储过程,其参数为数组类型
- C#调用Win32 API如何处理指针类型的参数
- C#实现对象的Xml格式序列化及反序列化
- 用C#调用ffmpeg实现媒体类型转换(1)
- C#调用Oracle的存储过程,其参数为数组类型
- C#实现对象的Xml格式序列化及反序列化
- C#调用VC的DLL的接口函数参数类型转换一览表
- C# 实现复杂对象的序列化与反序列化[收藏此页] [打印]【IT168知识库】
- 关于C#调用winAPI参数类型对应关系的不完全总结
- C#实现对象的Xml格式序列化及反序列化
- C#实现对象的Xml格式序列化及反序列化
- C# 数据库sql中用参数的方法来执行UPDATE命令,实现更新dataTime类型
- [c#]Webservice中如何实现方法重载(overload)以及如何传送不能序列化的对象作参数
- SQL存储过程中调用返回表类型参数的函数的一种实现
- C#调用Oracle的存储过程,其参数为数组类型”中的­Package
- C#实现对象的Xml格式序列化及反序列化
- C#调用VC的DLL的接口函数参数类型转换一览表