My problem is as follows:
I have a base class that needs to be abstract. It has several derived classes, each with their own special properties that are contained in
You can make createNewInstanceStep1 a protected void init method that initiates this object, and call it in each subclass's constructor.
You could make createNewInstanceStep1
generic. I've also modified the Step2
to be type void
(I'm expecting it to modify the current instance, so the return would always be return this;
anyway), because otherwise it doesn't really make sense the way I'd like to use it here. If it doesn't make sense to change it like this, then my whole approach of only making this method generic won't work.
And createNewInstance
now uses reflection to call the equivalent of return createNewInstanceStep1<this.GetType()>();
.
public BaseClass createNewInstance()
{
var method = typeof(BaseClass).GetMethod("createNewInstanceStep1", BindingFlags.NonPublic | BindingFlags.Instance).MakeGenericMethod(this.GetType());
var value = method.Invoke(this, null);
return (BaseClass)value;
}
//Each derived class implements their own version of this,
//to handle copying any custom members contained in Properties.
protected abstract void createNewInstanceStep2();
protected T createNewInstanceStep1<T>() where T : BaseClass, new()
{
T newInstance = new T(); // works!
//Copy default properties
newInstance.x = x;
newInstance.y = y;
newInstance.z = z;
//Call the new instance's step 2 method, and return the result.
newInstance.createNewInstanceStep2();
return newInstance;
}
If this won't work, another approach is a self-referential generic type. It's good to avoid this, though, because it's confusing and overall not a good design.
public sealed class SubClass : BaseClass<SubClass>
{
protected override SubClass createNewInstanceStep2()
{
Console.WriteLine("In step 2");
return this;
}
}
public abstract class BaseClass<T> where T : BaseClass<T>, new()
public T createNewInstance()
{
return createNewInstanceStep1();
}
//Each derived class implements their own version of this,
//to handle copying any custom members contained in Properties.
protected abstract T createNewInstanceStep2();
protected T createNewInstanceStep1()
{
T newInstance = new T();
...
What's the problem with letting the derived classes do the instantiation of the new instance?
public abstract class BaseClass
{
//Default properties here
int x, y, z, ...;
//Custom made class to hold custom properties
protected Attributes Properties;
public abstract BaseClass createNewInstance();
protected void CopyData(BaseClass original)
{
this.x = original.x;
this.y = original.y;
this.z = original.z;
}
}
public class ChildClass : BaseClass
{
public BaseClass createNewInstance()
{
ChildClass newInstance = new ChildClass();
newInstance.CopyData(this);
// Do any additional copying
return newInstance;
}
}
How are you going to know what type of instance to create? If you'll have an existing instance of the derived class and want to create another instance which is basically identical to it, you should have your base type implement a protected virtual CloneBase
method which calls MemberwiseClone
and does any deep copying the base type knows about, and a public Clone
method which chains to CloneBase
and casts to the base type. Each derived types should override CloneBase
to chain to base.CloneBase
and add any necessary additional deep copying (that step may be omitted if no additional deep-copy logic is required), and also shadow the public Clone
method with one that chains to CloneBase
and casts the result to its type [using a separate CloneBase
makes it possible to both declare a new Clone
method with a different signature while also overriding and chaining to the base-class method].
If you'll have an existing instance of the new class, but want its properties to be copied from some other instance, could have an abstract ConstructInstanceLike(x)
method which each derived type would implement to either call one of its constructors, or clone itself and modify the clone to match the passed-in object. Neither approach is terribly elegant, but either can work.
If you won't have an existing instance of the new class, you'll need some other means of getting something of the appropriate type. The nicest approach is probably to store a collection of Func<TParams, TResult>
delegates, one for each derived type of interest, and then invoke one of those functions to generate an object of one of the associated derived type. It would also be possible to define an interface
IFactory<TParam, TResult> { TResult Create(TParams param); }
but in many cases a delegate will be more convenient to work with.