一、前言:
在备战软考的过程中,涉及到了原型模式,其中讲到了在浅复制的过程中,值类型和引用类型是有区别的。那么究竟什么是值类型,什么是引用类型呢?
在理解这两个概念之前,我们先谈一谈栈和堆。
计算机的内存从概念上分,会有许许多多独立的块,堆和栈就是其中的两种内存块。栈是一种先进后出的数据结构,当我们调用一个方法时,假如这方法有参数的话,那么我们就需要在栈中为这个方法的参数和方法所用到的变量(局部变量)分配一小部分内存。当方法结束的时候,所占用的内存就会自动释放回栈中。堆是用于为类型实例(对象)分配空间的内存区域,当我们使用new创建一个对象的时候,new出来的东西放在堆中。更多详情,猛戳这里。
二、值类型和引用类型:
C#的类型一共分为两类,一种是值类型(Value Type),一类是引用类型(Reference)。
每一种编程语言的值类型都有一些非常细小的不同,C#的内置值类型共有七种:int、long、float、char、bool、enum、struct。而string类型是一种具有值类型特性的特殊引用类型,且按下不表。
三、函数调用时参数的传递方式:
1、传值调用是将实参的值传递给被调用函数的形参。因此实参既可以是表达式,也可以是变量(或数组元素)。
2、传地址调用是将实参的地址传给被调用函数的形参。所以,实参必须有地址。因此实参必须是变量(数组),不能是表达式或常量(因为他们不存在地址)。
3、如果采用传值方式调用,则形参值的变化不会影响实参。
4、如果采用引用方式调用,则形参值的变化直接反映到实参。
四、举例:
基于值类型的变量直接包含值。将一个值类型变量赋给另一个值类型变量时,将复制包含的值。举个例子:int a =10; int b = a,此时b的值为10,当把a付给b时,开辟一块新的空间来保存b的值,b的值为10。当把a的值该为50时,会在a的空间保存50.。与b无关,此时b仍为10。
而引用类型则与之不同,因为引用类型指向的是同一个地址。
原型模式例子:
简历类:
class Resume:ICloneable
{
private string name;
private string sex;
private string age;
private string timeArea;
private string company;
public Resume(string name)
{
this.name = name;
}
//设置个人信息:
public void SetPersonalInfo(string sex,string age)
{
this.sex = sex;
this.age = age;
}
//设置工作经历
public void SetWorkExperience(string sex,string age)
{
this.timeArea = timeArea;
this.company = company;
}
//显示
public void Display()
{
Console.WriteLine("{0} {1} {2}",name, sex, age);
Console.WriteLine("工作经历:{0} {1}",timeArea, company);
}
public Object Clone()
{
return (Object)this.MemberwiseClone();
}
}
客户端调用代码:
static void Main(string[] args)
{
Resume a = new Resume("大鸟");
a.SetPersonalInfo("男","29");
a.SetWorkExperience("1998-2000","XX公司")
Resume b = (Resume)a.Clone();
b.setWorkExperience("1998-2006","YY企业")
Resume c = (Resume)a.Clone();
c.SetPersonalInfo("男","24");
a.Display();
b.Display();
c.Display();
Console.Read();
}
工作经历类
Class WorkExperience
{
private string workDate;
public string WorkDate
{
get {return workDate;}
set {workDate = value;}
}
private string company;
public string Company
{
get {return company;}
set {company = value;}
}
}
简历类:
class Resume:ICloneable
{
private string name;
private string sex;
private string age;
private WorkExperience work; //引用"工作经历"对象
public Resume(string name)
{
this.name = name;
work = new WorkExperience(); //在“简历”类实例化时同时实例化“工作经历”
}
//设置个人信息:
public void SetPersonalInfo(string sex,string age)
{
this.sex = sex;
this.age = age;
}
//设置工作经历
public void SetWorkExperience(string workDate,string company)
{
work.WorkDate = workDate;
work.Company = company;
}
//显示
public void Display()
{
Console.WriteLine("{0} {1} {2}",name, sex, age);
Console.WriteLine("工作经历:{0} {1}",work.WorkDate, work.Company);
}
public Object Clone()
{
return (Object)this.MemberwiseClone();
}
}
static void Main(string[] args)
{
Resume a = new Resume("大鸟");
a.SetPersonalInfo("男","29");
a.SetWorkExperience("1998-2000","XX公司")
Resume b = (Resume)a.Clone();
b.setWorkExperience("1998-2006","YY企业")
Resume c = (Resume)a.Clone();
c.SetPersonalInfo("男","24");
c.SetWorkExperience("1998-2003","ZZ企业");
a.Display();
b.Display();
c.Display();
Console.Read();
}
大鸟 男 29
工作经历 1998-2003 ZZ公司
大鸟 男 29
工作经历 1998-2003 ZZ公司
大鸟 男 24
工作经历 1998-2003 ZZ公司
分析:
因为“简历”对象里的数据都是string型的,而string是一种拥有值类型特点的特殊引用类型,(在这里例子中可以看成是值类型。)前者是值类型,后者是类类型(属于引用类型)。所以两次的结果是不一致的。
当执行c.SetWorkExperience("1998-2003","ZZ企业")之后,内存区域的数据变成 1998-2003 ZZ公司。因为引用类型的a、b、c指向的是这一块内存区域。所以a、b、c三个的显示结果都为:工作经历 1998-2003 ZZ公司