简体   繁体   English

在使用继承和集合时是否有替代?

[英]Is there an alternative to is/as when working with inheritance and collections?

I have the following class hierarchy of type Item: 我有类型Item的以下类层次结构:

Item 项目

  • Weapon : Item 武器:物品

  • Armor : Item 护甲:物品

I use a generic linked list of type Item. 我使用Item类型的通用链表。 I have inserted different kinds of items: Armor, Weapon, etc. I stuck to two item types in this example. 我插入了不同种类的物品:护甲,武器等。在这个例子中我坚持了两种物品类型。

I have the following code: 我有以下代码:

// Create the map. The underneath data structure is: 
// Array2D<LinkedList<Item>> m_map;
// A generic 2d array contains a linked list of type cell at each cell.
// This makes it easy to drop and pick up items at a given player position.
Map map = new Map(10, 10);

// First, I store two items at position (0,0) on my map.
map.AddItem(0, 0, new Weapon(6));
map.AddItem(0, 0, new Armor(3));

// Now, this only retrieves weapons. 
var qItems = from w in map.GetItems(0, 0)
        where w is Weapon
        select w;

// Weapons are outputted
foreach (Weapon weapon in qItems)
    Console.WriteLine(weapon.m_damage);

This works fine, but when I'm interested in gathering all items (which will be often), I have to do this: 这很好,但是当我有兴趣收集所有项目(通常是这样)时,我必须这样做:

// All items are outputted
foreach (Item item in map.GetItems(0, 0))
{
    if (item is Weapon) 
        Console.WriteLine("Damage: {0}", (item as Weapon).m_damage); 
    else if (item is Armor)
        Console.WriteLine("AC: {0}", (item as Armor).m_ac);
} 

Is there a more efficient way of retrieving different item types than using is/as? 有没有比使用is / as更有效的方法来检索不同的项目类型? I think this can lead to bugs because what if a developer adds a new item type but forgets to make a condition for it in the foreach block? 我认为这会导致错误,因为如果开发人员添加新的项类型但是忘记在foreach块中为它创建条件会怎样? As you can guess, the list can get extensive of different types (10 or so in this project). 您可以猜到,列表可以包含大量不同类型(此项目中有10个左右)。

Edit: This is not a duplicate question. 编辑:这不是一个重复的问题。 I'm looking at all subclasses, while the possible duplicate message is focused on retrieving one subclass. 我正在查看所有子类,而可能的重复消息则集中在检索一个子类。

In oop we rely on virtual dispatch to do our bidding so I usually do not use is keyword to determine the type of the object. 在oop中我们依靠虚拟调度来进行我们的出价,所以我通常不使用is关键字来确定对象的类型。 I believe if you have an inheritance similar to the following then you can do 我相信如果你有类似于以下的继承,那么你可以做到

// items are outputted
foreach (var item in qItems)
    Console.WriteLine(item.ShowStats());
}

Item inheritance: 物品继承:

public abstract class Item {

    public abstract string ShowStats();
}

Then armor will be 然后盔甲将

public class Armor : Item {
    private readonly double _armor;

    public Armor(double armor) {
        _armor = armor;
    }

    public override string ShowStats() {
        return $"Armor: {_armor}";
    }
}

And weapon will be 武器将是

public class Weapon : Item {
    private readonly double _damage;

    public Weapon(double damage) {
        _damage = damage;
    }

    public override string ShowStats() {
        return $"Damage: {_damage}";
    }
}

The reason you can't really do this is because of the following reason: 你不能真正做到这一点的原因是由于以下原因:

While you could use an example like this to find all the classes that inherit Item , you can't really make this automated because each class needs to access a different variable. 虽然你可以使用下面的例子这样找出所有继承类Item ,你不能真正使这个自动化的,因为每个类都需要访问不同的变量。

Take for example, you code that you posted: 例如,您发布的代码:

// All items are outputted
foreach (Item item in map.GetItems(0, 0))
{
    if (item is Weapon) 
        Console.WriteLine("Damage: {0}", (item as Weapon).m_damage); 
    else if (item is Armor)
        Console.WriteLine("AC: {0}", (item as Armor).m_ac);
} 

In this code, you access the item as Weapon field m_damage . 在此代码中,您可以将item as Weapon字段m_damage This, however, changes with the next line, as instead of finding field m_damage , you look for m_ac inside of item as Armor . 但是,这会随着下一行而改变,因为不是查找字段m_damagem_acitem as Armor内部寻找m_ac item as Armor It simply can't be automated beyond actually defining which classes item can be a Type of. 除了实际定义哪个类item可以是Type之外,它无法实现自动化。

One suggestion I can make is that you could create properties within the Item class which contain m_damage and m_ac , then just override them in your inherited classes. 我可以做的一个建议是你可以在Item类中创建包含m_damagem_ac ,然后在你继承的类中override它们。 This will allow you to access these fields without needing to check their Type . 这将允许您访问这些字段,而无需检查其Type

public class Item
{
    public virtual int m_damage { get; set; }
}

public class Weapon : Item
{
    public override int m_damage => 4;
}

Alternatively, a better solution, as @ragyaiddo pointed out is to create a method within the Item class called PrintInfo() , which is then override into the inherited classes, which will simply print out information about that Item . 或者,更好的解决方案,正如@ragyaiddo指出的那样,在Item类中创建一个名为PrintInfo() ,然后将其override到继承的类中,这将简单地打印出有关该Item信息。

public abstract class Item
{
    public abstract void PrintStats();
}

public class Weapon : Item
{
    public int Damage { get; set; }

    public override void PrintStats()
    {
        Console.WriteLine("Damage: {0}", Damage);
    }
}

The visitor pattern is often used in this kind of situation. 访客模式通常用于这种情况。 But you will always have something that resolves the subclasses 但是你总会得到解决子类的东西

Your alternative is to build Items in such a way as they know what to do 您可以选择以他们知道该做什么的方式构建项目

So you just do Console.WriteLine(item.Stats) 所以你只需要做Console.WriteLine(item.Stats)

Stats could be a new class ItemStats that is flexible and could store many keyvalue pairs of stats 统计数据可以是一个新的类ItemStats ,它是灵活的,可以存储许多关键值对的统计数据

As I pointed out in the comment, you can define an abstract method in Item class and have the inheriting classes implement their specific implementation. 正如我在注释中指出的那样,您可以在Item类中定义一个抽象方法,并让继承类实现它们的特定实现。 I created a sample implementation here . 我在这里创建了一个示例实现。 This is a similar approach to @Emrah Süngü's solution. 这与@EmrahSüngü的解决方案类似。

You can take this further with the implementation of Template Method Pattern or Strategy Pattern , depending on your requirements. 您可以根据您的要求实现模板方法模式策略模式 These patterns are applicable when you have more than one class that need to follow the same common workflow, but each of these classes has different internal implementations of the said workflow. 当您有多个需要遵循相同的常用工作流的类时,这些模式适用,但这些类中的每个类都具有所述工作流的不同内部实现。

As answered here using linq... 正如在这里使用linq ...

foreach (Weapon item in map.GetItems(0, 0).OfType<Weapon>())
    Console.WriteLine("Damage: {0}", item.m_damage); 

foreach (Armor item in map.GetItems(0, 0).OfType<Armor>())
    Console.WriteLine("AC: {0}", item.m_ac);

Alternatively... 另外...

public class Item { }
public class Weapon : Item
{
    public int Damage { get; set; }

    public override string ToString()
    {
        return $"Damage: {Damage}";
    }
}

public class Armor : Item
{
    public int AC { get; set; }

    public override string ToString()
    {
        return $"AC: {AC}";
    }
}

foreach (Item item in map.GetItems(0, 0))
    Console.WriteLine(item.ToString());

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM