9

我的问题如下:

我有一个需要抽象的基类。它有几个派生类,每个派生类都有自己的特殊属性,这些属性包含在 Properties 成员中。

我需要能够创建这些派生类之一的新实例,以便所有成员都是等效的,但修改新实例不会修改原始实例。

最后,我想这样做,而不必在基类的每个派生类型中进行硬编码。(诚​​然,这将是最简单的解决方案,但这不是重点)

所有派生类都满足与基类的“is-a”关系。

这是代码:

public abstract class BaseClass
{
    //Default properties here
    int x, y, z, ...;

    //Custom made class to hold custom properties
    protected Attributes Properties;

    public BaseClass createNewInstance()
    {
        return createNewInstanceStep1();
    }

    //Each derived class implements their own version of this,
    //to handle copying any custom members contained in Properties.
    protected abstract BaseClass createNewInstanceStep2();

    protected BaseClass createNewInstanceStep1()
    {
        BaseClass newInstance = new BaseClass(); // <- Doesn't work because class is abstract

        //Copy default properties
        newInstance.x = x;
        newInstance.y = y;
        newInstance.z = z;

        //Call the new instance's step 2 method, and return the result.
        return newInstance.createNewInstanceStep2();
    }
}

这段代码的问题是 BaseClass newKeyFrame = new BaseClass(); 线。由于该类是抽象的,因此您无法创建它的实例。

问题是我需要能够调用派生类的任何类型的构造函数,因为它们的构造函数中都有不同的代码,无法共享。

我听说使用反射可能是一个可行的解决方案,但我不知道如何。

我怎样才能解决这个问题,而不必在每个派生类型的案例中进行硬编码?

4

4 回答 4

4

你可以做createNewInstanceStep1通用的。我还修改了Step2to be 类型void(我希望它修改当前实例,所以return this;无论如何都会返回),因为否则它并没有我想在这里使用它的方式真正有意义。如果像这样改变它没有意义,那么我只使这种方法通用的整个方法将行不通。

现在使用反射来createNewInstance调用.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;
}

如果这不起作用,另一种方法是自引用泛型类型。不过,最好避免这种情况,因为它令人困惑并且总体上不是一个好的设计。

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();
        ...
于 2013-10-30T19:47:50.620 回答
3

让派生类实例化新实例有什么问题?

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;
    }
}
于 2013-10-30T20:32:33.453 回答
0

您可以将 createNewInstanceStep1 设为一个受保护的 void init 方法来启动该对象,并在每个子类的构造函数中调用它。

于 2013-10-30T20:10:10.933 回答
0

你怎么知道要创建什么类型的实例?如果您将拥有派生类的现有实例并希望创建另一个与其基本相同的实例,则应该让您的基类型实现一个受保护的虚拟CloneBase方法,该方法调用MemberwiseClone并执行基类型知道的任何深度复制,并且一个公共Clone方法,它链接CloneBase并转换为基类型。每个派生类型都应该覆盖CloneBase以链接base.CloneBase并添加任何必要的额外深度复制(如果不需要额外的深度复制逻辑,则可以省略该步骤),并且还Clone使用链接到CloneBase并将结果转换为其类型的方法来隐藏公共方法[使用单独CloneBase的可以同时声明一个新的Clone具有不同签名的方法,同时还覆盖和链接到基类方法]。

如果您将拥有新类的现有实例,但希望从其他实例复制其属性,则可以有一个抽象ConstructInstanceLike(x)方法,每个派生类型都将实现该方法以调用其构造函数之一,或克隆自身并修改克隆以匹配传入的对象。这两种方法都不是非常优雅,但都可以工作。

如果您没有新类的现有实例,则需要一些其他方法来获取适当类型的内容。最好的方法可能是存储一组Func<TParams, TResult>委托,一个代表每个感兴趣的派生类型,然后调用其中一个函数来生成相关派生类型之一的对象。也可以定义一个接口

IFactory<TParam, TResult> { TResult Create(TParams param); }

但在许多情况下,委托会更方便使用。

于 2013-10-30T20:20:58.887 回答