创建一个基类方法,根据调用该方法的子类实例化一个新的子类对象

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了创建一个基类方法,根据调用该方法的子类实例化一个新的子类对象相关的知识,希望对你有一定的参考价值。

我搜索和搜索过,但说实话,我真的不知道该找什么。我有这个名为Item的抽象基类,我将有很多子类,它们都需要在基类中使用方法Split(double amount)。我希望该方法以某种方式知道哪个子类调用它并能够实例化该类型的新对象。这是我的一个奇怪的游戏,但我需要这个特定的方法将一堆物品分成两个......

!基类被称为Item,即使子类将表示项类型和该项类型的数量,有点像某些游戏中的堆栈...... !!!

这是我到目前为止所得到的:

public abstract class Item
{
    public double Quantity { get; private set; }

    public Item()
    {
        Quantity = 1;
    }

    public Item(double quantity)
    {
        Quantity = quantity;
    }

    public bool Merge(Item item)
    {
        if (item.GetType() == GetType())
        {
            Quantity += item.Quantity;
            return true;
        }
        return false;
    }

    public Item Split(double amount, Type häst)
    {
        Quantity -= amount;
        //Here I'd like to do something like "new the-type-that-called-this-method()" 
    }
}

我知道我可以在基类中使用抽象方法,并在每个子类中覆盖它,但是我必须重新编写完全相同的代码我不知道多少次...我想这个如果我找不到更好的方法,那就是我要做的......

顺便说一句,如果您有任何其他建议,或者您认为我的组织方式是愚蠢的,那么请不要犹豫告诉我,我从来没有做过这样的事情,而且我倾向于过于复杂化简单的事情而只是让它变得更难我出于某种原因......

更好的解释???例如,如果我有一个200 IronOre的“堆栈”(让我们说IronOre : Item,并且这个实例存储在myIronPile中)那么我想拿70个IronOre并把它放在一个新的“堆栈”中。所以我想打电话给myOtherIronPile = myIronPile.Split(70)。这将从myIronPile中移除70并将其放入存储在IronOre中的新myOtherIronPile实例中。

任何帮助都非常适用!谢谢! :d

答案

首先,Split似乎是你的方法的坏名字。当我看到你的方法的名字时,首先想到它的作用是:给定一个项目,它将它分成两个新项目。但这不是它的作用,它实际上改变了给定的项目并将其中的一部分提取到一个新的项目中。

嗯......不是Extract更好的名字?

public Item Extract(double amount) 

好的,现在当我看到这种方法时,我的第一印象实际上与它的确实相符。

我们也可以使用泛型来改变它,使其尽可能强类型化。如果我们使用静态方法,我们也可以利用类型推断来帮助我们(我们将略微修改名称以使其更符合它静态的):

 public static T ExtractFrom<T>(T item, double amount)
    where T: Item
 {
     item.Quantity -= amount;
     return (T)Activator.CreateInstance(
         item.GetType(), new object[] { amount });
 }

你甚至可以考虑将其作为一种扩展方法来实现,尽管如果可能的话我尽量避免改变扩展方法。

请注意,我使用的是item的运行时类型而不是typeof(T)。这很简单是因为类型推断很可能会解析为Item而你会因为无法直接实例化抽象类而得到运行时错误。

另一答案

要创建给定类型的对象,可以使用Activator.CreateInstance(来自System.Reflection命名空间):

public Item Split(double amount)
{
    Quantity -= amount;
    return Activator.CreateInstance(GetType(), new object[] { amount }) as Item;
}

GetType是一个虚拟调用,以最派生的形式返回对象的“真实”类型。


更好的方法是在对派生类型而不是基类Item进行操作时创建泛型方法:

public static T Split<T>(T item, double amount) where T: Item
{
    item.Quantity -= amount;
    return Activator.CreateInstance(typeof(T), new object[] { amount }) as T;
}

然后你就这样使用它:

var myIronPile = new IronOre(200);
var myOtherIronPile = Item.Split(myIronPile, 70);

但是要小心在Item变量上使用这些操作,因为这不起作用:

List<Item> items = new List<Item>();
items.Add(new IronOre(200));
var myOtherPile = Item.Split(items[0], 70); // call to Item.Split<Item>(...) !

这将使Activator.CreateInstance抛出异常,因为它想要实例化的类型是抽象的。

List<Item> items = new List<Item>();
items.Add(new IronOre(200));

var myOtherPile = Item.Split(items[0] as IronOre, 70); // works

IronOre item = items[0] as IronOre;
myOtherPile = Item.Split(item, 70); // also works

其他解决方案是将运行时类型与GetType一起使用,这会导致可能无法察觉的性能损失(typeof是一个编译时构造):

public static T Split<T>(T item, double amount) where T: Item
{
    item.Quantity -= amount;
    return Activator.CreateInstance(item.GetType(), new object[] { amount }) as T;
}

但有一个问题仍然存在:

List<Item> items = new List<Item>();
items.Add(new IronOre(200));

var myOtherPile = Item.Split(items[0], 70); // myOtherPile is of type Item

IronOre myOtherPile = Item.Split(items[0], 70); // error, cast needed
另一答案

你可以让Item类为它所代表的具体Item类型取一个类型参数,有点像自我类型。这也将有助于Merge方法,你可以有一个工厂Create方法,使创建更容易:

public abstract class Item<T> where T : Item<T>, new() {
  public double Quantity { get; private set; }
  public static T Create(double quantity) {
    return new T { Quantity = quantity };
  }
  public void Merge(T item) {
    Quantity += item.Quantity;
  }
  public T Split(double amount) {
    Quantity -= amount;
    return Create(amount);
  }
}

子类必须将自己作为类型参数提供,并且还必须具有无参数构造函数(隐式构造函数很好):

public class IronOre : Item<IronOre> { }

另请注意,此自我类型参数仅限于惯例,您可以确保子类的行为。

根据您的示例使用示例:

var myIronPile = IronOre.Create(200);
...
var myOtherIronPile = myIronPile.Split(70);

有关这种模式及其陷阱here的更多信息。

以上是关于创建一个基类方法,根据调用该方法的子类实例化一个新的子类对象的主要内容,如果未能解决你的问题,请参考以下文章

Java 继承02

从 Typescript 中的基类创建子类的新实例 [重复]

Typescript - 从基类中的静态方法实例化子类,使用 args 并引用其他静态方法

工厂方法(创建型)

工厂模式

java中,当实例化子类时会递归调用父类中的构造方法。这个说法对么?为啥