C# 在处理继承和集合时,是否有Is/as的替代方案?
我有以下Item类型的类层次结构: 项目C# 在处理继承和集合时,是否有Is/as的替代方案?,c#,C#,我有以下Item类型的类层次结构: 项目 武器:物品 盔甲:物品 我使用Item类型的通用链表。我插入了不同种类的物品:盔甲、武器等。在本例中,我坚持使用两种物品类型 我有以下代码: // Create the map. The underneath data structure is: // Array2D<LinkedList<Item>> m_map; // A generic 2d array contains a linked list of type c
- 武器:物品
- 盔甲:物品
// 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);
有没有比使用Is/as更有效的方法检索不同的项目类型?我认为这可能会导致bug,因为如果开发人员添加了一个新的项目类型,但忘记在foreach块中为其设置条件,该怎么办?正如您所猜测的,该列表可以包含多种不同的类型(在本项目中大约有10种)
编辑:这不是重复的问题。我正在查看所有子类,而可能的重复消息集中在检索一个子类上 通常在这种情况下使用。但是,您将始终拥有解析子类的内容
您的选择是以这样一种方式构建项目,即它们知道要做什么
所以您只需执行Console.WriteLine(item.Stats)
Stats可以是一个新类
ItemStats
,它非常灵活,可以存储许多统计数据的键值对您不能真正做到这一点的原因如下:
虽然您可以使用这样的示例来查找继承Item
的所有类,但无法真正实现自动化,因为每个类都需要访问不同的变量
以发布的代码为例:
// 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);
}
在此代码中,您可以访问项目作为武器
字段m_伤害
。但是,这会随着下一行的变化而变化,因为您不会查找字段m_damage
,而是将m_ac
作为盔甲查找在物品内部。除了实际定义哪些类项
可以是类型
之外,它无法实现自动化
我可以提出的一个建议是,您可以在Item
类中创建包含m_-damage
和m_-ac
的属性,然后在继承的类中覆盖它们。这将允许您访问这些字段,而无需检查它们的类型
public class Item
{
public virtual int m_damage { get; set; }
}
public class Weapon : Item
{
public override int m_damage => 4;
}
或者,正如@ragyaiddo所指出的,更好的解决方案是在项
类中创建一个名为PrintInfo()
的方法,然后将覆盖到继承的类中,该类只需打印出有关该项
的信息
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);
}
}
在面向对象编程中,我们依靠虚拟调度来进行竞价,因此我通常不使用is
关键字来确定对象的类型。我相信,如果你有一个类似于以下的遗传,那么你就可以做到
// items are outputted
foreach (var item in qItems)
Console.WriteLine(item.ShowStats());
}
项目继承:
public abstract class Item {
public abstract string ShowStats();
}
那么盔甲就会被摧毁
public class Armor : Item {
private readonly double _armor;
public Armor(double armor) {
_armor = armor;
}
public override string ShowStats() {
return $"Armor: {_armor}";
}
}
武器将是
public class Weapon : Item {
private readonly double _damage;
public Weapon(double damage) {
_damage = damage;
}
public override string ShowStats() {
return $"Damage: {_damage}";
}
}
正如用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);
正如我在评论中指出的,您可以在Item
类中定义一个抽象方法,并让继承类实现它们的特定实现。我创建了一个示例实现。这与@Emrah Süngü的解决方案类似
根据您的需求,您可以通过或的实现进一步实现这一点。当您有多个类需要遵循相同的通用工作流时,这些模式是适用的,但这些类中的每个类都有所述工作流的不同内部实现。您的意思是ItemStats只是一个类,而不是10+个子类?很可能,在OO中,用一种可以配置事物以获得变体的方法来代替子类化是好的(这是组合而不是继承的一部分)。如果您的子类所做的唯一事情就是提供统计信息,那么这是一个好的选择。但如果它们也提供了新的行为,则可能需要一种不同的方法。我可能会用统计数据和行为来编写东西我可以想象,以后编写统计数据和行为时需要这种灵活性。你觉得我应该保持现状吗?我不知道。这是一个与您的总体设计有关的更大的问题。如果你有10多个子类,那么你必须弄清楚它们是如何相互作用的,然后你可能会发现你的设计非常笨重,很难处理。乍一看,我认为子类有助于保持事物的有序性。否则,在呈现或返回统计数据之前,在一个类中需要测试大量的条件。我想我得看看情况如何,如果需要的话,以后再换。我喜欢这样做作为一种选择。有一件事我应该提到:如果(物品是武器)控制台,你在哪里写了。WriteLine(…,(物品是武器)。m#damage)
可以很容易地更改为如果(物品是武器w)控制台。WriteLine(…,w.m#damage)
@Frontear取决于他们使用的是哪个c版本,我从来不知道你能做到!容易多了。谢谢分享。我用的是C#7.2。@Frontear不,这不正确is
关键字在框架中已经存在很长时间了。与is
关键字匹配的模式来自c#7
。您可以很容易地测试它。您不能在项
中定义一个方法,比如PrintInfo()
,继承类有自己的实现吗?因此,您不必显式地强制转换它们。因此,在您的foreach中,您可以直接调用item.PrintInfo()
OP询问收集所有项目的情况。很有趣。我从未想过在foreach中使用“var”。我以前试过普通的“Item”而不是“var”,但遇到了问题。Var解决了这些转换问题。我认为原因是var使用IEnumerable获得了一个已生成的对象,但我可能是错的。@Bob只要编译器能够确定在Item类中创建属性时可以使用的对象类型:var
:是的,我想我必须使用“is”进行转换,以查看该属性是否有效。否则,将返回类型为“剑”的无效属性,如“盔甲类”
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);
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());