tags:

views:

442

answers:

4

I need a method that creates an empty clone of an object in a base class? For instance:

public class ChildClass : ParentClass
{
   public ChildClass()
   {
   }
}
public class ParentClass
{
  public SomeMethod()
  {
     // I want to create an instance of the ChildClass here
  }
}

Up until now, we have an abstract method defined in the parent class. And, all of the child classes implement them. But, the implementation is the same for all, just a different type.

public class ChildClass : ParentClass
{
   public ChildClass()
   {
   }
   public ParentClass CreateEmpty()
   {
      return new ChildClass();
   }
}
public class ParentClass
{
  public SomeMethod()
  {
     // I want to create an instance of the ChildClass here
     ParentClass empty = CreateEmpty();

  }
  public abstract ParentClass CreateEmpty();
}

Is there any way to do this from the parent class so that I don't have to keep implementing the same logic for each different child class? Note that there may be more levels of inheritance (i.e. ChildChildClass : ChildClass : ParentClass).

+3  A: 

If using reflection isn't a problem to you, you could do it using Activator class:


//In parent class
public ParentClass CreateEmpty()
{
    return (ParentClass)Activator.CreateInstance(this.GetType());
}

This will return empty object of the type you want. Notice that this method does not need to be virtual.

On the other hand, I think that your current approach is perfectly fine, few more lines of code aren't so bad.

Ravadre
Ahh, reflection. Up until now, I had found no real use for it. Thanks.
bsh152s
A: 

This is somewhat experimental. I don't know whether this will lead to a cyclic dependency. Haven't touched C# for some months.

public class ParentClass<T> where T : ParentClass<T>, new() { // fixed
    public ParentClass() {
        var x = new T(); // fixed, was T.new()
    }
}

public class ChildClass : ParentClass<ChildClass> {
    public ChildClass() { }
}

Otherwise go for the ReflectionCode by Ravadre.

Marcel J.
I think you mean "new T()".
Jon Skeet
Also, you need to write "where T : ParentClass<T>, new()"
Ravadre
http://ravadre.pastebin.com/m74c16ab - here's a complete example, there's one problem with it - how to inherit from ChildClass now? :). It's not generic like ParentClass, so this solution will get more and more complicated.
Ravadre
Whoops, good point.
Marcel J.
A: 

You can make a deep clone of the object using the binary serializer.

EDIT: Just noticed the word "empty" next to clone (which I thought was an oxymoron). Leaving this response up anyhow hoping it will help others that find this question because they are looking to do a regular clone.

Eric J.
A: 

I'm using the following pattern.

Pros:

  • This pattern secure the type-safety of cloning in private and public sides of classes.
  • The output class will be always correct.
  • You never forgot override the "clone" method. The "MyDerivedClass" never returns another class than the "MyDerivedClass".

Cons:

  • For one class, you need create one interface and two classes (prototype and final)

Sample:

// Common interface for cloneable classes.
public interface IPrototype : ICloneable {
    new IPrototype Clone();
}

// Generic interface for cloneable classes.
// The 'TFinal' is finaly class (type) which should be cloned.
public interface IPrototype<TFinal> where TFinal : IPrototype<TFinal> {
    new TFinal Clone();
}

// Base class for cloneable classes.
// The 'TFinal' is finaly class (type) which should be cloned.
public abstract class PrototypeBase<TFinal> : IPrototype<TFinal> where TFinal : PrototypeBase<TFinal> {
    public TFinal Clone() {
        TFinal ret = this.CreateCloneInstance();
        if ( null == ret ) {
            throw new InvalidOperationException( "Clone instance was not created." );
        }

        this.FillCloneInstance( ret );
        return ret;
    }

    // If overriden, creates new cloned instance
    protected abstract TFinal CreateCloneInstance();

    // If overriden, fill clone instance with correct values.
    protected abstract void FillCloneInstance( TFinal clone );

    IPrototype IPrototype.Clone() { return this.Clone(); }
    object ICloneable.Clone() { return this.Clone(); }
}

// Common interface for standalone class.
public interface IMyStandaloneClass : IPrototype<IMyStandaloneClass> {
    string SomeText{get;set;}
    string SomeNumber{get;set;}
}

// The prototype class contains all functionality exception the clone instance creation.
public abstract class MyStandaloneClassPrototype<TFinal> : PrototypeBase<TFinal>, IMyStandaloneClass where TFinal : MyStandaloneClassPrototype<TFinal> {
    public string SomeText {get; set;}
    public int SomeNumber {get; set}

    protected override FillCloneInstance( TFinal clone ) {
        // Now fill clone with values
        clone.SomeText = this.SomeText;
        clone.SomeNumber = this.SomeNumber;
    }
}

// The sealed clas contains only functionality for clone instance creation.
public sealed class MyStandaloneClass : MyStandaloneClassPrototype<MyStandaloneClass> {
    protected override MyStandaloneClass  CreateCloneInstance() {
        return new MyStandaloneClass();
    }
}

public interface IMyExtendedStandaloneClass : IMyStandaloneClass, IPrototype<IMyExtendedStandaloneClass> {
    DateTime SomeTime {get; set;}
}

// The extended prototype of MyStandaloneClassPrototype<TFinal>.
public abstract class MyExtendedStandaloneClassPrototype<TFinal> : MyStandaloneClassPrototype<TFinal> where TFinal : MyExtendedStandaloneClassPrototype<TFinal> {
    public DateTime SomeTime {get; set;}

    protected override FillCloneInstance( TFinal clone ) {
        // at first, fill the base class members
        base.FillCloneInstance( clone );

        // Now fill clone with values
        clone.SomeTime = this.SomeTime;
    }
}

public sealed class MyExtendedStandaloneClass : MyExtendedStandaloneClassPrototype<TFinal> {
    protected override MyExtendedStandaloneClass CreateCloneInstance() {
        return new MyExtendedStandaloneClass 
    }
}
TcKs