用序列化与反序列化实现C#中对Class类型参数的传值调用

众所周知,函数参数的调用是几乎所有语言中都会涉及到的问题。C#和大部分语言一样,有传值调用和传引用调用。其中对于一般的int,float,bool等基本类型以及struct类型参数,C#是默认传值调用。而对于Class类型以及加了关键字ref,out之类的参数,则使用传引用调用。而我在最近就遇到这样一种情况:使用了Class类型参数,但是并不想传引用调用,我就是想“强行”传值调用(倔强boy脸)。在一番挣扎后,终于还是找到了一种比较简单快捷的方法,那就是序列化与反序列化。

首先先看一段代码

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();
        }
    }
}

这里的基本思路就是先将源数据写进一个数据流中,再读取它并赋值给你所创建的副本数据,这样的话系统调用副本数据的时候的就不会是和源数据相同的内存地址了,使用起来也比拷贝构造函数简单许多。结果如下图
序列化与反序列化后的结果
当然,这里我使用的是文件流来读入,也可以使用内存流这样就不用在本地创建一份文件了。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值