运行时创建泛型对象列表

6

我有一个在运行时已知类型的对象,我从数据库中读取并反序列化这个对象。它可以工作。现在我想将它添加到某个列表中:

private static List<T> generateList<T>()
{
    List<T> lst = new List<T>();
    return lst;
}

private void readObjects(System.Type objType)
{
    var methodInfo = typeof(My.Serializator).GetMethod("DeserializeDb");
    var genericMethod = methodInfo.MakeGenericMethod(objType1);
    List<curType> currentList= generateList<curType>();
    // ...read stream from database and convert it to object
    while (_rs.Read())
    {
        var _objItem = genericMethod.Invoke(null, new[] { _streamedData });
        currentList.Add(_objItem);
    }
}

无法工作。错误是:

curType是一个变量,但被使用如同类型。

如果我把列表更改为:

 List<object> currentList = new List<object>(); 

它可以工作。但我能否使用泛型(T)代替对象类型来实现这一点?


3
不能指望编译器推断你在运行时提供的类型。无论你使用反射或其他方法,它都将始终返回 "object"。你必须在编译时知道确切的类型是什么。 - MakePeaceGreatAgain
1
我们无法确定curType是类型还是变量或其他内容,因为您没有显示它。如果curTypeSystem.Type类型的表达式,那么它是一个变量,而不是“类型”。 - Jeppe Stig Nielsen
2
你正在使用 curType,但它没有在任何地方声明。你是不是想用 objType?如果是,请编辑问题。 - Peter B
为什么不将readObjects泛型化?既然它是某种反序列化,假定调用者应该在编译时知道正确的类型?为什么你实际上想使用运行时反射? - Luaan
除非您展示更多细节,例如curType、_rs、objType1和_streamedData,否则我们将无法提供帮助。 - nozzleman
显示剩余4条评论
1个回答

4

您可以通过Activator轻松创建所需的列表类型,然后将其强制转换为IList并使用:

private IList readObjects(System.Type objType)
{
    var listType = typeof(List<>).MakeGenericType(curType);
    var list = (IList)Activator.CreateInstance(listType);

    // ...

    while (_rs.Read())
    {
        // ...
        list.Add(_objItem);
    }
}

list将是List<YorActualType>的实例。

更新

当您声明具有通用参数的方法时,它假定您在编译时提供类型信息。否则,您需要使用反射。

由于您在运行时提供类型信息(curType可以保存任何类型信息),因此编译器不知道将使用什么类型,并且您无法声明方法返回某个具体内容。只允许抽象。

让我通过稍微疯狂但具有说明性的示例来展示:

var types = new [] { typeof(int), typeof(string) };
var rand = new Random();
var list = readObjects(types[rand.Next(0,2)];

直到最后一刻,您甚至也不会知道会创建什么类型的列表。编译器也不知道。如果您不提供类型,编译器将永远不知道应该使用什么类型。当您使用Type时,它只告诉编译器在运行时将传递带有Type类型的某些常规参数到方法中。在编译时没有数据可以推断出类型。该数据只能通过泛型类型参数传递。

因此,您可以采用以下几种方式:

  1. Provide exact types you need at compile time

    private List<T> readObjects<T>()
    {
        var objType = typeof(T);
        var list = new List<T>();
        // rest of code....
    }
    
  2. Use reflection and base types

    private IList readObjects(Type objType)
    {
        // rest of code with Activator and so on
    }
    

    And later usage depends on your needs. If you know what type you going to use, simply convert:

    var list = (IList<MyType>)readObjects(typeof(myType));
    

    But I guess in that case better use way #1 with generic argument.

    Otherwise you going to use reflection. Or some base classes, interfaces and so on. It depends on what exactly task you going to solve.

附注:您可以在MSDN上阅读更多关于泛型类型的内容。


然而,这并不能阻止某人在列表中放置任何不同类型的实例,至少在编译时是不可能的。相反,您会得到一个 ArgumentException,指示提供的类型无法添加到泛型列表中。 - MakePeaceGreatAgain
@HimBromBeere 当然,这里没有编译时检查。但是在运行时会抛出异常。此外,他获取数据的方法是通用的,因此不太可能出现错误。我相信该方法返回通用参数类型的值。 - lorond
谢谢。这正是我想要的。如果我有一个返回此列表的方法,我应该如何声明它并调用它? private List<T> readObjects(System.Type objType) - Simon
好的,我现在有了:"private IList readObjects(Type objType)"和"var dbData = readData(myType);"因为我不知道类型(我从readObjects方法内部的数据库中获取运行时类型)。它可以工作,在最后我有正确类型的对象列表。只是多了这个:我有一个方法:"public static void WriteToXmlFile<T>(string filePath, T objectToWrite, bool append = false) where T : new()"和我有iList:"var dbData = readData(myType);"我该如何调用writeToXmlFile方法,类似于:"WriteToXmlFile<typeof(dbData)>("C:\salesmen.txt", dbData);" - Simon
@Simon 你可以像调用DeserializeDb一样调用WriteToXmlFile。但是如果是你的代码,最好在这种情况下删除泛型。为什么需要它们,当这段代码应该能够序列化任何东西?使用object而不是T看起来更合理。此外,在序列化方法的泛型参数上有一个可疑的new()约束。这个方法是否创建T的实例?为什么?它应该序列化现有实例。 - lorond
谢谢,我可以使用对象代替泛型。 - Simon

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