C# C:如何使用对父类的引用来访问子类?

C# C:如何使用对父类的引用来访问子类?,c#,C#,所以我有一个抽象类叫做Item,从中继承的是一个叫做Food的类,以及其他类,它们也可以被认为是物品,比如武器和衣服 我试图在一个游戏中给NPC提供一种能力,我正在研究循环浏览他们的库存清单,以便找到食物吃,但他们将无法吃东西,除非他们知道这是食物,而不是剑或帽子 所以我想,如果inventory[I].GetType==Food这样的东西会告诉他们它是否可以食用,但是既然我确定它是Food,那么我如何获得对Food类实例的引用呢?谢谢一次检查和获取键入的引用的有效方法是使用as运算符: for

所以我有一个抽象类叫做Item,从中继承的是一个叫做Food的类,以及其他类,它们也可以被认为是物品,比如武器和衣服

我试图在一个游戏中给NPC提供一种能力,我正在研究循环浏览他们的库存清单,以便找到食物吃,但他们将无法吃东西,除非他们知道这是食物,而不是剑或帽子


所以我想,如果inventory[I].GetType==Food这样的东西会告诉他们它是否可以食用,但是既然我确定它是Food,那么我如何获得对Food类实例的引用呢?谢谢

一次检查和获取键入的引用的有效方法是使用as运算符:

foreach (var item in items)
{
    var food = item as Food;
    if (food != null)
    {
         // do whatever special things can be done with food, e.g. add an "Eat" item to some context menu
    }
}
不过,实现这一点的更好方法可能是利用多态性。 继续使用上下文菜单示例,您的项基类必须有一个虚拟方法,如AddMenuItems,并且该方法在您的每个项上都被调用


然后,Food类可以重写该方法,将Eat项添加到菜单中,而其他类型的项则没有。

一次检查和获取键入的引用的有效方法是使用as运算符:

foreach (var item in items)
{
    var food = item as Food;
    if (food != null)
    {
         // do whatever special things can be done with food, e.g. add an "Eat" item to some context menu
    }
}
不过,实现这一点的更好方法可能是利用多态性。 继续使用上下文菜单示例,您的项基类必须有一个虚拟方法,如AddMenuItems,并且该方法在您的每个项上都被调用

然后,Food类可以重写该方法,将Eat项添加到菜单中,而其他类型的项则没有。

您可以这样检查:

if (inventory[i] is Food)
{
   ...
}
如果对象是食物或食物的子类,则上述结果将返回true

然后您可以强制转换它并调用其方法:

Food food = (Food)inventory[i];
food.Eat();
您可以这样检查:

if (inventory[i] is Food)
{
   ...
}
如果对象是食物或食物的子类,则上述结果将返回true

然后您可以强制转换它并调用其方法:

Food food = (Food)inventory[i];
food.Eat();
让食物变得可口。然后使用items.OfType:

让食物变得可口。然后使用items.OfType:


您需要将对象强制转换为特定类型!有两种可能做到这一点

直接转换,例如:var f=Foodo; 或运算符,并检查对象是否为null 因此,在你的情况下:

var f=食品库存[i]; 或如O.R.Mapper var food=作为食物的物品的响应;
您需要将对象强制转换为特定类型!有两种可能做到这一点

直接转换,例如:var f=Foodo; 或运算符,并检查对象是否为null 因此,在你的情况下:

var f=食品库存[i]; 或如O.R.Mapper var food=作为食物的物品的响应;
如果物品有一个名为IsEdible的属性,那么库存中的所有其他物品都必须实现它,但当然,只有食物返回true

interface IItem
{
      bool IsEdible {get;}
}

class Food : IItem
{
    public bool IsEdible => true;
    public void Eat();
}

class Gun : IItem
{
      public bool IsEdible => false;
}


...
{
   IItem item = ...
   if(item.IsEdible)
   {
       Food food = (Food)item;
       food.Eat();
   }
}

我还想指出,这种类型稍微打破了OOP习惯用法,因为您不必对具体类型进行转换。如果可能的话,应该使用接口函数。

如果项目有一个名为IsEdible的属性,那么库存中的所有其他项目都必须实现它,但当然,只有食品返回true

interface IItem
{
      bool IsEdible {get;}
}

class Food : IItem
{
    public bool IsEdible => true;
    public void Eat();
}

class Gun : IItem
{
      public bool IsEdible => false;
}


...
{
   IItem item = ...
   if(item.IsEdible)
   {
       Food food = (Food)item;
       food.Eat();
   }
}

我还想指出,这种类型稍微打破了OOP习惯用法,因为您不必对具体类型进行转换。如果可能的话,应该使用接口函数。

如果您在7版或更高版本7.1、7.2中使用C作为日期,您可以使用新的模式匹配功能使代码更加优雅:

public class Item { }
public class Food : Item
{
    public void Eat() { }
}
public class Clothes : Item
{
    public void Wear() {}
}

public void DecideWhatToDo(Item item)
{
    switch (item)
    {
        case Food f:
            f.Eat();
            break;
        case Clothes c:
            c.Wear();
            break;
    }
}
您还可以对您的项目进行常规筛选:

public List<T> Filter<T>(this List<Item> items) where T : Item
{
    List<T> result = new List<T>();
    foreach(var item in items)
        switch (item)
        {
            case T i:
               result.Add(i);
               break;
        }

     return result;
}
然后你像:items.Filter那样进行过滤,结果被过滤并转换成所需的类型

另一种避免投射和反射的方法是对项目进行建模,以提供可接受的用法列表:

//can be a class as well if You need it to be
public interface Item 
{
    List<Usage> PossibleUsages {get;}
    void Do(Usage whatToDo)     
}

public enum Usage
{ Eat, Drink, Wear, ThrowAway }

public class Food : Item
{
    public List<Usage> PossibleUsages
    {
        get => new List<Usage> { Usage.Eat }
    }

    public void Do(Usage whatToDo)
    {
        switch(whatToDo)
        {
            case Usage.Eat: Eat(); break;
            default: throw new ArgumentException("Unsupported action", nameof(whatToDo));
        }       
    }

    private void Eat()
    {
        //Your stuff
    }
}

如果在版本7或更高版本7.1、7.2中使用C作为日期,则可以使用新的模式匹配功能使代码更加优雅:

public class Item { }
public class Food : Item
{
    public void Eat() { }
}
public class Clothes : Item
{
    public void Wear() {}
}

public void DecideWhatToDo(Item item)
{
    switch (item)
    {
        case Food f:
            f.Eat();
            break;
        case Clothes c:
            c.Wear();
            break;
    }
}
您还可以对您的项目进行常规筛选:

public List<T> Filter<T>(this List<Item> items) where T : Item
{
    List<T> result = new List<T>();
    foreach(var item in items)
        switch (item)
        {
            case T i:
               result.Add(i);
               break;
        }

     return result;
}
然后你像:items.Filter那样进行过滤,结果被过滤并转换成所需的类型

另一种避免投射和反射的方法是对项目进行建模,以提供可接受的用法列表:

//can be a class as well if You need it to be
public interface Item 
{
    List<Usage> PossibleUsages {get;}
    void Do(Usage whatToDo)     
}

public enum Usage
{ Eat, Drink, Wear, ThrowAway }

public class Food : Item
{
    public List<Usage> PossibleUsages
    {
        get => new List<Usage> { Usage.Eat }
    }

    public void Do(Usage whatToDo)
    {
        switch(whatToDo)
        {
            case Usage.Eat: Eat(); break;
            default: throw new ArgumentException("Unsupported action", nameof(whatToDo));
        }       
    }

    private void Eat()
    {
        //Your stuff
    }
}

你能提供一些示例代码而不是口头描述吗?这使得我们更容易理解你在做什么。你能提供一些示例代码而不是口头描述吗?这使得我们更容易理解你在做什么。有了新的roslyn功能,如果库存[i]是食物,那么你可以实际做,在这一点上,可变食物将被填充。我相信这是C7及以上,iirc。如果OP使用的是Unity3D,那么它可能不可用,但这只是一个猜测。@SwiftingDuster您可以在unity中使用任何您喜欢的版本,就像创建一个普通的c项目一样
如果库存[i]是食物,那么你可以实际使用roslyn的新功能,在这一点上,可变食物将被填充,我相信这是C7及以上,iirc。如果OP使用的是Unity3D,那么它很可能不可用,但这只是一个猜测。@SwiftingDuster您可以在unity中使用任何您喜欢的版本,就像创建一个普通的c项目一样。除非有非食物项目也可以食用,否则确实有可能!,拥有一个额外的IEatable接口而不是直接使用of type可能有点过于复杂了。@O.R.Mapper:也许Food也应该是一个抽象类,Egg或Bread应该是继承它的具体类。但是所有的物品都是可下载的,这对OP寻找食物很重要。如果所有的物品类型都是硬编码的,那么是的。如果具体项目是从资源文件加载的,并且硬编码的区别仅限于一组非常基本的属性,例如是或不是食物,那么没有任何额外接口的微小类层次结构可能是最好的方法。这在很大程度上取决于OP正确的所有因素,因为它不是问题的中心主题,问题没有告诉我们。为什么可以吃的东西应该自己吃?我想全国人大应该吃苹果,不是吗?无论如何,我同意这是实现这一目标的最佳方式。@HimBromBeere:也许应该是玩家角色吃了它。但是因为这里没有char实例,所以我只说了eatableThing.Eat;。所以这可能更好:char.eateatableting;除非有非食物的东西也可以吃,否则真的有可能!,拥有一个额外的IEatable接口而不是直接使用of type可能有点过于复杂了。@O.R.Mapper:也许Food也应该是一个抽象类,Egg或Bread应该是继承它的具体类。但是所有的物品都是可下载的,这对OP寻找食物很重要。如果所有的物品类型都是硬编码的,那么是的。如果具体项目是从资源文件加载的,并且硬编码的区别仅限于一组非常基本的属性,例如是或不是食物,那么没有任何额外接口的微小类层次结构可能是最好的方法。这在很大程度上取决于OP正确的所有因素,因为它不是问题的中心主题,问题没有告诉我们。为什么可以吃的东西应该自己吃?我想全国人大应该吃苹果,不是吗?无论如何,我同意这是实现这一目标的最佳方式。@HimBromBeere:也许应该是玩家角色吃了它。但是因为这里没有char实例,所以我只说了eatableThing.Eat;。所以这可能更好:char.eateatableting;将IsDiable保存在一个称为IEatable的单独接口中,比直接将其实现到IItem@VimalCK:这在很大程度上取决于OP在做什么。例如,在代码库中乱扔几十个小接口并不一定比把所有东西都放在一个死板的类中要好。如果OP的游戏将一些只有一些基本属性的物品与具体物品区分开来,例如面包、大米、肉等。无论如何,这些物品可能不是硬编码的,而是从设置或资源文件动态加载的,因此,在通用IItem接口上具有相关的区别属性(如IsEdiable权限)可以说是最好的方法。将IsEdiable保留在称为IEatable的单独接口中,要比直接实现它更好IItem@VimalCK:这在很大程度上取决于OP在做什么。例如,在代码库中乱扔几十个小接口并不一定比把所有东西都放在一个死板的类中要好。如果OP的游戏将一些只有一些基本属性的物品与具体物品区分开来,例如面包、大米、肉等。无论如何,这些物品可能不是硬编码的,而是从设置或资源文件动态加载的,那么,在通用IItem接口上具有相关的区别属性(如IsEdible权限)可以说是最好的方法。