zoukankan      html  css  js  c++  java
  • Effective C# Item27:避免ICloneable接口

        一般情况下,我们不建议针对类型实现ICloneable接口。因为如果一个类型支持ICloneable接口,那么该类型的所有派生类都必须实现它,而且类型中所有成员类型也都要实现ICloneable接口,或者有其他创建复制对象的机制,当我们设计的类型包含交织成网状的对象时,支持深复制会变得比较复杂。

        复制操作分为浅复制和深复制两种,其中浅复制是指新对象包含所有成员变量的副本,如果成员变量为引用类型,那么新对象将和原对象引用同样的对象;深复制中新对象也包含所有成员变量的副本,但是所有引用类型的成员变量都会被递归的复制。对于C#来说,内建类型,例如整型,执行深复制和浅复制的结果是相同的。

        任何只包含内建类型成员的值类型都不需要实现ICloneable接口,一个简单的赋值语句要比Clone()方法效率更高,因为Clone()方法必须对返回值进行装箱,才能转换成一个System.Object引用。

        而对于引用类型来说,如果引用类型需要通过实现ICloneable接口的方式来表明自身支持浅复制或者深复制,这时,该类型的派生类也必须实现ICloneable接口。

        来看下面的代码。

    代码
    1 class BaseType : ICloneable
    2 {
    3 private string _label = "class name";
    4 private int [] _values = new int [ 10 ];
    5
    6 public object Clone()
    7 {
    8 BaseType rVal = new BaseType( );
    9 rVal._label = _label;
    10 for( int i = 0; i < _values.Length; i++ )
    11 rVal._values[ i ] = _values[ i ];
    12 return rVal;
    13 }
    14 }
    15
    16  class Derived : BaseType
    17 {
    18 private double [] _dValues = new double[ 10 ];
    19
    20 static void Main( string[] args )
    21 {
    22 Derived d = new Derived();
    23 Derived d2 = d.Clone() as Derived;
    24
    25 if ( d2 == null )
    26 Console.WriteLine( "null" );
    27 }

        上述代码在运行后,我们会发现d2的值时null,这是因为Derived类从BaseType类继承了Clone()方法,但是继承来的实现对Derived类型来说是不正确的,因为它只是克隆了基类,BaseType.Clone()方法创建了一个BaseType类型的对象,而不是一个Derived类型的对象。

        如果需要解决这个问题,我们可以将BaseType类中的Clone()方法声明为abstract,这样所有的派生类都必须实现这个方法。另外,我们还可以通过以下的方式来解决这个问题。

    代码
    1 class BaseType
    2 {
    3 private string _label;
    4 private int [] _values;
    5
    6 protected BaseType( )
    7 {
    8 _label = "class name";
    9 _values = new int [ 10 ];
    10 }
    11
    12 // Used by devived values to clone
    13   protected BaseType( BaseType right )
    14 {
    15 _label = right._label;
    16 _values = right._values.Clone( ) as int[ ] ;
    17 }
    18 }
    19
    20  sealed class Derived : BaseType, ICloneable
    21 {
    22 private double [] _dValues = new double[ 10 ];
    23
    24 public Derived ( )
    25 {
    26 _dValues = new double [ 10 ];
    27 }
    28
    29 // Construct a copy
    30 // using the base class copy ctor
    31   private Derived ( Derived right ) :
    32 base ( right )
    33 {
    34 _dValues = right._dValues.Clone( )
    35 as double[ ];
    36 }
    37
    38 static void Main( string[] args )
    39 {
    40 Derived d = new Derived();
    41 Derived d2 = d.Clone() as Derived;
    42 if ( d2 == null )
    43 Console.WriteLine( "null" );
    44 }
    45
    46 public object Clone()
    47 {
    48 Derived rVal = new Derived( this );
    49 return rVal;
    50 }
    51 }
       

        总之,对于值类型来讲,我们永远都不需要实现ICloneable接口,使用默认的赋值操作就可以了,我们应该为那些确实需要复制操作的”叶子类“实现ICloneable接口,对于那些子类可能需要实现ICloneable接口的基类来说,我们应该为其创建一个受保护的复制构造函数。除此之外,我们应该避免实现ICloneable接口。

  • 相关阅读:
    观察者模式股票提醒
    中介者模式虚拟聊天室
    模板方法模式数据库的连接
    职责链模式财务审批
    期末总结
    软件需求分析考试
    tomcat启动极其慢的解决方法困扰我一年多的问题终于解决
    状态模式银行账户
    解释器模式
    动态加载JS文件提升访问网站速度
  • 原文地址:https://www.cnblogs.com/wing011203/p/1651994.html
Copyright © 2011-2022 走看看