Search code examples
c#interfaceclone

Why doesn't my clone code compile?


I have this simple clone interface that I'd like to use. It looks like it should compile, but it doesn't. I get a message saying that my BObject class doesn't implement DeepClone(). I don't understand this because I have a DeepClone() method and my BObject class implements IObject.

interface IDeepCloneable<T>
{
    T DeepClone();
}

interface IObject : IDeepCloneable<IObject>
{
    string Name { get; }
    double Sales { get; }
}

//'BObject' does not implement interface member
//  'IDeepCloneable<IObject>.DeepClone()'
class BObject : IObject
{
    public string Name { get; set; }
    public double Sales { get; set; }

    public BObject DeepClone()
    {
        return new BObject() { Name = this.Name, Sales = this.Sales };
    }
}

Am I declaring my interfaces wrong?


Or maybe the DeepClone implementation? I could use this code:

public IObject DeepClone() //returns an IObject instead of a BObject
{
    return new BObject() { Name = this.Name, Sales = this.Sales };
}

The problem I have is that there's no check that the BObject.DeepClone() method returns a BObject as a result. I could have a class that looks like this:

class BObjectImposter : IObject
{
    public string Name { get; set; }
    public double Sales { get; set; }

    public IObject DeepClone()
    {
        //returns a BObject instead of a BObjectImposter
        return new BObject() { Name = this.Name, Sales = this.Sales };
    }
}

Using this class, I could write this:

BObjectImposter objImp = new BObjectImposter();
IObject copy = objImp.DeepClone();

I might expect that copy is an implementation of BObjectImposter, but it's actually an implementation of an unrelated class BObject that happens to also implement IObject. I understand that the point of interfaces is that it shouldn't matter which implementation I use, but this doesn't seem like good code to me. Maybe somewhere in my BObjectImposter implementation I expect DeepClone() to return a BObjectImposter object. Also, one implementation of IObject shouldn't depend on another implementation of IObject.


Maybe I could make IObject an abstract class and declare DeepClone() there. This seems like it might break my design if I have one implementation (call it ObjectA) where I need to set Name before setting Sales in the constructor, and another implementation (call it ObjectB) where I need to set Sales before setting Name in the constructor.


Solution

  • As you've hinted at in your question, IObject implements IDeepClonable<IObject>, so its DeepClone() method must return IObject.

    You need to use the CRTP all the way:

    interface IObject<T> : IDeepCloneable<T> where T : IObject<T>
    class BObject : IObject<BObject>
    

    (you also ought to add where T : IDeepCloneable<T> to IDeepCloneable)