让我们以以下类为例:
基类:
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 派生类时,更不用说在基类中添加字段意味着更改复制方法在所有派生类中。
有更好的方法吗?
答案 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