Effective C# 学习笔记(三十二)避免使用ICloneable接口
2011-07-17 23:34
501 查看
实现ICloneabl接口听起来很美,不是么?还真不是,你要是为某个类实现了ICloneable接口,你就得为它的子类和相关的深拷贝和浅拷贝担心,注意每一属性的可被复制的深浅及其被复制的必要性。这看起来是多么大的一个工程,也许远远超出你的想象。
首先来看纯值类型(不包含引用类型属性的值类型),这种类型没有必要实现ICloneable接口,因为这种类型只需为新对象赋值,就可将值赋值到新对象中,调用Clone方法的话,即让返回值装了箱,你还得在给新对象赋值时进行拆箱,性能上得不偿失。
而对于包含引用类型的值类型及引用类型来说,应该对该值类型的引用类型属性进行分析,以保证整个类型的Copy逻辑是一个深拷贝的逻辑。而且对于引用类型实现ICloneable接口,其子类的实现也是一个很大包袱。看如下代码:
class BaseType :
ICloneable
{
private
string label = "class name";
private
int[] values = new int[10];
public
object Clone()
{
BaseType
rVal = new BaseType();
rVal.label
= label;
for
(int i = 0; i < values.Length; i++)
rVal.values[i]
= values[i];
return
rVal;
}
}
class Derived :
BaseType
{
private
double[] dValues = new double[10];
static
void Main(string[] args)
{
Derived
d = new Derived();
Derived
d2 = d.Clone() as Derived;
if
(d2 == null)
Console.WriteLine("null");
}
}
上面的代码的父类BaseType实现了ICloneable,但子类调用父类的Clone方法时返回的对象为BaseType,所以上面的方法对于d2的赋值只能是null ,因为他不能转换为Derived类型。
而正确的实现方式应该如下,让子类实现ICloneable接口,并让父类实现一个protected修饰的以自身类型为参数的构造函数,供子类在其Clone方法中调用,以初始化Copy对象的父类的值,代码如下:
class BaseType
{
private
string label;
private
int[] values;
protected
BaseType()
{
label
= "class name";
values
= new int[10];
}
//
Used by devived values to clone
protected
BaseType(BaseType right)
{
label
= right.label;
values
= right.values.Clone() as int[];
}
}
sealed class Derived
: BaseType, ICloneable
{
private
double[] dValues = new double[10];
public
Derived()
{
dValues
= new double[10];
}
//
Construct a copy
//
using the base class copy ctor
private
Derived(Derived right) :base(right)
{
dValues
= right.dValues.Clone()
as
double[];
}
public
object Clone()
{
//这里通过重载的私有构造函数来创建Copy的父类中的属性和子类中的属性
Derived
rVal = new Derived(this);
return
rVal;
}
}
注意:这里的子类用了sealed关键字,也就是提醒你适可而止,否则随着继承的深度加深你要做的事就会越来越多,还是避免使用其为好。
首先来看纯值类型(不包含引用类型属性的值类型),这种类型没有必要实现ICloneable接口,因为这种类型只需为新对象赋值,就可将值赋值到新对象中,调用Clone方法的话,即让返回值装了箱,你还得在给新对象赋值时进行拆箱,性能上得不偿失。
而对于包含引用类型的值类型及引用类型来说,应该对该值类型的引用类型属性进行分析,以保证整个类型的Copy逻辑是一个深拷贝的逻辑。而且对于引用类型实现ICloneable接口,其子类的实现也是一个很大包袱。看如下代码:
class BaseType :
ICloneable
{
private
string label = "class name";
private
int[] values = new int[10];
public
object Clone()
{
BaseType
rVal = new BaseType();
rVal.label
= label;
for
(int i = 0; i < values.Length; i++)
rVal.values[i]
= values[i];
return
rVal;
}
}
class Derived :
BaseType
{
private
double[] dValues = new double[10];
static
void Main(string[] args)
{
Derived
d = new Derived();
Derived
d2 = d.Clone() as Derived;
if
(d2 == null)
Console.WriteLine("null");
}
}
上面的代码的父类BaseType实现了ICloneable,但子类调用父类的Clone方法时返回的对象为BaseType,所以上面的方法对于d2的赋值只能是null ,因为他不能转换为Derived类型。
而正确的实现方式应该如下,让子类实现ICloneable接口,并让父类实现一个protected修饰的以自身类型为参数的构造函数,供子类在其Clone方法中调用,以初始化Copy对象的父类的值,代码如下:
class BaseType
{
private
string label;
private
int[] values;
protected
BaseType()
{
label
= "class name";
values
= new int[10];
}
//
Used by devived values to clone
protected
BaseType(BaseType right)
{
label
= right.label;
values
= right.values.Clone() as int[];
}
}
sealed class Derived
: BaseType, ICloneable
{
private
double[] dValues = new double[10];
public
Derived()
{
dValues
= new double[10];
}
//
Construct a copy
//
using the base class copy ctor
private
Derived(Derived right) :base(right)
{
dValues
= right.dValues.Clone()
as
double[];
}
public
object Clone()
{
//这里通过重载的私有构造函数来创建Copy的父类中的属性和子类中的属性
Derived
rVal = new Derived(this);
return
rVal;
}
}
注意:这里的子类用了sealed关键字,也就是提醒你适可而止,否则随着继承的深度加深你要做的事就会越来越多,还是避免使用其为好。
相关文章推荐
- Effective C# 学习笔记(九) 在你的API中避免使用类型转换运算
- 微软企业库4.1学习笔记(三十二)数据访问模块 在应用中使用数据访问模块
- Effective Java学习笔记: 第59条 避免不必要地使用受检的异常
- Effective C# 学习笔记(三)在类型转换上多使用 as 和 is
- Effective C# 学习笔记(四十三)使用Expression处理绑定(属性值更改)事件
- Effective C# 学习笔记(四)使用Conditional Attributes 替代 #if
- Effective C# 学习笔记(十三)对静态类成员使用合适的初始化方式
- Effective C# 学习笔记(四十)使用Dynamic处理匿名类型参数
- Effective Java 学习笔记之第七条——避免使用终结(finalizer)方法
- Effective C#学习笔记:适当使用.NET运行时诊断
- Effective C# 学习笔记(十五)使用Using和Try/Finally 进行资源清理
- Java:Effective Java 学习笔记(第48条:如果需要精确的答案,请避免使用float和double)
- Effective C# 学习笔记(四十八)使用安全代码
- STL学习笔记之使用reserve来避免不必要的重新分配
- 微软企业库4.1学习笔记(三十二)数据访问模块 在应用中使用数据访问模块
- Effective C# 学习笔记(四十四)合理地在C#中使用Dynamic特性
- 微软企业库4.1学习笔记(三十二)数据访问模块 在应用中使用数据访问模块
- Effective C# 学习笔记(三十九) 使用Dynamic处理范型参数的运行时类型
- Effective C# 学习笔记(三十三) 只在更新基类时,使用new关键字
- Effective C# 学习笔记(四十二)理解Expression API的使用方式