8

我正在尝试创建一个简单的物品“库存”,就像在任何 RPG 中一样。我制作了非常基本的类,它们具有属性。

无论如何,我有一个基类item并继承自weapon. item具有也在 中使用的属性(名称、值、重量、“重要项目”)weapon,但weapon具有额外的属性(攻击、防御、速度、惯用手)。

我有以下代码(对不起,如果可读性很糟糕):

static void Main(string[] args)
{   
     List<item> inventory = new List<item>();
     inventory.Add(new weapon("Souleater", 4000, 25.50f, false, 75, 30, 1.25f, 2));
                           //item--------------------------->  weapon--------->

     Console.Write("Name: {0}\nValue: {1}\nWeight: {2}\nDiscardable: {3}\nAttack: {4}\nDefense: {5}\nSpeed: {6}\nHandedness: {7}",
            inventory[0].Name, inventory[0].BValue, inventory[0].Weight, inventory[0].Discard, 
            inventory[0].Atk, inventory[0].Def, inventory[0].Speed, inventory[0].Hands);

     Console.ReadLine();
}

基本上,我想做的是weapon向库存中添加一个新的,但库存是一种List<item>类型。我一时兴起希望,由于它的继承,它会被接受。weapon是的,但是无法访问特定于的属性:

(“shopSystem.item”不包含“Atk”的定义,并且找不到接受“shopSystem.item”类型的第一个参数的扩展方法“Atk”)

那么,有什么方法可以实现我在这里的意图吗?有一个可以存储item对象的“库存”,以及weapon,armouraccessory对象,这些对象继承自item? 还值得一提的是,如果我声明以下内容,我可以访问所有所需的属性:

weapon Foo = new weapon("Sword", 200, 20.00f, false, 30, 20, 1.10f, 1);

非常感谢您的阅读。

这是itemweapon类,如果有人感兴趣的话:

class item
{
    #region Region: Item Attributes
    protected string name = "";
    protected int baseValue = 0;
    protected float weight = 0.00f;
    protected bool noDiscard = false;
    #endregion

    public item(string n, int v, float w, bool nd){
        name = n; baseValue = v; weight = w; noDiscard = nd;}

    public string Name{
        get{return name;}
        set{if(value != ""){
            name = value;}
        }//end set
    }

    public int BValue{
        get{return baseValue;}
    }

    public float Weight{
        get{return weight;}
    }

    public bool Discard{
        get{return noDiscard;}
    }
}

class weapon : item
{
    #region Region: Weapon Attributes
    private int atk = 0;
    private int def = 0;
    private float speed = 0.00f;
    private byte hands = 0;
    #endregion

    public weapon(string n, int v, float w, bool nd, int a, int d, float s, byte h) : base(n, v, w, nd){
        atk = a; def =d; speed = s; hands = h;}

    public int Atk{
        get{return atk;}
    }

    public int Def{
        get{return def;}
    }

    public float Speed{
        get{return speed;}
    }

    public byte Hands{
        get{return hands;}
    }

}
4

5 回答 5

7

为了访问特定于继承类的属性,您需要转换为适当的具体类类型。inventory[0]因此,您需要访问 ,而不是访问(weapon)inventory[0]

此外,如果您正在执行基于底层类具有不同实现的常见任务,例如将值记录到控制台(我意识到这是为了测试而完成的,但无论如何都是一个很好的例子),您应该实现一个可覆盖的方法在基类中。

例如,在基类中:

public virtual void LogProperties()
{
Console.Write("Name: {0}\nValue: {1}\nWeight: {2}\nDiscardable: {3}\nAttack",
            this.Name, this.BValue, this.Weight, this.Discard);
}

在武器类中:

public override void LogProperties()
{
Console.Write("Name: {0}\nValue: {1}\nWeight: {2}\nDiscardable: {3}\nAttack: {4}\nDefense: {5}\nSpeed: {6}\nHandedness: {7}",
            this.Name, this.BValue, this.Weight, this.Discard, 
            this.Atk, this.Def, this.Speed, this.Hands);
}

然后,要访问这个:

inventory[0].LogProperties();
于 2012-07-15T22:53:55.250 回答
2

通过使用 aList<item>来存储项目和项目的子类,您正在做正确的事情!

当您从列表中提取项目时,您只需要做一些工作来测试它是weapon. 还是其他类型的item.

要查看i数组中位置的元素是否为 a weapon,请使用运算符,如果项目不是指定的类型(可能是盔甲或其他类型的项目)as,它将返回。null这被称为dynamic演员表。即使该项目是null.

weapon w = inventory[i] as weapon;  // dynamic cast using 'as' operator
if (w != null)
{
     // work with the weapon
}

另一种方法是is在进行强制转换之前使用运算符检查类型static

if (inventory[i] is weapon)
{
     // static cast (won't fail 'cause we know type matches)
     weapon w = (weapon)inventory[i];           

     // work with the weapon
} 
于 2012-07-16T00:14:57.203 回答
1

这就是 Linq 在处理多态性时可以成为您朋友的地方。转换项目的问题是当给定的索引不是正确的类型时。(即inventory[0]不是weapon(weapon)inventory[0]会抛出。)

您可以使用以下方法避免异常:

var myWeapon = inventory[0] as weapon;

但是,您将到处进行#null 检查。使用 Linq,您可以获得很大的灵活性:

var weapons = inventory.OfType<weapon>();

从这里您可以使用WhereFirstOrDefault等来查找您感兴趣的数据或迭代武器。

于 2012-07-16T02:00:15.143 回答
0

您可以实现一个通用接口(例如IItem),通过该接口查询项目的属性,或者实现一个通用基类,可能是一个抽象基类,并将其T用作List. 如果使用后一个选项,请确保在适当的情况下覆盖基类的函数,剩下的工作将由多态性完成。

于 2012-07-15T22:57:09.170 回答
0

您需要将值转换回 aweapon才能访问这些属性,即((weapon)inventory[0]).Atk). 注意括号:强制转换的优先级低于字段访问,所以你不能写(weapon)inventory[0].Atk; 那将被解释为(weapon)(inventory[0].Atk).

您还需要区分不同的类(例如,您可能还有一个armor类)。最简单的做法是在基类上添加一个额外的值item,告诉您对象的实际类型,然后进行相应的转换。

然而,更好的方法是使用继承——让我们item定义一系列可以处理所有通用内容的方法——例如显示有关项目的信息——然后在继承类中覆盖它。也就是说,不是直接访问所有这些数据,而是调用一种以合理方式组合它的方法。对于您的特定示例,您正在将项目转换为字符串,因此覆盖 ToString() 可能是有意义的 - 这意味着您可以这样做:

Console.Write(inventory[0]);

并且根据它是物品还是武器,将调用适当类的 ToString() (通常,您必须自己调用方法,但 ToString() 是特殊的,因为所有对象都有该方法,所以Console.Write 为您完成)。

在您的武器类中,您将像这样实现 ToString 方法:

public string ToString() {
  //Put all of the data in here, like in your example
  return String.Format("Name: {0}\n...", name, ...);
}
于 2012-07-15T22:58:00.747 回答