C# 如何正确地从派生类复制字段?
让我们以以下课程为例: 基类:C# 如何正确地从派生类复制字段?,c#,inheritance,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
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;
}
}
派生类:
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派生的类,更不用说在基类中添加一个字段意味着在所有派生类中也更改copy方法
有更好的方法吗?除非你有很好的理由隐藏(这就是
new
所做的)你的复制
-基类的实现,否则你不应该new
它
看来你根本不需要这个。你实际上想复制一个拼写
,不管它的实际类型是什么。因此,让实例解析对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
,而无需区分实际类型:
this.Spell = spell.Copy()
如果您有基类实例,这将解析为Spell
的新实例;如果您有派生类型的实例,这将解析为ManaSpell
。除非您有很好的理由隐藏(这就是new
所做的)您的复制
-基类的实现,你不应该new
it
看来你根本不需要这个。你实际上想复制一个拼写
,不管它的实际类型是什么。因此,让实例解析对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
,而无需区分实际类型:
this.Spell = spell.Copy()
如果您有基类实例,这将解析为Spell
的新实例;如果您有派生类型的实例,这将解析为ManaSpell
。创建克隆的简单方法是使用从System.Object
继承的私有方法MemberwiseClone
。它的优点是自动考虑派生类中的字段。也就是说,您不必派生复制方法来使其工作
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();
}
}
。。。印刷品
castRange=6,manaCost=18
创建克隆的一种简单方法是使用从System.Object
继承的私有方法MemberwiseClone
。它的优点是自动考虑派生类中的字段。也就是说,您不必派生复制方法来使其工作
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();
}
}
。。。印刷品
castRange=6,manaCost=18
您应该再次明确阅读有关新建
、虚拟
和覆盖
的内容<代码>新建
始终是一个设计缺陷。无论如何,我不明白你的真正目的。为什么要在顶部复制manaple
的实例,并通过Spell
类型的引用来引用它。后者不知道其派生类型的任何内容,因此,即使你可以做你想做的事情,this.Spell
始终将Spell
作为编译时类型提供给你。只需省略类型检查和new
,让实例解析Copy
来调用自己。有点离题,但是您应该考虑使用公共字段。它们提供了更好的封装,而一些库/框架(WPF绑定,Newtonsoft.Json
…)默认不支持字段。使用公共字段通常被一些人认为是一种不好的做法,因此:)您应该明确地再次阅读有关new
、virtual
和override
<代码>新建
始终是一个设计缺陷。无论如何,我不明白你的真正目的。为什么要在顶部复制manaple
的实例,并通过Spell
类型的引用来引用它。后者不知道其派生类型的任何内容,因此,即使你可以做你想做的事情,this.Spell
始终将Spell
作为编译时类型提供给你。只需省略类型检查和new
,让实例解析Copy
来调用自己。有点离题,但是您应该考虑使用公共字段。它们提供了更好的封装,而一些库/框架(WPF绑定,Newtonsoft.Json
…)默认不支持字段。一些人经常认为使用公共字段是一种不好的做法,所以:)