C#中带有列表的动态类型

5

我是一位帮助翻译的助手。

有一个小问题需要大家帮忙解决。

我想要做以下事情:

Type[] classes = new Type[]{ Class1, Class2 };

foreach(Type t in classes){   
  List<t> list = new List<t>(); 
}

有没有一些方法可以做到这一点?

想知道 foreach 的用途是什么? - V4Vendetta
我不想使用 typeof(class1),我希望它完全动态化。 - Stian Standahl
V4Vendetta:循环遍历类变量中的类型。 - Stian Standahl
4
似乎是XY问题 - L.B
MSDN详细介绍了如何实例化泛型类型:http://msdn.microsoft.com/zh-cn/library/b8ytshk6.aspx - Adam Houldsworth
显示剩余4条评论
5个回答

7

在运行时无法将类型转换为泛型类型,因为泛型中的类型需要在编译时解析。

您可以使用反射以动态方式创建泛型类型,但除非硬编码转换,否则返回的只是一个对象。

我无法真正了解您想要什么,我同意评论中的观点,这是一个XY问题。我倾向于做出放肆的声明,即这里试图解决某个设计问题,而不是直接解决设计问题,或者直接问您想要实现什么。


您可以使用以下代码创建类型,然后使用dynamic类型来鸭子类型 List<T> 的各个成员,而不需要知道/关心它是列表还是T是什么:

using System;
using System.Collections.Generic;

namespace ConsoleApplication61
{
    class Program
    {
        static void Main(string[] args)
        {
            dynamic o = CreateGeneric(typeof(List<>), typeof(int));
            o.Add(1);
            Console.WriteLine(o[0]);
            Console.Read();
        }

        public static object CreateGeneric(Type generic, Type innerType, params object[] args)
        {
            System.Type specificType = generic.MakeGenericType(new System.Type[] { innerType });
            return Activator.CreateInstance(specificType, args);
        }
    }
}

上面的示例使用鸭子类型来处理Add方法和索引器。DLR在运行时处理类型和鸭子类型 - 例如,知道1是一个int
只是为了澄清,我可能不会在生产中使用这样的代码(除非您的要求非常特定),并且任何类型不匹配的问题都将在运行时发生;因此,您需要非常准确地输入类型(有限的IntelliSense)或具有良好的错误处理。
感谢this blog post提供CreateGeneric方法。
这假定使用新CLR的.NET 4。正如@MartinLiversage也指出的那样,这个特定的示例假定您以某种强类型的方式利用列表。在我的示例中,我将int传递给隐藏在dynamic中的List<int>
我们自.NET 4发布以来就一直在使用它。我们有一个庞大的应用程序和更大的代码库。dynamic 在应用程序中没有被使用过,在测试代码库中也只用了几次。这并不是说“不要使用”,而是说“大多数情况下,你不需要它”。

1
使用 dynamic 很不错,但调用 o.Add(1) 假设 o 是一个 List<int>,那么你可以一样地转换为 List<int> 而不是使用 dynamic。实际上,如果动态类型是 List<long>List<short> 等,则 o.Add(1) 也可以工作,使其比特定列表的转换略具灵活性,因此在特殊场景下,它还是有些用处的,例如当你有不同宽度的数字列表时。 - Martin Liversage
@MartinLiversage 没错,但那只是我的例子。我不确定如果 1 是作为参数在其他地方提供的对象会发生什么 - DLR 是否会识别底层类型,或者它是否不关心并且仍然会尝试。在我看来,这种情况非常具体,如果他们正在动态创建特定列表,我只能假设它们的使用也是特定的。 - Adam Houldsworth

5
你可以像这样做:
foreach(Type t in classes)
{
   var listType = typeof(List<>).MakeGenericType(t);
   var instance = Activator.CreateInstance(listType);
}

3
Activator.CreateInstance会返回一个对象,如何在运行时将其转换为强类型以便访问t - Adam Houldsworth
@AdamHouldsworth:类型只有在运行时才知道,这使得无法获得强类型访问。 - Martin Liversage
1
@MartinLiversage 我知道,抱歉,这个问题更多是修辞上强调了一个缺失的部分 - OP有点暗示想以强类型方式使用该类型,但动态地...如果这有任何意义的话 lol - Adam Houldsworth
@MartinLiversage 的确,我不反对。我在问题本身的评论中已经推断出这一点 - 必须猜测意思意味着它可以被关闭。 - Adam Houldsworth
尝试使用dynamic特性。 - Rusted
显示剩余2条评论

3
这可以通过使用Type.MakeGenericType方法来实现。
这是一个很棒的方法,我找到了一个可以帮助你的: CodeRef
public static object CreateGeneric(Type generic, Type innerType, params object[] args)
{
    System.Type specificType = generic.MakeGenericType(new System.Type[] { innerType });
    return Activator.CreateInstance(specificType, args);
}

然后像这样使用:

var o = CreateGeneric(typeof(List<>), t);

很抱歉,要添加项目,您需要按以下方式执行(其中item是您要添加的项目)。

MethodInfo addMethod = o.GetType().GetMethod("Add");
addMethod.Invoke(o, new object[] { item.ToType(t) });

或者像另一个答案中提到的那样使用“通用”类型。

1
你能展示一些代码如何将 o 强制转换为适当的 List<T> 吗?我对此非常感兴趣。 - Michal B.
1
我认为这个例子没有涵盖到缺失的隐含需求,即能够在没有转换能力的情况下正常使用列表(除非你硬编码类型,否则会使类型列表失去意义)。 - Adam Houldsworth
据我所知,你无法将o强制转换为List<whatever>。你必须使用反射来获取诸如“Add”之类的方法,并使用Invoke来调用它们。但是,可能可以从非泛型基类派生并添加任何方法。 - Chris Gessler

1
你可以尝试这个:
Type[] classes = new Type[] { typeof(A), typeof(B) };           
foreach (Type t in classes)
{
     Type genericType = typeof(List<>).MakeGenericType(t);
     var list = Activator.CreateInstance(genericType);
}

试图动态创建类型的意义是什么?如果最终得到一个非泛型类型的实例,那还有什么意义呢? - Pencho Ilchev
@PenchoIlchev - 这样使用Add等方法就不会那么痛苦了。 - Chris Gessler
@ChrisGessler 他将返回的对象转换为IList。请查看编辑。 - Pencho Ilchev

1

如果您想将对象保留在一个列表中,那么它们可能有一些共同点。

在这种情况下,我认为,就像L.B在评论中提到的那样,您在这里问了一个错误的问题。

可能是您的设计问题。考虑这些类型,看看它们有什么共同点,并考虑从一个基本类型派生或使两者实现相同的接口。在这种情况下,您将能够实例化一个基本类型/接口对象的列表并使用它们。

只是为了给您一个开头:

    abstract class Vehicle {
        public int NumberOfWheels { get; set; }
    }

    class Car : Vehicle
    {
        public Car()
        {
            NumberOfWheels = 4;
        }
    }

    class Bicycle : Vehicle
    {
        public Bicycle()
        {
            NumberOfWheels = 2;
        }
    }

    static void Main(string[] args)
    {

        var v1 = new Car();
        var v2 = new Bicycle();

        var list = new List<Vehicle>();
        list.Add(v1);
        list.Add(v2);

        foreach (var v in list)
        {
            Console.WriteLine(v.NumberOfWheels);
        }

        Console.ReadKey();
    }

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