将子类型对象添加到超类型列表,然后从超类型列表返回子类型列表



我有3个接口

public interface IItem
{
string Name { get; set; }
}
public interface IEquipable : IItem
{
void Equip();
}
public interface IConsumable : IItem
{
void Use();
}

IEquipableHelmetBow类实现,IConsumablePotionFood类实现。

然后,我有一个包含IItem列表的属性的类,并在实例化它后继续添加IEquipableIConsumable的一些项目。

public class Character
{
public List<IItem> Items { get; private set; }
public Character()
{
this.Items = new List<IItem>();
}
public void AddItem(IItem item)
{
this.Items.Add(item);
}
}

Program.cs

...
Character char = new Character();
char.AddItem(new Potion());
char.AddItem(new Food());
char.AddItem(new Helmet());
char.AddItem(new Bow());
...

是否有办法从IItems列表中获得所有IEquipable成员的列表,每个IEquipable?

我想做一些像

...
List<IEquipable> equipmentList = //do something to char.Items and get all items of type IEquipable.
IEquipment equipment = equipmentList.First(...)
equipment.Equip();
...

我试过使用List<IEquipable> equipmentList = char.Items.OfType<IEquipable>().ToList(),但结果列表最终为空。

我在你的代码中实现(并修复了一些小的拼写错误)如下:

void Main()
{
Character character = new Character();
character.AddItem(new Potion());
character.AddItem(new Food());
character.AddItem(new Helmet());
character.AddItem(new Bow());

List<IEquipable> equipmentList = character.Items.OfType<IEquipable>().ToList();
}
public class Potion : IConsumable
{
public string Name { get; set; }
public void Use()
{
throw new NotImplementedException();
}
}
public class Food : IConsumable
{
public string Name { get; set; }
public void Use()
{
throw new NotImplementedException();
}
}
public class Helmet : IEquipable
{
public string Name { get; set; }
public void Equip()
{
throw new NotImplementedException();
}
}
public class Bow : IEquipable
{
public string Name { get; set; }
public void Equip()
{
throw new NotImplementedException();
}
}
public interface IItem
{
string Name { get; set; }
}
public interface IEquipable : IItem
{
void Equip();
}
public interface IConsumable : IItem
{
void Use();
}
public class Character
{
public List<IItem> Items { get; private set; }
public Character()
{
this.Items = new List<IItem>();
}
public void AddItem(IItem item)
{
this.Items.Add(item);
}
}

您的确切代码(尽管char重命名为character)工作得非常好。equipmentList最终有两个元素。您所看到的问题,即"结果列表最终为空",与您发布的代码不可重复。

您可以使用OfType方法

根据指定的类型筛选IEnumerable中的元素。

public static IEnumerable<TResult> OfType<TResult> (this IEnumerable source)

使用

var equipable = Character.Items.OfType<IEquipable>();

或者将其封装为实例中的方法或扩展方法(如果您喜欢)

所以它确实像我想要的那样工作。我的实际代码只是有另一个问题,我是一个傻瓜,实际上没有发布。所以就在这里,供将来参考。

using System.Collections.Generic;
using RolePlayGame.Library.Items.Backstage;
using System.Linq;
using System.Text;
using System;
namespace RolePlayGame.Library.Characters.Backstage
{
public class Inventory
{
public List<IItem> StoredItems { get; private set; }
public List<EquippedItem> Gear { get; private set; }
public Inventory()
{
this.StoredItems = new List<IItem>();
this.Gear = new List<EquippedItem>();
}
public bool HasItem(string name)
{
return this.StoredItems.Exists(item => item.Name == name);
}
public bool HasItem(IItem item)
{
return this.StoredItems.Contains(item);
}
public void RemoveItem(string name)
{
int firstIndex = this.StoredItems.FindIndex(item => item.Name == name);
if (firstIndex != -1)
{
this.StoredItems.RemoveAt(firstIndex);
}
}
public void RemoveItem(IItem item)
{
int firstIndex = this.StoredItems.IndexOf(item);
if (firstIndex != -1)
{
this.StoredItems.RemoveAt(firstIndex);
}
}
public void AddItem(IItem item, int quantity)
{
for (int i = 0; i < quantity; i++)
{
this.StoredItems.Add(item);
}
}
public void AddItem(IItem item)
{
this.StoredItems.Add(item);
}
public bool CheckEquipmentSlot(EquipmentSlot slot)
{
return this.Gear.Exists(item => item.UsedSlots.Contains(slot));
}
public bool HasEquipment(IEquipment equipment)
{
return this.Gear.Exists(item => item.Item == equipment);
}
public void AddEquipment(IEquipment equipment)
{
IEquipment alreadyEquipped;
foreach (EquipmentSlot slot in equipment.SlotsUsed)
{
if (this.Gear.Exists(item => item.UsedSlots.Contains(slot)))
{
alreadyEquipped = this.Gear.Find(item => item.UsedSlots.Contains(slot)).Item;
this.RemoveEquipment(slot);
this.StoredItems.Add(alreadyEquipped);
}
}
EquippedItem newEquipment = new EquippedItem(equipment);
this.Gear.Add(newEquipment);
}
public void RemoveEquipment(EquipmentSlot slot)
{
this.Gear.RemoveAll(equipment => equipment.UsedSlots.Contains(slot));
}
public int GetAttributeBonusTotal(AttributeType attribute)
{
int bonusTotal = 0;
foreach (IEquipment item in this.StoredItems.OfType<IEquipment>().ToList())
{
bonusTotal += item.GetAttributeBonus(attribute);
}
return bonusTotal;
}
public int GetCarryWeight()
{
int totalWeight = 0;
foreach (IItem item in StoredItems)
{
totalWeight += item.Weight;
}
return totalWeight;
}
public string GearToString()
{
StringBuilder builder = new StringBuilder();
builder.Append("    Equipped Gear:");
foreach (EquippedItem equipment in this.Gear)
{
builder.Append($"n        {equipment.Item.Name}");
}
return builder.ToString();
}
public string ItemsToString()
{
StringBuilder builder = new StringBuilder();
builder.Append("    Inventory:");
foreach (IItem item in this.StoredItems.Distinct())
{
builder.Append($"n        {item.Name} x {this.StoredItems.FindAll(value => value == item).Count()}");
}
return builder.ToString();
}
public int GetDefenseRateAgainstTypeTotal(DamageType againstType)
{
int rate = 0;
List<IOutfit> outfits = this.Gear.Select(value => value.Item).OfType<IOutfit>().ToList();
foreach (IOutfit item in outfits)
{
rate += item.GetDefenseRateAgainstType(againstType);
}
return rate;
}
}
}

最后一行有问题(现已修复)。List<IOutfit> outfits = this.Gear.Select(value => value.Item).OfType<IOutfit>().ToList();以前为List<IOutfit> outfits = this.Gear.OfType<IOutfit>().ToList();。但是Gear的类型是List<EquippedItem>,EquippedItem不是IItem的实现。

Here is EquippedItem.cs

using RolePlayGame.Library.Items.Backstage;
using System.Collections.Generic;
namespace RolePlayGame.Library
{
public class EquippedItem
{
public List<EquipmentSlot> UsedSlots { get; set; }
public IEquipment Item { get; set; }
public EquippedItem(IEquipment equipment)
{
this.Item = equipment;
this.UsedSlots = equipment.SlotsUsed;
}
}
}

在使用.OfType<IOutfit>()进行类型过滤之前,我需要从Gear中的项目中选择Item属性作为另一个列表。这就是.Select(value => value.Item)登场的地方。

就是这样。我将学习张贴实际的代码以供将来的问题。