实例与继承
Instance vs. Inheritance
我正在学习 C# 并且难以理解继承与实例相比有哪些用例?
具体来说,我正在制作一个控制台角色扮演游戏,我目前的理解是我应该制作一个 superclass/baseclass npc
,我从中继承了 3 个子类 mage
, paladin
和 thief
, 其中我每个实例 High level mage
, low level mage
.
有道理还是我的逻辑有问题?如果交换了 inheritance/instance,这也会反过来吗?
将实例与继承进行比较类似于比较苹果和橙子。它们或多或少是无关的。继承用于定义 class 定义的继承权,而实例是当您创建其中一个 class 的实际实例时。
在考虑继承是否是正确的选择时,你需要问自己的一个(但到目前为止不是唯一的)问题是 subclass 和 superclass 共享一个 是关系?如果答案是肯定的,您可能想要使用继承。如果答案是可能或否,那么您几乎可以肯定不会。
举几个例子。
q: class Mercedes
是否与 class Car
.[=41 共享 is a 关系=]
答:也许吧!您更有可能只是有一个 class Car
,它有一个 属性,它是 Make
(和 Model
等)。
q: MyWebPage
是否与 class BaseWebPage
共享 is a 关系
答:可能,是的。这是使用继承的常见方式。 MyWebPage
覆盖基础 class 的某些功能以控制渲染(作为一个示例)
回到你的例子。 class Mage
是否与 Npc
共享 是 关系......答案是另一个“可能”。为了使使用继承有意义,您的 Mage
必须覆盖 Npc
的一些基本功能 - 您可能有类似于上面 Car
示例的内容。
全部 classes 继承,默认从Object
为了使用非抽象class(或任何继承的),您需要创建一个实例。
实例保存字段并定义函数,这些函数可以作为 wrappers/accessors 存储在该实例中的其他实例(称为组合)。
对于给定的例子,假设 NPC 有等级。所有 subclasses 然后从它扩展,因此从它继承属性。法师(和其他类型)有自己的属性
class NPC {
int Level { get; set; }
public NPC(int level) {
Level = level;
}
}
class Mage : NPC {
String PrimaryMagic {get; set; }
public Mage(int level, string magic) : base(level) {
PrimaryMagic = magic;
}
}
var lowMage = new Mage(1, "fire");
var highMage = new Mage(50, "ultima");
但你也可以制作一个没有特定属性的通用水平角色
var nobody = new NPC(0);
你也可以取消法师 class 并给所有 NPC 一些 Spells
列表属性,例如默认为 empty/null.
var magicStudent = new NPC(1); // starts with no magic
// .. Game progresses
if (magicStudent.Level >= 5) {
magicStudent.Spells.Add("cure"); // Based on some condition, update a list of spells.
}
i do not understand use cases of inheritance. Thats what bugged me
您在另一个答案上发表了此评论;该答案讨论了何时确定继承候选人,而 OneCricketeer 的答案讨论了一个示例继承层次结构,但您可能仍然想知道“有什么意义?”
从本质上讲,关键是当您不关心事物是什么的细节时,它允许您以通用的方式对待事物。你不需要知道你手里的东西是刀、抹刀、信用卡等,就可以用它在三明治上涂蛋黄酱;我提到的所有这些东西都可以合理地用来舀起蛋黄酱并将其摊开。它们可以根据一些基本特性以通用方式处理,这些特性使它们成为很好的吊具(直边、相当刚硬的主体、防水和防油)
假设您有自己的角色类型,并且它们都来自 NPC。假设每一种玩家都有 Health
,当它达到 0 时他们就死了。因为所有角色都有生命值,所以将它放在 NPC 上可能是有意义的。也许不同种类的角色有不同的初始生命值:
class Npc{
public int Health{get;set;}
}
class Medic:Npc{
Medic(){
Health = 30;
}
}
class Soldier:Npc{
Soldier(){
Health = 80;
}
}
假设您的播放器组织了一个clan/posse/team,那么您的程序有例如:
var team = new Npc[10];
你的队伍最多可以有10种角色,可以任意组合。继承这个东西就是Soldier是-Npc,Medic是-Npc,所以现在你做了一个Npc数组,里面可以塞士兵和medc:
team[0] = new Soldier();
team[1] = new Medic();
假设有人向团队投掷炸弹,对每个人的健康造成 55 点伤害。它会直接杀死一个新的医生。我们不需要知道他们是什么样的玩家;所有 Npc 都有生命值,在这种通用的“带走一些生命值”场景中,医务人员和士兵都可以被视为 Npc:
void DropBombOn(Npc[] team){
foreach(var npc in team) {
npc.Health -= 25;
if(npc.Health <= 0)
Console.WriteLine("Character is dead");
}
}
但是哪个角色死了?让我们修改 classes:
class Npc{
public int Health{get;set;}
public string Name{get;set;}
}
设置它们:
team[0] = new Soldier("john");
team[1] = new Medic("fred");
class Medic:Npc{
Medic(string name){
Health = 30;
Name = name;
}
}
class Soldier:Npc{
Soldier(string name){
Health = 80;
Name = name;
}
}
void DropBombOn(Npc[] team){
foreach(var npc in team) {
npc.Health -= 55;
if(npc.Health <= 0)
Console.WriteLine("Character called " + npc.Name + " is dead");
}
}
你可以添加其他类型的角色,他们仍然是Npc,所以他们可以像其他人一样被轰炸..
继承允许我们将非常具体的类型视为更一般的东西,如果有可以在一般意义上发生的操作,在所有这些类型的东西具有的一般属性上。
也许所有玩家的 class 中都有代表他们的位图图像。你可以通过访问每个团队并说“给我你的像素”来在屏幕上绘制团队;你不需要访问每个人并说“如果这个 npc 是军医,就画一个军医。如果这个 npc 是士兵,就画一个士兵......” - 你只需绘制他们给你的任何像素,每个不同的玩家都会给出不同的像素像素。那,本质上就是我们对名字所做的——所有角色都有一个名字,我们不关心我们得到的是哪个名字;我们只是询问并打印出了我们得到的结果
另一个有用的东西是 classes 可以有覆盖(替换)基础 classes 的方法。这里我们创建一个描述角色的方法。我们把它放在 Npc 上,这意味着它可以用于从 Npc 继承的任何东西。我们标记为abstract
,意思是“任何继承自Npc的东西必须提供一个returns字符串的方法,并称为DescribeYourself”:
public class Npc{
public int Health{get;set;}
public string Name{get;set;}
public abstract void DescribeYourself();
}
public class Medic:Npc{
public Medic(string name){
Health = 30;
Name = name;
}
public override string DescribeYourself(){
return "I'm a medic called " + Name;
}
}
public class Soldier:Npc{
public string ServiceNumber {get; set;}
public Soldier(string name,string serviceNumber){
Health = 80;
Name = name;
ServiceNumber = serviceNumber;
}
public override string DescribeYourself(){
return "I'm a soldier with service number " + ServiceNumber + " and I ain't telling you anything else";
}
}
void DropBombOn(Npc[] team){
foreach(var npc in team) {
npc.Health -= 55;
if(npc.Health <= 0)
Console.WriteLine(npc.DescribeYourself() + " and I'm dead");
}
}
这次我们没有拉出 name/print 一条常见的消息 - 我们要求 npc 描述自己。如果是士兵,代码的士兵版本会运行,我们会看到 我是一名士兵,服务编号为 12345,我不会告诉你任何其他事情,我已经死了安慰。如果是医生,他们会更愿意提供个人信息,说 我是一名医生,名叫约翰,我已经死了 :)
我们在制作士兵时还必须提供服务编号。这概述了不同种类的classes在设置和使用过程中可以有不同的数据requirements/more或更少的数据,但我们仍然可以以通用的方式对待它们:
team[0] = new Soldier("john", "12345");
team[1] = new Medic("fred");
这种“以普通方式对待”的术语是多态性,它可能是 C# 最强大的功能之一。在实际意义上,例如,Microsoft可以提供一个 Stream class,它基本上将字节写入某个地方,然后大量继承的 classes 可以将字节写入不同的地方。我们现在有可以将字节写入.. 控制台、文件、一些保管箱存储、内存、字符串的流......它们都以相同的方式使用,调用 write 的人不会关心代码必须跳过多少圈才能写入控制台、文件和 FTP 服务器。他们只是调用 Write 并期望字节最终到达承诺的位置。
您可以编写自己的流,并将它们交给 Microsoft 的 classes - Microsoft 的 classes 对您的 class 一无所知 es,但您仍然可以要求位图将其自身保存到您的流中,也许您的流 class 接受它并将字节添加到电子邮件中并发送它..
突然之间,您刚刚让位图将自己写入电子邮件成为可能,并且位图可以完全不知道电子邮件是什么,但它仍然可以写入。
你可以完全不知道什么是塞尺,但你可以用它来涂抹蛋黄酱..
我正在学习 C# 并且难以理解继承与实例相比有哪些用例?
具体来说,我正在制作一个控制台角色扮演游戏,我目前的理解是我应该制作一个 superclass/baseclass npc
,我从中继承了 3 个子类 mage
, paladin
和 thief
, 其中我每个实例 High level mage
, low level mage
.
有道理还是我的逻辑有问题?如果交换了 inheritance/instance,这也会反过来吗?
将实例与继承进行比较类似于比较苹果和橙子。它们或多或少是无关的。继承用于定义 class 定义的继承权,而实例是当您创建其中一个 class 的实际实例时。
在考虑继承是否是正确的选择时,你需要问自己的一个(但到目前为止不是唯一的)问题是 subclass 和 superclass 共享一个 是关系?如果答案是肯定的,您可能想要使用继承。如果答案是可能或否,那么您几乎可以肯定不会。
举几个例子。
q: class Mercedes
是否与 class Car
.[=41 共享 is a 关系=]
答:也许吧!您更有可能只是有一个 class Car
,它有一个 属性,它是 Make
(和 Model
等)。
q: MyWebPage
是否与 class BaseWebPage
共享 is a 关系
答:可能,是的。这是使用继承的常见方式。 MyWebPage
覆盖基础 class 的某些功能以控制渲染(作为一个示例)
回到你的例子。 class Mage
是否与 Npc
共享 是 关系......答案是另一个“可能”。为了使使用继承有意义,您的 Mage
必须覆盖 Npc
的一些基本功能 - 您可能有类似于上面 Car
示例的内容。
全部 classes 继承,默认从Object
为了使用非抽象class(或任何继承的),您需要创建一个实例。
实例保存字段并定义函数,这些函数可以作为 wrappers/accessors 存储在该实例中的其他实例(称为组合)。
对于给定的例子,假设 NPC 有等级。所有 subclasses 然后从它扩展,因此从它继承属性。法师(和其他类型)有自己的属性
class NPC {
int Level { get; set; }
public NPC(int level) {
Level = level;
}
}
class Mage : NPC {
String PrimaryMagic {get; set; }
public Mage(int level, string magic) : base(level) {
PrimaryMagic = magic;
}
}
var lowMage = new Mage(1, "fire");
var highMage = new Mage(50, "ultima");
但你也可以制作一个没有特定属性的通用水平角色
var nobody = new NPC(0);
你也可以取消法师 class 并给所有 NPC 一些 Spells
列表属性,例如默认为 empty/null.
var magicStudent = new NPC(1); // starts with no magic
// .. Game progresses
if (magicStudent.Level >= 5) {
magicStudent.Spells.Add("cure"); // Based on some condition, update a list of spells.
}
i do not understand use cases of inheritance. Thats what bugged me
您在另一个答案上发表了此评论;该答案讨论了何时确定继承候选人,而 OneCricketeer 的答案讨论了一个示例继承层次结构,但您可能仍然想知道“有什么意义?”
从本质上讲,关键是当您不关心事物是什么的细节时,它允许您以通用的方式对待事物。你不需要知道你手里的东西是刀、抹刀、信用卡等,就可以用它在三明治上涂蛋黄酱;我提到的所有这些东西都可以合理地用来舀起蛋黄酱并将其摊开。它们可以根据一些基本特性以通用方式处理,这些特性使它们成为很好的吊具(直边、相当刚硬的主体、防水和防油)
假设您有自己的角色类型,并且它们都来自 NPC。假设每一种玩家都有 Health
,当它达到 0 时他们就死了。因为所有角色都有生命值,所以将它放在 NPC 上可能是有意义的。也许不同种类的角色有不同的初始生命值:
class Npc{
public int Health{get;set;}
}
class Medic:Npc{
Medic(){
Health = 30;
}
}
class Soldier:Npc{
Soldier(){
Health = 80;
}
}
假设您的播放器组织了一个clan/posse/team,那么您的程序有例如:
var team = new Npc[10];
你的队伍最多可以有10种角色,可以任意组合。继承这个东西就是Soldier是-Npc,Medic是-Npc,所以现在你做了一个Npc数组,里面可以塞士兵和medc:
team[0] = new Soldier();
team[1] = new Medic();
假设有人向团队投掷炸弹,对每个人的健康造成 55 点伤害。它会直接杀死一个新的医生。我们不需要知道他们是什么样的玩家;所有 Npc 都有生命值,在这种通用的“带走一些生命值”场景中,医务人员和士兵都可以被视为 Npc:
void DropBombOn(Npc[] team){
foreach(var npc in team) {
npc.Health -= 25;
if(npc.Health <= 0)
Console.WriteLine("Character is dead");
}
}
但是哪个角色死了?让我们修改 classes:
class Npc{
public int Health{get;set;}
public string Name{get;set;}
}
设置它们:
team[0] = new Soldier("john");
team[1] = new Medic("fred");
class Medic:Npc{
Medic(string name){
Health = 30;
Name = name;
}
}
class Soldier:Npc{
Soldier(string name){
Health = 80;
Name = name;
}
}
void DropBombOn(Npc[] team){
foreach(var npc in team) {
npc.Health -= 55;
if(npc.Health <= 0)
Console.WriteLine("Character called " + npc.Name + " is dead");
}
}
你可以添加其他类型的角色,他们仍然是Npc,所以他们可以像其他人一样被轰炸..
继承允许我们将非常具体的类型视为更一般的东西,如果有可以在一般意义上发生的操作,在所有这些类型的东西具有的一般属性上。
也许所有玩家的 class 中都有代表他们的位图图像。你可以通过访问每个团队并说“给我你的像素”来在屏幕上绘制团队;你不需要访问每个人并说“如果这个 npc 是军医,就画一个军医。如果这个 npc 是士兵,就画一个士兵......” - 你只需绘制他们给你的任何像素,每个不同的玩家都会给出不同的像素像素。那,本质上就是我们对名字所做的——所有角色都有一个名字,我们不关心我们得到的是哪个名字;我们只是询问并打印出了我们得到的结果
另一个有用的东西是 classes 可以有覆盖(替换)基础 classes 的方法。这里我们创建一个描述角色的方法。我们把它放在 Npc 上,这意味着它可以用于从 Npc 继承的任何东西。我们标记为abstract
,意思是“任何继承自Npc的东西必须提供一个returns字符串的方法,并称为DescribeYourself”:
public class Npc{
public int Health{get;set;}
public string Name{get;set;}
public abstract void DescribeYourself();
}
public class Medic:Npc{
public Medic(string name){
Health = 30;
Name = name;
}
public override string DescribeYourself(){
return "I'm a medic called " + Name;
}
}
public class Soldier:Npc{
public string ServiceNumber {get; set;}
public Soldier(string name,string serviceNumber){
Health = 80;
Name = name;
ServiceNumber = serviceNumber;
}
public override string DescribeYourself(){
return "I'm a soldier with service number " + ServiceNumber + " and I ain't telling you anything else";
}
}
void DropBombOn(Npc[] team){
foreach(var npc in team) {
npc.Health -= 55;
if(npc.Health <= 0)
Console.WriteLine(npc.DescribeYourself() + " and I'm dead");
}
}
这次我们没有拉出 name/print 一条常见的消息 - 我们要求 npc 描述自己。如果是士兵,代码的士兵版本会运行,我们会看到 我是一名士兵,服务编号为 12345,我不会告诉你任何其他事情,我已经死了安慰。如果是医生,他们会更愿意提供个人信息,说 我是一名医生,名叫约翰,我已经死了 :)
我们在制作士兵时还必须提供服务编号。这概述了不同种类的classes在设置和使用过程中可以有不同的数据requirements/more或更少的数据,但我们仍然可以以通用的方式对待它们:
team[0] = new Soldier("john", "12345");
team[1] = new Medic("fred");
这种“以普通方式对待”的术语是多态性,它可能是 C# 最强大的功能之一。在实际意义上,例如,Microsoft可以提供一个 Stream class,它基本上将字节写入某个地方,然后大量继承的 classes 可以将字节写入不同的地方。我们现在有可以将字节写入.. 控制台、文件、一些保管箱存储、内存、字符串的流......它们都以相同的方式使用,调用 write 的人不会关心代码必须跳过多少圈才能写入控制台、文件和 FTP 服务器。他们只是调用 Write 并期望字节最终到达承诺的位置。
您可以编写自己的流,并将它们交给 Microsoft 的 classes - Microsoft 的 classes 对您的 class 一无所知 es,但您仍然可以要求位图将其自身保存到您的流中,也许您的流 class 接受它并将字节添加到电子邮件中并发送它..
突然之间,您刚刚让位图将自己写入电子邮件成为可能,并且位图可以完全不知道电子邮件是什么,但它仍然可以写入。
你可以完全不知道什么是塞尺,但你可以用它来涂抹蛋黄酱..