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.
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
)