遍历泛型字典时抛出异常

3

I have the following code:

private Dictionary<int, Entity> m_allEntities;
private Dictionary<Entity, List<IComponent>> m_entityComponents;

public Dictionary<int, T> GetComponentType<T>() where T: IComponent
{
    Dictionary<int, T> components = new Dictionary<int, T>();

    foreach (KeyValuePair<int, Entity> pair in m_allEntities)
    {
        foreach (T t in m_entityComponents[m_allEntities[pair.Key]])
        {
            components.Add(pair.Key, t);
        }
    }

    return components;
}

m_allEntities = 存储所有实体的字典,键为它们的ID,值为实体对象。 m_entityComponents = 存储所有实体及其组件列表的字典,键为实体对象,值为它的组件列表。

我有几个不同的类实现了IComponents接口。GetComponentType()函数的作用是循环遍历所有实体,并创建一个特定类型的实体ID和组件的字典。

例如:如果我想要获取所有具有位置组件的实体列表,则可以使用:

entityManager.GetComponentType<Location>();

我遇到的问题出在第二个循环中,因为它似乎没有过滤我的字典。相反,它试图将字典中所有组件强制转换为“Location”类型,这当然会引发异常。我该如何修改这段代码以达到想要的效果?
2个回答

4
如果您的集合具有不同类型,则需要测试正确的类型。
public Dictionary<int, T> GetComponentType<T>() where T: IComponent
{
    Dictionary<int, T> components = new Dictionary<int, T>();

    foreach (KeyValuePair<int, Entity> pair in m_allEntities)
    {
        foreach (IComponent c in m_entityComponents[m_allEntities[pair.Key]])
        {
            if (c is T)
                components.Add(pair.Key, (T)c);
        }
    }

    return components;
}

谢谢,我刚测试了这个更改,但它甚至无法编译。失败的那一行是:var t = c as T;。错误信息为:“类型参数 'T' 不能与 'as' 运算符一起使用,因为它没有类类型约束或 'class' 约束。” - Kittoes0124
@Kittoes - 我会忽略错误非常具体这一事实。但是,如果您查看我的更改,它们将消除对类约束的需求。 - ChaosPandion
非常感谢您。它最终运行得非常好。jimmy_keen也在下面发布了一个解决方案,我非常喜欢。您建议使用哪个(因为它们都完全符合我的需求)? - Kittoes0124
2
@Kittoes - 请注意,jimmy_keen提供的解决方案将为您节省一行代码。然而,有人可能会认为这个解决方案稍微更有效率,因为它创建了一个较少的迭代器。 - ChaosPandion

2

您可以使用.OfType<T>方法:

foreach (T t in m_entityComponents[m_allEntities[pair.Key]].OfType<T>())

用于过滤与您的泛型类型匹配的元素。


我刚试了一下,但是.OfType<T>()不是我的方法选项。我需要包含其他using语句才能使用这个功能吗? - Kittoes0124
@Kittoes:是的,你需要使用LINQ命名空间。只需添加using System.Linq;即可。 - k.m
我也是这么想的,应该先尝试添加LINQ再询问。我真的很喜欢这个解决方案,因为我不需要转换任何东西。ChaosPandion上面的解决方案也非常好用。这两个选项哪个更“安全”,它们之间是否有性能差异? - Kittoes0124
1
@Kittoes:OfType<T>在底层执行了t is T比较(加上其他几个与LINQ相关的东西)。本质上是一样的。我不会争论哪种更有效率——一旦编译器完成它的工作,差异很可能不存在。选择那种解释代码目的更好(即更易读、理解等)的解决方案。 - k.m

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接