如何正确地从派生类复制字段?

时间:2019-01-23 16:19:39

标签: c# inheritance

让我们以以下类为例:

基类:

public class Spell
{
    public int castRange;

    public Spell Copy()
    {
        Spell spell = new Spell();
        spell.castRange = this.castRange;
        return spell;
    }
}

派生类:

public class ManaSpell : Spell
{
    public int manaCost;

    public new ManaSpell Copy()
    {
        ManaSpell spell = new ManaSpell();
        spell.castRange = this.castRange;
        spell.manaCost = this.manaCost;
        return spell;
    }
}

我不能对 Copy()方法使用 virtual override ,因为它们的返回类型不同,所以我使用 new 关键字。问题始于下一课:

public class Unit
{
    public Spell spell;

    public Unit(Spell spell)
    {
        // This will call the Copy method in the base class, even if the 
        // parameter is actually a ManaSpell

        this.spell = spell.Copy();

        // So instead I have to do a check first:

        if (spell is ManaSpell)
        {
            ManaSpell manaSpell = spell as ManaSpell;
            this.spell = manaSpell.Copy();
        }
    }
}

所有方法都有效,但感觉设计效率很低,尤其是当我添加越来越多的 Spell 派生类时,更不用说在基类中添加字段意味着更改复制方法在所有派生类中。

有更好的方法吗?

2 个答案:

答案 0 :(得分:3)

除非您有充分的理由隐藏(这是new的工作),您对基类的实现Copy,您不应该{{ 1}}。

看来您根本不需要。不管其实际类型如何,您实际上都想复制new。因此,让实例解析对Spell的调用,这是通过通常的重写来完成的:

Copy

现在,您可以在public class Spell { public int castRange; public virtual Spell Copy() { Spell spell = new Spell(); spell.castRange = this.castRange; return spell; } } public class ManaSpell : Spell { public int manaCost; public override Spell Copy() { ManaSpell spell = new ManaSpell(); spell.castRange = this.castRange; spell.manaCost = this.manaCost; return spell; } } 的任何实例上调用Copy,而无需区分实际类型:

Spell

如果您有基类实例,它将解析为this.Spell = spell.Copy() 的新实例,如果您有派生类型的实例将解析为Spell

答案 1 :(得分:1)

创建克隆的一种简单方法是使用从MemberwiseClone继承的私有方法System.Object。它具有自动考虑派生类的字段的优点。即您无需派生复制方法即可使其工作。

public class Spell
{
    public int castRange;

    public Spell ShallowClone()
    {
        return (Spell)MemberwiseClone();
    }

    public override string ToString() => $"castRange = {castRange}";
}

public class ManaSpell : Spell
{
    public int manaCost;

    public override string ToString() => $"castRange = {castRange}, manaCost = {manaCost}";
}

此测试...

Spell spell = new ManaSpell { castRange = 5, manaCost = 10 };
var copy = spell.ShallowClone();
Console.WriteLine(copy);
Console.ReadKey();

...显示

  

castRange = 5,manaCost = 10

如果您需要键入ManaSpell的结果,则无法避免强制转换。


避免强制转换的可能解决方案是使用通用静态方法。 C#编译器可以从静态(编译时)参数类型推断返回类型。

public class Spell
{
    public int castRange;

    public Spell ShallowClone()
    {
        return (Spell)MemberwiseClone();
    }

    public override string ToString() => $"castRange = {castRange}";

    public static T ShallowClone<T>(T original)
        where T : Spell
    {
        return (T)original.ShallowClone();
    }
}

这个...

ManaSpell manaSpell = new ManaSpell { castRange = 6, manaCost = 18 };
ManaSpell manaSpellCopy = Spell.ShallowClone(manaSpell);
Console.WriteLine(manaSpellCopy);

...打印

  

castRange = 6,manaCost = 18