views:

120

answers:

2

Basically my setting is this:

public abstract class BaseObject{
    public abstract BaseObject Clone();
}

public class DerivedObject : BaseObject{
    public DerivedObject Clone()
    {
        //Clone logic
    }
}

The above code doesn't compile because it isn't possible to change the return type when overriding a method.

Is it possible to achieve that every derived type's Clone method returns an argument of it's own type (maybe through generics)?

+3  A: 

Well, C# doesn't allow covariant return types as you've found... but you can use generics:

public abstract class BaseObject<T> where T : BaseObject<T>
{
    public abstract T Clone();
}

public class DerivedObject : BaseObject<DerivedObject>
{
    public override DerivedObject Clone()
    {
         // ...
    }
}

This solution can be a pain in various ways - not least because it's hard to understand - but it can work reasonably well in many situations.

EDIT: The reason I've included the constraint on T is so that BaseObject can call "its own" methods on instances of T, which is usually very handy. If you don't need this though, you can lose the constraint.

Jon Skeet
Could you describe some of the various ways that this solution could be a pain?I'm trying to figure out whether it's worth to take this pain for the lesser pain of not having to typecast every single time I use the Clone method.
chrischu
@Mehrdad: Fixed, thanks. @crischu: Well, there's nothing to say that another class can't derive from `BaseObject<DerivedObject>` for one thing - so it's not foolproof. Also if you have to interact with another type, it can get *really* hairy - in Protocol Buffers I've got `IMessage<TMessage,TBuilder> where TMessage : IMessage<TMessage,TBuilder> where TBuilder : IBuilder<TMessage, TBuilder>` for example. Nasty. It's mostly okay, but you need to keep a cool head.
Jon Skeet
A: 

You can do something like this. Instead of returning default(T), return something based on the cloning logic.

public class MyBase<T>
{
    public T Clone()
    {
        return default(T);
    }
}

public class MyDerived : MyBase<MyDerived>
{
}

By the way, for object cloning I like to serialize the current object to memory using the binary serializer, then deserialize that memory back into a new instance.

Eric J.