Item 27 避免使用ICloneable接口
实现ICloneable接口,看起来是个不错的选择,想要类型支持拷贝,就实现ICloneable,不想支持拷贝,就不实现ICloneable。但是,大家仔细想一想,你的对象并不是在一个独立的环境中运行,需要考虑到对派生类的影响,基类已经实现了ICloneable接口,派生类也继承了基类的Clone方法,所以派生类最好也支持ICloneable接口,这样所有派生类都应保持一致,所以所有派生类都应实现ICloneable接口,还需要考虑到类的成员都必须支持ICloneable接口或提供一种机制支持拷贝,如果支持深拷贝的对象包含有网状结构的对象,就会使拷贝很成问题。
这里谈到了深拷贝,对应的是浅拷贝。所谓浅拷贝,是创建一个新对象,然后将原对象的所有成员拷贝到新对象中,如果某成员是引用类型,那么仅仅拷贝引用给新对象,也就是说新,旧对象的那个成员引用的是同一个对象。而深拷贝是拷贝所有成员并对引用类型的对象进行递归的拷贝,即对引用的对象也进行拷贝(而不仅仅是引用的拷贝,并且是递归下去,直到递归到只有值类型成员的拷贝)。像值类型和String类型,深拷贝和浅拷贝效果一样,都是完全复制,得到一个与原对象无关新对象(只有内容一样,没有其它关联,对两者中任何一个的修改,不会影响到另一个对象)。
那么当我们定义的类型实现了ICloneable接口时,我们应该实现深拷贝还是浅拷贝呢,这取决于类型本身,但同时在一个类型中混用深拷贝和浅拷贝会导致很多不一致问题,一旦实现了ICloneable接口,这种混用就很难避免,所以定义类型时应该尽量避免实现ICloneable接口,让类更简单一些,使用和实现都相对简单一些。
任何只以值类型和String类型对象作为成员的值类型都不用支持ICloneable接口,用简单的赋值语句要比Clone方法高效得多,Clone方法要对返回的对象进行装箱,才能强制返回一个System.Object对象,而调用者使用对象时又要对其进行拆箱,这又是何苦呢?例如:
{
private int _errorCode;
private int _details;
private string _msg;
}
{
private string _label = "PeterLau";
private int[] _values = new int[10];
public object Clone()
{
BaseType baseType = new BaseType();
baseType._label = this._label;
baseType._values = this._values;
return baseType;
}
}
public class DerivedType : BaseType
{
private double[] _dValues=new double[10];
static void Main()
{
DerivedType derivedType1 = new DerivedType();
DerivedType derivedType2 = derivedType1.Clone() as DerivedType;
if (derivedType2 == null)
Console.WriteLine("null");
}
}
上述这段代码的输出是"null",很明显当子类对象调用Clone()方法时,其实是调用的基类的Clone()方法,该方法返回的是基类的对象,当使用as转换为子类对象时当然返回的是null。即使你解决了这一个问题,基类的Clone()方法也不可能拷贝子类的_dValues字段。所以一旦你的类型实现了ICloneable接口,那么就强迫你的派生类也要正确的实现ICloneable接口(因为子类继承了基类的Clone方法,在客户端可以通过子类对象调用Clone方法,但是调用返回的结果又是错的),在基类中实现ICloneable接口会给派生类带来这样的负担,所以当你的引用类型要实现ICloneable接口时,最好将你的类型定义为sealed。如果你的基类实现了ICloneable接口,于是你的整个继承体系都要实现ICloneable接口时,这时你可以在基类中定义一个抽象的Clone方法,强迫所有派生类提供Clone方法的实现。当然这是出于安全考虑,最好还是不要在非密封类中实现ICloneable接口。
{
private string _label = "PeterLau";
private int[] _values = new int[10];
public BaseType()
{ }
protected BaseType(BaseType baseType)
{
_label = baseType._label;
_values = baseType._values.Clone() as int[];
}
}
public class DerivedType : BaseType,ICloneable
{
private double[] _dValues=new double[10];
public DerivedType()
{
}
private DerivedType(DerivedType right)
: base(right)
{
_dValues = right._dValues;
}
public object Clone()
{
DerivedType derivedType = new DerivedType(this);
return derivedType;
}
static void Main()
{
DerivedType derivedType1 = new DerivedType();
DerivedType derivedType2 = derivedType1.Clone() as DerivedType;
if (derivedType2 == null)
Console.WriteLine("null");
}
}
基类并不实现ICloneable接口; 通过提供一个受保护的构造函数,让派生类可以拷贝基类的成员。叶子类,应该都是密封的,必要它应该实现ICloneable接口。基类不应该强迫所有的派生类都要实现ICloneable接口,但你应该提供一些必要的方法,以便那些希望实现ICloneable接口的派生类可以使用。
ICloneable接口有它的用武之地,但相对于它的规则来说,我们应该避免它。对于值类型,你不应该实现ICloneable接口,应该使用赋值语句。对于引用类型来说,只有在拷贝确实有必要存在时,才在叶子类上实现对ICloneable的支持。基类在可能要对ICloneable 进行支持时,应该创建一个受保护的构造函数。总而言之,我们应该尽量避免使用ICloneable接口。
转载于:https://www.cnblogs.com/net-liu/archive/2009/09/28/1571410.html
Item 27 避免使用ICloneable接口相关推荐
- Effective Modern C++ Item 27 熟悉依万能引用型别进行重载的替代方案
Item 27 熟悉依万能引用型别进行重载的替代方案 Item 26说过,万能引用和重载在一起总会产生各种各样的问题,无论是独立函数,成员函数,都最好不要和万能引用放一起重载,其中构造函数和万能引用放 ...
- Effective C#:避免使用ICloneable接口
最近在学习Bill Wagner的书籍:<Effective C#:50 Specific Ways to Improve Your C#>,虽然是一本很早的书了,但是感觉很实用,并且跟国 ...
- C#的System.ICloneable接口说明
原理 如果我们有两个值类型的变量,将其中一个变量的值赋给另一个,实际上会创建该值的一个副本,这个副本与原来的值没有什么关系--这意味着改变其中一 个的值不会影响另一个变量的值.而如果是两个引用类型的变 ...
- [c#基础]ICloneable接口
摘要 该接口使你能够创建现有对象的副本的自定义的实现.该接口只提供了,一个Clone方法,实现对象的浅拷贝.有浅拷贝,那么就有相对应的深拷贝.但该接口并没有对我们提供,需要我们自己实现. 什么是浅拷贝 ...
- 读书笔记 effective c++ Item 41 理解隐式接口和编译期多态
1. 显示接口和运行时多态 面向对象编程的世界围绕着显式接口和运行时多态.举个例子,考虑下面的类(无意义的类), 1 class Widget { 2 public: 3 Widget(); 4 vi ...
- 2022/10/26 OR 27 关于java的接口初识 猫狗接口/教练学员案例
接口的特点: 接口的关键字 public interface 接口名() 类实现接口的关键字 public class 类名 implements 接口名() 接口的实现参照多态:抽象类多态,接口多 ...
- 提高C#编程水平的50个要点
1.总是用属性 (Property) 来代替可访问的数据成员 2.在 readonly 和 const 之间,优先使用 readonly 3.在 as 和 强制类型转换之间,优先使用 as 操作符 4 ...
- 提高C#编程水平的50个要诀[转载]
一篇旧时的文章,看后觉得还可以,特别贴出来. 提高C#编程水平的50个要点: 1.总是用属性 (Property) 来代替可访问的数据成员 2.在 readonly 和 const 之间,优先使用 ...
- 提高C#编程水平的50个技巧
转自:网络 1.总是用属性 (Property) 来代替可访问的数据成员 2.在 readonly 和 const 之间,优先使用 readonly 3.在 as 和 强制类型转换之间,优先使用 a ...
- 提高.NET编程水平的50个要点(转载)
1.总是用属性 (Property) 来代替可访问的数据成员 2.在 readonly 和 const 之间,优先使用 readonly 3.在 as 和 强制类型转换之间,优先使用 as 操作符 ...
最新文章
- inittab 文件分析
- Autohotkey puretext
- 小游戏专场:腾讯云Game-Tech技术沙龙上海站顺利落下帷幕
- jvm内存结构_浅谈JVM内存结构
- CSHOP后台设置SMTP发邮件提示 Error: need RCPT command 错误解决
- 东北大学计算机 大一物理考试题,东北大学大学物理期末考题及答案Word版
- 报错:The type javax.servlet.http.HttpServletRequest cannot be resolved
- php 时间选择,PHP-在学说2中的日期之间选择条目
- shell练习DAY14
- 路由器-配置(思科)
- ios cell点击对勾_带图像和对勾的iOS自定义TableView
- fpgrowth算法实战 mlib_MLlib--FPGrowth算法
- 控制理论个人学习笔记-非线性系统理论
- C#对STK11.4二次开发的Hello World
- android 手机内存64实际不到,我手机64G都天天清理,为什么内存越来越少?原来方法不对...
- ubuntu14.04 clementine音乐播放器无法播放ape格式解决方法
- 米老师经典语录————再造生命力
- sql函数RIGHT的简单用法
- 2022-02-11 学习记录:通过CSS3的clip-path实现多边形
- Python 爬抖音
热门文章
- python日历下拉框_selenium+Python(Js处理日历控件)
- Collectors.averagingLong()
- 【渝粤教育】国家开放大学2018年秋季 0299-22T中国古代文学(1) 参考试题
- 【渝粤教育】国家开放大学2018年春季 8038-22T实用管理基础 参考试题
- [渝粤教育] 西南科技大学 运输组织学 在线考试复习资料
- 数据科学家应该掌握的12种机器学习算法(附信息图)
- [转]SQL2008关于c001f011的错误解决办法
- 51NOD 1181 质数中的质数(质数筛法)
- 用Unity简单实现第三人称人物的移动和转向
- 欧拉定理、费马小定理及其拓展应用