Question

As I've learned that it is not advised to implement ICloneable (due to the fact that it does not differentiate between Deep Copy or Shallow Copy), I'm trying to determine whether I should implement it as an abstract or an interface.

I feel that my implementation would stay largely the same, e.g. a binary deep copy and a MemberwiseClone shallow copy, so to that end I felt that an abstract method would be ideal. However, my understanding is also that C# does not do Multiple Inheritance, thus if I ever need to use another abstract class, then I no longer can.

In that case, I feel that implementing a custom ICloneable (e.g. ICustomCloneable) would be the better option, but if the implementation is effectively the same across many classes, I feel like I'm not adequately taking advantage of code reuse.

That being said, is it valid to use an interface to keep the abstract inheritance clear for more important things in my cloneable classes? Or is there another way to do this?

Alternatively, is it valid (read: not smelly) for an abstract to implement another abstract? This would be my guess as to getting around the single-inheritance that prevents me from implementing the CloneCapable class as well as another abstract, but it sounds like it might be questionable. e.g.:

public abstract class CloneCapable
{
  public object ShallowCopy()
  {
    // implementation
  }

  public object DeepCopy()
  {
    // implementation
  }
}

public abstract class ClassA : CloneCapable {}

// abstract-abstract since I can't do ClassB : ClassA, CloneCapable
public abstract class ClassB : ClassA {} 
Was it helpful?

Solution

I would definitely make this an interface. The reason being is that, interfaces are supposed to be very general, and that's why we can implement multiple interfaces. If there is some boiler-plate code you want to write, there's nothing stopping you taking advantage of both interfaces and abstract classes.

public interface ICustomCloneable<T>
{
    T ShallowCopy();
    T DeepCopy();
}

public abstract class CustomCloneable<T> ICustomCloneable<T> where T : class
{
    public T ShallowCopy() { return ShallowCopy(this); }
    public T DeepCopy() { return DeepCopy(this); }

    // static helpers
    public static object ShallowCopy(T obj) { /* boilerplate implementation */ }
    public static object DeepCopy(T obj) { /* boilerplate implementation */ }
}


public class ClassA : CustomCloneable<ClassA> { /* Use boilerplate functionality */ }
public class ClassB : SomeOtherClass, ICustomCloneable<ClassB>
{
    // implement ICustomCloneable using static helpers
    public ClassB ShallowCopy() { return CustomCloneable<ClassB>.ShallowCopy(this); }
    public ClassB DeepCopy() { return CustomCloneable<ClassB>.DeepCopy(this); }
}

I've used generics here, but there's no reason you need to... It might even be desirable not to. This approach allows you to write boilerplate code, but not be tied down by it:

public class ClassC : ICustomCloneable<ClassC>
{
    public ClassC ShallowCopy() { /* Do special cloning for ClassC */ }
    public ClassC DeepCopy() { /* Do special cloning for ClassC */ }
}

OTHER TIPS

I was thinking that creating an interface was the way to go, but then I found this question and the first answer. That is a fine way to do the cloning, but I thought that that might go very well with a Custom Extension Method so I wrote the fallowing code, based on the code in the first post and on the MS help page:

Some classes to play with:

[Serializable]
public abstract class Base
{
    public abstract int m1();
}

[Serializable]
public class derived : Base
{
    public int a = 42;
    public override int m1()
    {
        throw new NotImplementedException();
    }
}

A class with an extension method based on code samples from both linkes

//Extension methods must be defined in a static class 
public static class StringExtension
{
    // This is the extension method. 
    // The first parameter takes the "this" modifier
    // and specifies the type for which the method is defined. 
    public static T MyCloneExtension<T>(this T t)
    {
        // Code in this function was copied from https://stackoverflow.com/questions/78536/deep-cloning-objects-in-c-sharp
        if (!typeof(T).IsSerializable)
        {
            throw new ArgumentException("The type must be serializable.", "source");
        }

        // Don't serialize a null object, simply return the default for that object
        if (Object.ReferenceEquals(t, null))
        {
            return default(T);
        }

        IFormatter formatter = new BinaryFormatter();
        Stream stream = new MemoryStream();
        using (stream)
        {
            formatter.Serialize(stream, t);
            stream.Seek(0, SeekOrigin.Begin);
            return (T)formatter.Deserialize(stream);
        }
    }
}

and finally a call to clone an object

derived d = new derived();
derived d2 = d.MyCloneExtension<derived>();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top