3

这是一个两部分的问题。

首先
,我有一个名为 ComponentList 的类,如下所示:

public class ComponentList<T> : List<T> where T : Component
{

}

现在我想创建一个无类型 ComponentLists 的空列表:

List<ComponentList<>> MasterList = new List<ComponentList<>>();

我收到一个错误,因为 ComponentList 想要指定一个泛型类型,即使我还没有尝试初始化任何 ComponentLists(只是包含它们的 MasterList)。如何在不初始化任何 ComponentLists 的情况下声明 ComponentLists 的 MasterList(因为我计划在运行时使用仅在运行时知道的类型来初始化它们)?毕竟,MasterList 需要包含不同泛型类型的 ComponentList,而不仅仅是一个。

其次
,我知道以前有人问过这个问题,但我似乎无法理解任何提议的解决方案的概念。

如您所知,我有一个List<>名为 MasterList 的列表,它是 ComponentLists(自定义类)的列表。ComponentList 是未定义类型的泛型类(被限制为 Component 的子类型)。

在下面的示例中,我尝试检查 ComponentList 泛型类型(从 MasterList 引用)是否与此类(调用代码的类)相同。

if (MasterList[i].GetType() == typeof(ComponentList<this.GetType()>))
{

}

问题是,代码是从未知的子类中自动调用的,而不是从这个父类中调用的。所以 ComponentList 泛型类型需要和子类的类型进行比较,而不是这个基类。因此,“this.GetType”代替了实际基类的硬编码名称。

this.GetType()ComponentList<> 中传递的类型显然返回“预期类型”错误,因为返回GetType()编译时类型,而不是运行时类型(这是我需要的)。

那么我怎样才能获得运行时类型呢?如果我做不到,那么完成我想做的事情的最佳选择是什么?(我听说过一些叫做反射的东西可能对我有帮助,但我真的不明白)。

4

3 回答 3

2

您无法在运行时确定泛型类型。C#(实际上都是 .Net)是故意这样设计的。编译器将泛型类型视为与显式定义的类型几乎相同的方式。

考虑以下:

class MyGenericClass<T>
{
    public static T MyProperty { get; set; }
}

class MyIntClass
{
    public static int MyProperty { get; set; }
}

class UseThem
{
    public void test()
    {
        // both are JIT'ed to be exactly the same machine code
        var foo = MyGenericClass<int>.MyProperty;
        var bar = MyOtherClass.MyProperty;
    }
}

因此,您必须为泛型类提供一个类型,以便 JIT 知道要编译什么。

可能的替代方案 如果最终可能成为泛型类型的所有可能类型都相似(从同一个基类继承或实现类似的接口),那么您可以使用接口或基类作为泛型类型来实例化泛型类:

List<ComponentList<ComponentBase>> MasterList = new List<ComponentList<ComponentBase>>();
// -OR-
List<ComponentList<IMyComponent>> MasterList = new List<ComponentList<IMyComponent>>();

我有一种预感,你应该能够通过一些创造性的重构来定义一个通用接口或基类。:)

于 2013-05-03T03:53:54.900 回答
0

I've run into this issue as well when I was trying to set up an entity-component system for a game written in C#. There's really isn't a way to store components as their actual types, you have to store them all as Components and cast them.

The way I have it set up is as a Dictionary<Type, List<Component>> as a private member of a ComponentManager class. The method that adds components is generic and checks if it's Type is contained in the Dictionary, so getting an IEnumerable<SpecificComponent> is as simple as:

public IEnumerable<T> EnumerateComponents<T>()
    where T : Component
{
    foreach (Component c in components[typeof(T)])
        yield return (T)c;
}

(You'll also want to check that the dictionary contains typeof(T), that bit is built-in with a custom collection of mine that inherits from Dictionary to avoid exceptions in cases like this.)

Type safety is "guaranteed" as long as the dictionary is never modified outside of a generic method (and definitely not directly accessible from the outside). Not ideal, but it's fast enough where it will never be your bottleneck.

EDIT

Something to explore might be C# 4's dynamic keyword. I haven't looked into it much, but storing the components as a List<dynamic> might work better (or it may introduce way too much overhead), just something to think about.

于 2013-05-03T05:35:27.420 回答
-1

我认为您的要求不可能,但是也许这就是您所需要的。

public class ComponentA : Component { }
public class ComponentB : Component { }
public class Component { }

public class ComponentList<T> : List<T> where T : Component
{

}

class Program
{
    static void Main(string[] args)
    {
        ComponentList<Component> MasterList = new ComponentList<Component>();

        MasterList.Add(new ComponentA());
        MasterList.Add(new ComponentB());

        for (int i = 0; i < MasterList.Count; i++)
        {
            if (MasterList[i] is ComponentA)
            {
            }

        }
    }
}
于 2013-05-03T03:37:53.193 回答