几天前我被告知使用 is 是代码气味和反模式 .

我在游戏中使用它来过滤集合中的一些元素(播放器的库存) .


Classes structure

集合元素的基类是 Item

public abstract class Item
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

从该类派生出3个类, PotionArmorWeapon

药水是 IUsable

public interface IUsable
{
    void Use(Character target);
}

IStackable

public interface IStackable
{
    int Quantity { get; set; }
    int MaxQuantity { get; set; }
}

例如,类 Potion 是这样定义的:

public class Potion : Item, IUsable, IStackable
{
    //Some properties and implementations of interfaces
}

武器和盔甲是 IEquipable

public interface IEquipable
{
    void TakeOn(Character target);
    void TakeOff(Character target);
}

这是 Armor 的缩写示例

public class Armor : Item, IEquipable
{
    //Some properties and implementations of interfaces
}

一个 Weapon

public class Weapon : Item, IEquipable
{
    //Some properties and implementations of interfaces
}

Collection access

现在,考虑到告诉我的是闻闻的部分:

要访问集合中的某些特定项,我使用的是通用方法 SpecificInventoryItems<T>() ,其中包含此部分以获取预期的 T 项:

foreach (Item item in player.Inventory)
{
    if (item is T)
    {
        string stack = "";
        if (item is IStackable)
        {
            var stackable = item as IStackable;
            stack = "(" + stackable.Quantity + "/" + stackable.MaxAmount + ") ";
        }
        options += "[" + i++ + "] - " + item.Name + " " + stack + ": " + item.Description;
        if (item is Armor)
            options += " " + ArmorDetail(item as Armor);
        else if (item is Weapon)
            options += " " + WeaponDetail(item as Weapon);
        options += "\n";
        items.Add(item as T); //items is List<T>
    }
}

它出什么问题了?怎么可能(应该)我重新设计这个结构?我虽然使用分隔列表,如 List<IUsable> 等,而不仅仅是 List<Item> Inventory

另一个例子,在显示库存的特定元素之后,用户可以选择一个项目,并且根据其类型,该项目将被使用或被采用:

switch (selected) //selected type is T
{
    case IEquipable eq:
        eq.TakeOn(player);
        break;

    case IUsable us:
        us.Use(player);
        //Manage the deletion of the IUsable if not IStackable, or IStackable and quantity = 0
        break;

    default:
        break;
    }
}