【问题标题】:How could I declare list of generics with different and unknown types and How could I initialize a generic with a type only known at runtime?如何声明具有不同类型和未知类型的泛型列表以及如何使用仅在运行时知道的类型初始化泛型?
【发布时间】:2013-05-03 03:26:20
【问题描述】:

这是一个两部分的问题。

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

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

}

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

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

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

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

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

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

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

}

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

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

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

【问题讨论】:

  • 使用泛型的全部目的是编译时类型安全。类型在编译时已知并且需要提供参数。
  • 那么我将如何制作一个不是泛型的列表(因此编译时类型不安全)?

标签: c# list generics types


【解决方案1】:

您无法在运行时确定泛型类型。 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>>();

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

【讨论】:

  • 哦,好的,这解决了实例化列表的第一个问题。我将只使用所有条目派生自的基本 Component 类(感谢约束)。谢谢!我仍然需要初始化运行时类型列表的替代方法。我需要为从 Component 派生的每个类创建一个单独的列表,但是从组件派生的类只有在运行时才知道。
  • 在问题的第二部分,您只想知道泛型类型。 Jon Skeet 的这个回答很好地解释了如何查看泛型类型参数:stackoverflow.com/a/293908/337759
【解决方案2】:

当我尝试为用 C# 编写的游戏设置实体组件系统时,我也遇到了这个问题。确实没有办法将组件存储为它们的实际类型,您必须将它们全部存储为 Components 并转换它们。

我设置它的方式是作为 Dictionary&lt;Type, List&lt;Component&gt;&gt; 作为 ComponentManager 类的私有成员。添加组件的方法是通用的,并检查它的 Type 是否包含在 Dictionary 中,因此获取IEnumerable&lt;SpecificComponent&gt; 很简单:

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

(您还需要检查字典是否包含typeof(T),该位是内置于我的自定义集合中,该集合继承自 Dictionary 以避免在这种情况下出现异常。)

只要字典从未在泛型方法之外修改(并且绝对不能从外部直接访问),类型安全就“得到保证”。不理想,但它足够快,永远不会成为您的瓶颈。

编辑

要探索的可能是 C# 4 的 dynamic 关键字。我没有深入研究它,但是将组件存储为 List&lt;dynamic&gt; 可能会更好(或者它可能会引入太多开销),只是需要考虑一下。

【讨论】:

  • 感谢大家的回答!我现在有一些新的东西要研究和尝试。 :D
【解决方案3】:

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

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)
            {
            }

        }
    }
}

【讨论】:

  • 如此广泛的概括消除了将 T 限制为有用的东西的目的。您不妨初始化一个 List&lt;List&lt;Object&gt;&gt; 并丢失所有强类型。
  • 如何将 T 设置为 ComponentList 的约束(组件)? List> MasterList = new List>();
猜你喜欢
  • 2010-10-10
  • 1970-01-01
  • 2019-06-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-06
  • 1970-01-01
  • 2014-09-19
  • 1970-01-01
相关资源
最近更新 更多