如何在没有“dynamic”关键字的情况下在 C# 中动态定义方法的返回类?

How can I define a method's return class dynamically in C# without the 'dynamic' keyword?

提问人:Tirais 提问时间:5/30/2023 最后编辑:Tirais 更新时间:5/31/2023 访问量:89

问:

我有几个从“可脚本对象”Unity继承的类 - “Item”是主类,其他类是从中继承的:“Tool”、“Weapon”、“Food”和其他几个类。我只能通过字典访问类的每个实例(例如:我可以通过其名称(字符串)获取所需的项目,并且由于所有字典的键数据类型相同,因此我无法进行方法重载。因此,在运行时,必须定义类并且它必须返回,但我不知道如何在没有动态类型的情况下做到这一点。 我认为可以通过元组同时返回多个类实例,但是您必须为每个方法调用创建一个额外的检查,当代码增长时,这将导致代码支持困难。据我所知,动态类型如果您经常使用它,可能会导致性能下降。Dictionary <string, Tool> toolsDatabase; Dictionary <string, Weapon> weaponsDatabase; Dictionary <string, Food> foodsDatabase");

这里有一些代码。也许我真的没有为 C# 提出正确的问题。简而言之,任务是使用一个关键字和一个方法从多个字典中获取不同的类,这些字典将不同的类存储在值中。 private static Dictionary<string, Item> items;

    public static Dictionary<string, Item> Items => items;
    private static Dictionary<string, Tool> tools;
    public static Dictionary<string, Tool> Tools => tools;
    private static Dictionary<string, Weapon> weapons;
    public static Dictionary<string, Weapon> Weapons => weapons;

    [SerializeField] private List<Item> itemsDatabase = new List<Item>();
    [SerializeField] private List<Tool> toolsDatabase = new List<Tool>();
    [SerializeField] private List<Weapon> weaponsDatabase = new List<Weapon>();

    public Item GetItem(string itemName)
    {
        foreach (var item in items)
        {
            if (item.Key == itemName)
            {
                return item.Value;
            }
        }
        foreach (var tool in tools)
        {
            if (tool.Key == itemName)
            {
                return tool.Value;
            }
        }
        foreach (var weapon in weapons)
        {
            if (weapon.Key == itemName)
            {
                return weapon.Value;
            }
        }
        return new Item();
    }
C# 继承 方法 类型

评论

0赞 Irwene 5/30/2023
泛型能满足您的需求吗?
1赞 CodeCaster 5/30/2023
请展示一些代码。您也许可以使用一个,但从您的描述中不清楚这是否足够。Tool GetTool(string name)
1赞 Fildor 5/30/2023
“我怎样才能动态地定义方法的返回类” - 你实际上不应该想要这样做。除非您需要像歧视联合这样的东西,它(尚)不受支持,但存在变通库。
0赞 Fildor 5/30/2023
或者确实是泛型,尽管我不确定这是否是您的意思: - 这仍然需要您在编译时知道类型。TItem Get<TItem>(string key) where TItem: Item
2赞 JonasH 5/30/2023
如果是答案,如果你问的是正确的问题,你应该认真思考。最自然的方法应该是 CodeCaster 建议的方法。我还建议阅读 Eric Lipperts Wizards and Warriors 系列,它讨论了使用类型系统来表示游戏中概念的困难。dynamicGetTool

答:

1赞 Olivier Jacot-Descombes 5/30/2023 #1

我会使用一本字典

Dictionary<string, Item> itemsDatabase;

然后,您可以选择查找具有给定键的任何类型的项,或者您可以将此方法与泛型类型参数一起使用:

private bool TryGetItem<T>(string key, out T t)
    where T : Item
{
    if (itemsDatabase.TryGetValue(key, out Item item) && item is T specificItem) {
        t = specificItem;
        return true;
    }
    t = null;
    return false;
}

用法

if (TryGetItem<Food>("apple", out Food apple)) {
    // ...
}

这可以通过仅指定参数的类型或仅指定泛型类型参数并让 C# 编译器推断另一个参数来简化:out

if (TryGetItem("apple", out Food apple)) {

}

// OR

if (TryGetItem<Food>("apple", out var apple)) {

}

或者您可以像这样打开类型

if (itemsDatabase.TryGetValue("someKey", out Item item)) {
    switch (item) {
        case Tool tool:
            tool.Apply();
            break;
        case Weapon weapon:
            weapon.Attack();
            break;
        case Food food:
            food.Eat();
            break;
    }
}

在某些情况下,还可以使用多态性来避免型式试验。示例:不要像 一样为每个项目类型使用不同的方法,如 ,如上所示,而是在抽象基类中添加一个抽象方法。然后,每种类型的项都会添加自己的实现。ApplyAttackEatUseItem

if (itemsDatabase.TryGetValue("someKey", out Item item)) {
    item.Use(); // Tools are applied, weapons attack and food is eaten.
}

评论

0赞 Tirais 5/31/2023
感谢您的建议。但事实证明,这是和以前一样的问题。由于我必须精确地返回特定类的实例,但将返回的是具有“Item”类的实例。在这种情况下,我将不得不进行显式类型转换以返回,例如,返回到原始的“Tool”类,并在调用此类方法时始终进行一些额外的检查。
0赞 Olivier Jacot-Descombes 5/31/2023
好吧,要么你知道你正在寻找一个工具,那么你可以使用我的方法,否则你将不得不做一个类型检查,除非你使用多态性。我添加一个例子。TryGetItem