【问题标题】:Why can't I cast an item to a generic type — where the generic type is an interface, and after checking that the item implements said interface?为什么我不能将一个项目转换为泛型类型——泛型类型是一个接口,并且在检查项目是否实现了所述接口之后?
【发布时间】:2020-12-17 04:23:34
【问题描述】:

说明

我正在尝试为我的游戏编写效果(à la buffs/debuffs)。这是我想要构建的:

EffectsManager 类包含所有主动效果。像 HealthController 这样的类会查询它以获取与健康相关的效果,即实现 IHealthEffect 接口的效果,并返回一个仅包含此类效果的 List。

尝试的解决方案

内部EffectsManager:

public List<T> GetEffects<T>() {
    List<T> effects = new List<T>();

    foreach (ActiveEffect activeEffect in activeEffects) {
        // ActiveEffect is just a wrapper for Effect that houses duration, time elapsed, etc.
        // I only need to return the Effect itself, which houses the logic.
        Effect effect = activeEffect.GetEffect();
        if (effect is T) {
            effects.Add((T)effect);
        }
    }

    return effects;
}

然后,调用者脚本会写:

List<IHealthEffect> healthEffects = effectsManager.GetEffects<IHealthEffect>();

问题

无法将类型“Effect”转换为“T”

开启effects.Add((T)effect);。

类似问题

Cannot implicitly convert type 'Int' to 'T'

但就我而言,我不是隐式而是显式地转换。为什么编译器不让我继续,我应该使用Convert.ChangeType()——正如那里所建议的那样——无论如何?

更多信息

Effect 只是一个只有 1 个成员的抽象类:public int priority;。

由于效果可能会影响多个属性,例如健康和法力,我决定使用要求实现者编写特定方法的接口,例如:void Execute(HealthController healthController);。

【问题讨论】:

    标签: c# generics types casting


    【解决方案1】:

    给你:

       if (effect is T typed) {
            effects.Add(typed);
       }
    

    至于原因:您需要在中间添加 object:(T)(object)effect

    【讨论】:

    • 谢谢!这比预期的要快得多。你能解释一下typed 是如何工作的或链接到一个资源吗?谷歌搜索“C# typed”或“C# T typed”似乎没有给我任何信息。
    • @verified_tinker typed 这里只是我们声明的变量名;它与is T foo 和Add(foo) 相同。这是最近的 C# 新增功能,它允许您在一个步骤中执行“类型测试并存储在局部变量中”,因为它非常常见并且避免了第二次强制转换;具体来说,它是 C# 7.0 中的“模式匹配”功能:docs.microsoft.com/en-us/dotnet/csharp/whats-new/…
    【解决方案2】:

    但就我而言,我不是隐式而是显式的演员。

    Effect 和T 不相关,因此编译器将尝试转换而不是强制转换,并且显然也没有定义类型之间的转换,因此出现错误。

    按照 Marc 的建议,您可以充分利用模式匹配:

    if (effect is T t) {
         effects.Add(t);
    }
    

    但如果您的代码与发布的完全一致,您可以完全通过 LINQ 实现:

    var healthEffects = activeEffects.Select(x => x.GetEffect()).OfType<IHealthEffect>();
    

    【讨论】:

    • 除了将代码变成单行代码之外,还有其他优势吗?性能——在标准的foreach 实现上使用 LINQ?
    • 1) 它可以提高内存效率,因为如果您只需要迭代项目,则不需要分配列表。尽管使用yield return 也可以达到同样的效果 2) 它减少了代码库中不必要的代码。当框架有等价物时,为什么要创建额外的方法? 3) 更灵活:您可以根据需要轻松链接其他 LINQ 方法,例如 Where 等。
    猜你喜欢
    • 1970-01-01
    • 2010-11-10
    • 2012-06-09
    • 2013-08-16
    • 2011-02-01
    • 1970-01-01
    • 2019-06-25
    • 2018-07-23
    • 2010-10-04
    相关资源
    最近更新 更多