了解GOF写的设计模式中的原型模式都知道其实它讲的就是对象的克隆(Clone).
《设计模式》里写道:原型模式的意图是:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.
其实它讲的就是深度复制,即复制一个现有的对象,改变复制后的属性或字段不影响模型对象.废话不多讲我们直接用一个简单的例子来看看这倒底是个神马意思.我们用一个例子来讲解.
假如现在要你打印出班级学生的所有详细信息.
假设学生信息只有学号、姓名、班级.
我们先打印一个学生的成绩.代码如下:
using
System;
using
System.Collections.Generic;
using
System.Text;
namespace
ConsoleApplication1
{
class
Program
{
static
void
Main(
string
[] args)
{
Student s1
=
new
Student(
"
001
"
,
"
张三
"
,
"
高三(3)班
"
);
s1.Print();
}
}
class
Student
{
private
string
_sno;
private
string
_name;
private
string
_classname;
public
string
Sno
{
get
{
return
_sno; }
set
{ _sno
=
value; }
}
public
string
Name
{
get
{
return
_name; }
set
{ _name
=
value; }
}
public
string
Classname
{
get
{
return
_classname; }
set
{ _classname
=
value; }
}
public
Student()
{
}
public
Student(
string
sno,
string
name,
string
classname)
{
Sno
=
sno;
Name
=
name;
Classname
=
classname;
}
public
void
Print()
{
Console.WriteLine(
"
{0} {1} {2}
"
, Sno, Name, Classname);
}
}
}
结果:
那么打印多个学生的话那么可以直接在Main函数里实例化学生即:
static
void
Main(
string
[] args)
{
Student s1
=
new
Student(
"
001
"
,
"
张三
"
,
"
高三(3)班
"
);
s1.Print();
Student s2
=
new
Student(
"
002
"
,
"
李四
"
,
"
高三(3)班
"
);
s2.Print();
.
.
.
}
但是现在要你动态的复制(Clone)生成对象的话那又该怎么写呢?代码如下
using
System;
using
System.Collections.Generic;
using
System.Text;
namespace
ConsoleApplication1
{
class
Program
{
static
void
Main(
string
[] args)
{
Student s1
=
new
Student(
"
001
"
,
"
张三
"
,
"
高三(3)班
"
);
Student s2
=
(Student)s1.Clone();
s2.Sno
=
"
002
"
;
s2.Name
=
"
李四
"
;
s1.Print();
s2.Print();
}
}
class
Student : ICloneable
{
private
string
_sno;
private
string
_name;
private
string
_classname;
public
string
Sno
{
get
{
return
_sno; }
set
{ _sno
=
value; }
}
public
string
Name
{
get
{
return
_name; }
set
{ _name
=
value; }
}
public
string
Classname
{
get
{
return
_classname; }
set
{ _classname
=
value; }
}
public Student()
{
}
public
Student(
string
sno,
string
name,
string
classname)
{
Sno
=
sno;
Name
=
name;
Classname
=
classname;
}
public
Object Clone()
{
return
this
.MemberwiseClone();
}
public
void
Print()
{
Console.WriteLine(
"
{0} {1} {2}
"
, Sno,Name,Classname);
}
}
}
结果:
可能有同学要问这个this.MemberwiseClone()是个什么方法:其实他就相当于
public
Object Clone()
{
//
return this.MemberwiseClone();
Student obj
=
new
Student();
obj.Sno
=
this
.Sno;
obj.Name
=
this
.Name;
obj.Classname
=
this
.Classname;
return
obj;
}
到这里好像是复制一个完全独立的新对象,跟我们要谈的深复制和浅负责没多大关系.哈哈,那现在转折点要来了.浅复制在不经意间出现了
如果学生类里面加上性别年龄升高并且这三个属性用Feature类来封装.看看结果又会变成什么样 代码如下:
using
System;
using
System.Collections.Generic;
using
System.Text;
namespace
ConsoleApplication1
{
class
Program
{
static
void
Main(
string
[] args)
{
Student s1
=
new
Student(
"
001
"
,
"
张三
"
,
"
高三(3)班
"
);
s1.SetFeature(
"
男
"
,
"
176厘米
"
,
"
18
"
);
Student s2
=
(Student)s1.Clone();
s2.Sno
=
"
002
"
;
s2.Name
=
"
李四
"
;
s2.SetFeature(
"
男
"
,
"
180厘米
"
,
"
19
"
);
s1.Print();
s2.Print();
}
}
class
Student : ICloneable
{
private
string
_sno;
private
string
_name;
private
string
_classname;
private
Feature _fe;
public
string
Sno
{
get
{
return
_sno; }
set
{ _sno
=
value; }
}
public
string
Name
{
get
{
return
_name; }
set
{ _name
=
value; }
}
public
string
Classname
{
get
{
return
_classname; }
set
{ _classname
=
value; }
}
public
Student()
{
_fe
=
new
Feature();
}
public
Student(
string
sno,
string
name,
string
classname)
{
Sno
=
sno;
Name
=
name;
Classname
=
classname;
_fe
=
new
Feature();
}
public
void
SetFeature(
string
sex,
string
high,
string
age)
{
_fe.Sex
=
sex;
_fe.High
=
high;
_fe.Age
=
age;
}
public
Object Clone()
{
return
this
.MemberwiseClone();
}
public
void
Print()
{
Console.WriteLine(
"
{0} {1} {2}
"
, Sno, Name, Classname);
Console.WriteLine(
"
{0} {1} {2}
"
, _fe.Sex, _fe.High, _fe.Age);
}
}
class
Feature
{
private
string
_sex;
private
string
_age;
private
string
_high;
public
string
Sex
{
get
{
return
_sex; }
set
{ _sex
=
value; }
}
public
string
High
{
get
{
return
_high; }
set
{ _high
=
value; }
}
public
string
Age
{
get
{
return
_age; }
set
{ _age
=
value; }
}
public
Feature()
{
}
public
Feature(
string
sex,
string
high,
string
age)
{
Sex
=
sex;
High
=
high;
Age
=
age;
}
}
}
出现了我们意想不到的结果: (这就是所谓的浅复制,改变复制后的实例对象属性会影响到原型的属性值变化)
我们期望的是出现这种结果: (这就是我们期望的深复制,改变复制后的实例对象不会影响到原型的属性值)
为什么会出现这种情况呢,其实我把 public Object Clone()函数换一种写法大家就会明白问题出现在哪里了 public Object Clone()等实现方法如下:
public
Object Clone()
{
//
return this.MemberwiseClone();
Student obj
=
new
Student();
obj.Sno
=
this
.Sno;
obj.Name
=
this
.Name;
obj.Classname
=
this
.Classname;
obj._fe
=
this
._fe;
return
obj;
}
眼尖的同学一眼就发现了问题所在!没错,问题就出现在 obj._fe=this._fe;这句代码身上;
对象是引用类型,这句代码的意思是复制后的s2对象和原型对象s1共用一个_fe,导致s2修改了_fe里面的属性值.原型对象s1的_fe也跟着变.
系统调用 return this.MenberwiseClone()方法就是上面的那些具体代码,这样产生了浅复制.其实想复制的话代码应该是:
public
Object Clone()
{
//
return this.MemberwiseClone();
Student obj
=
new
Student();
obj.Sno
=
this
.Sno;
obj.Name
=
this
.Name;
obj.Classname
=
this
.Classname;
//
obj._fe = this._fe;
obj._fe.Age
=
this
._fe.Age;
obj._fe.High
=
this
._fe.High;
obj._fe.Sex
=
this
._fe.Sex;
return
obj;
}
这样的话就是深复制就是我们期望的结果.
其实
obj._fe.Age = this._fe.Age;
obj._fe.High = this._fe.High;
obj._fe.Sex = this._fe.Sex;
这段代码是实例化一个Feature 对象在内在分配一个对象空间.好让复制后的S1和S2对象不共享同一个Feature对象而已.如果我们还希望能用到this.MenberwiseClone()方法那么该怎么改写代码呢?
using
System;
using
System.Collections.Generic;
using
System.Text;
namespace
ConsoleApplication1
{
class
Program
{
static
void
Main(
string
[] args)
{
Student s1
=
new
Student(
"
001
"
,
"
张三
"
,
"
高三(3)班
"
);
s1.SetFeature(
"
男
"
,
"
176厘米
"
,
"
18
"
);
Student s2
=
(Student)s1.Clone();
s2.Sno
=
"
002
"
;
s2.Name
=
"
李四
"
;
s2.SetFeature(
"
男
"
,
"
180厘米
"
,
"
19
"
);
s1.Print();
s2.Print();
}
}
class
Student : ICloneable
{
private
string
_sno;
private
string
_name;
private
string
_classname;
private
Feature _fe;
public
string
Sno
{
get
{
return
_sno; }
set
{ _sno
=
value; }
}
public
string
Name
{
get
{
return
_name; }
set
{ _name
=
value; }
}
public
string
Classname
{
get
{
return
_classname; }
set
{ _classname
=
value; }
}
public
Student(Feature fe)
{
_fe
=
(Feature)fe.Clone();
}
public
Student()
{
_fe
=
new
Feature();
}
public
Student(
string
sno,
string
name,
string
classname)
{
Sno
=
sno;
Name
=
name;
Classname
=
classname;
_fe
=
new
Feature();
}
public
void
SetFeature(
string
sex,
string
high,
string
age)
{
_fe.Sex
=
sex;
_fe.High
=
high;
_fe.Age
=
age;
}
public
Object Clone()
{
Student obj
=
new
Student(
this
._fe);
obj.Sno
=
this
.Sno;
obj.Name
=
this
.Name;
obj.Classname
=
this
.Classname;
//
obj._fe = this._fe;
//
obj._fe.Age = this._fe.Age;
//
obj._fe.High = this._fe.High;
//
obj._fe.Sex = this._fe.Sex;
return
obj;
}
public
void
Print()
{
Console.WriteLine(
"
{0} {1} {2}
"
, Sno, Name, Classname);
Console.WriteLine(
"
{0} {1} {2}
"
, _fe.Sex, _fe.High, _fe.Age);
}
}
class
Feature :ICloneable
{
private
string
_sex;
private
string
_age;
private
string
_high;
public
string
Sex
{
get
{
return
_sex; }
set
{ _sex
=
value; }
}
public
string
High
{
get
{
return
_high; }
set
{ _high
=
value; }
}
public
string
Age
{
get
{
return
_age; }
set
{ _age
=
value; }
}
public
Feature()
{
}
public
Feature(
string
sex,
string
high,
string
age)
{
Sex
=
sex;
High
=
high;
Age
=
age;
}
public
Object Clone()
{
return
this
.MemberwiseClone();
}
}
}
结果就是我们期望的:
所以要深复制的话且用到MenberwiseClone()方法的话,要保证使用MenberwiseClone()方法的对象类是没用引用类型属性(即没有类对象属性)
上面完整的代码就是著名的设计模式里的原型模式.如果你搞不懂深复制和浅复制也就淡不上搞懂原型模式了
给大家留一个思考 如果Feature 里面再包含一个兴趣类即Interest类里面包含三个兴趣属性 那么该如何深拷贝Student对象呢(也就是怎么实现原型模式)且用到MenberwiseClone()方法当然了也可以不用到.
class
Feature :ICloneable
{
private
string
_sex;
private
string
_age;
private
string
_high;
private
Interest _interest;
}
class
Interest
{
private
string
_it1;
private
string
_it2;
private
string
_it3;
public
string
It1
{
get
{
return
_it1; }
set
{ _it1
=
value; }
}
public
string
It2
{
get
{
return
_it2; }
set
{ _it2
=
value; }
}
public
string
It3
{
get
{
return
_it3; }
set
{ _it3
=
value; }
}
public
Interest()
{
}
public
Interest(
string
it1,
string
it2,
string
it3)
{
It1
=
it1;
It2
=
it2;
It3
=
it3;
}
}