Clonable object
ClonableObjClass.cs
// Copyright 2012 ESRI
// 
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
// 
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
// 
// See the use restrictions.
// 

using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Collections;

using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.SystemUI;
using ESRI.ArcGIS.Geometry;

namespace ClonableObject
{
  /// <summary>
  /// This call demonstrated an implementation of a hybrid colnable
  /// class which has both .NET members as well as COM members.
  /// </summary>
  [Guid(ClonableObjClass.GUID)]
  [ClassInterface(ClassInterfaceType.None)]
  [ProgId("ClonableObject.ClonableObjClass")]
  public sealed class ClonableObjClass : IClone
  {
    #region different types of class members (COM and .NET)
    public const string GUID = "0678ecf9-4066-4a61-94fb-45d6c4753826";

    private int m_version = 1;
    private ISpatialReference m_spatialRef = null;
    private IPoint m_point = null;
    private string m_name = string.Empty;
    private ArrayList m_arr = null;
    private Guid m_ID;
    #endregion

    #region class constructor
    public ClonableObjClass()
    {
      m_ID = Guid.NewGuid();

      m_spatialRef = new UnknownCoordinateSystemClass();
    }
    #endregion

    #region public class properties
    public string Name
    {
      get { return m_name; }
      set { m_name = value; }
    }

    public int Version
    {
      get { return m_version; }
    }

    public ISpatialReference SpatialReference
    {
      get { return m_spatialRef; }
      set { m_spatialRef = value; }
    }

    public Guid ID
    {
      get { return m_ID; }
    }

    public IPoint Point
    {
      get { return m_point; }
      set { m_point = value; }
    }

    public ArrayList ManagedArray
    {
      get { return m_arr; }
      set { m_arr = value; }
    }
    #endregion

    #region IClone Members

    /// <summary>
    /// Assigns the properties of src to the receiver.
    /// </summary>
    /// <param name="src"></param>
    public void Assign(IClone src)
    {
      //1. make sure that src is pointing to a valid object
      if (null == src)
      {
        throw new COMException("Invalid objact.");
      }

      //2. make sure that the type of src is of type 'ClonableObjClass'
      if (!(src is ClonableObjClass))
      {
        throw new COMException("Bad object type.");
      }

      //3. assign the properties of src to the current instance
      ClonableObjClass srcClonable = (ClonableObjClass)src;
      m_name = srcClonable.Name;
      m_version = srcClonable.Version;
      m_ID = new Guid(srcClonable.ID.ToString());

      //don't clone the spatial reference, since in this case we want both object to 
      //reference the same spatial reference (for example like features in a featureclass 
      //which share the same spatial reference)
      m_spatialRef = srcClonable.SpatialReference;

      //clone the point. Use deep cloning 
      if (null == srcClonable.Point)
        m_point = null;
      else
      {
        IObjectCopy objectCopy = new ObjectCopyClass();
        object obj = objectCopy.Copy((object)srcClonable.Point);
        m_point = (IPoint)obj;
      }

      m_arr = (ArrayList)srcClonable.ManagedArray.Clone();
    }

    /// <summary>
    /// Clones the receiver and assigns the result to clonee.
    /// <returns></returns>
    public IClone Clone()
    {
      //create a new instance of the object
      ClonableObjClass obj = new ClonableObjClass();
      //assign the properties of the new object with the current object's properties.
      //according to each 'Ref' property, the user need to decide whether to use deep cloning
      //or shallow cloning. 
      obj.Assign(this);

      return (IClone)obj;
    }

    /// <summary>
    /// Returns TRUE when the receiver and the other object have the same properties.
    /// </summary>
    /// <param name="other"></param>
    /// <returns></returns>
    public bool IsEqual(IClone other)
    {
      //1. make sure that the 'other' object is pointing to a valid object
      if (null == other)
        throw new COMException("Invalid objact.");

      //2. verify the type of 'other'
      if (!(other is ClonableObjClass))
        throw new COMException("Bad object type.");

      ClonableObjClass otherClonable = (ClonableObjClass)other;

      //test that all ot the object's properties are the same.
      //please note the usage of IsEqual when using arcobjects components that
      //supports cloning
      if (otherClonable.Version == m_version &&
        otherClonable.Name == m_name &&
        otherClonable.ID == m_ID &&
        otherClonable.ManagedArray == m_arr &&
        ((IClone)otherClonable.SpatialReference).IsEqual((IClone)m_spatialRef) &&
        ((IClone)otherClonable.Point).IsEqual((IClone)m_point))

        return true;
    
      return false;
    }

    public bool IsIdentical(IClone other)
    {
      //1. make sure that the 'other' object is pointing to a valid object
      if (null == other)
        throw new COMException("Invalid objact.");

      //2. verify the type of 'other'
      if (!(other is ClonableObjClass))
        throw new COMException("Bad object type.");

      //3. test if the other is the 'this'
      if ((ClonableObjClass)other == this)
        return true;

      return false;
    }

    #endregion
  }
}