我如何使用 Activator.CreateInstance 创建一个 List<T> ,其中 T 在运行时是未知的?
How can I use Activator.CreateInstance to create a List<T> where T is unknown at runtime?
我正在使用 Activator.CreateInstance
通过类型变量创建对象(在 运行 时间内未知):
static dynamic CreateFoo( Type t ) =>
Activator.CreateInstance( t );
显然,我还没有正确理解如何使用 dynamic
类型,因为这仍然只是 returning 一个对象。
我需要能够将集合传递给对 Activator.CreateInstance
的另一个调用,其中正在创建的类型可以是 List<T>
:
var values = Enumerable.Range( 1, 50 ).Select(
I => CreateFoo( typeof( Foo ) ) ).ToArray( );
//This will explode.
var foobar = Activator.CreateInstance(
typeof( List<Foo> ), values );
调用上面的代码时,它会爆炸并出现以下异常:
我明白它为什么这样做了——当用类型参数定义列表时,没有期望可枚举对象的列表的构造函数。
问题是我无法转换对象,因为我在 运行 时不知道类型。 Activator.CreateInstance
似乎只 return 一个对象,这对 List<Foo>
来说很好,因为我将使用依赖对象和属性来设置它们,所以盒装对象对它们来说非常合适,但是尝试创建列表时会破坏所有内容(并且可能会破坏任何其他具有期望类型参数的构造函数的内容)。
我在这里尝试完成的事情的正确方法是什么?
符合Minimal, Complete and Verifiable Example要求:
using System;
using System.Collections.Generic;
using System.Linq;
namespace MCVEConsole {
class Program {
static int Main( string[ ] args ) {
var values = Enumerable.Range( 1, 50 ).Select(
I => CreateFoo( typeof( Foo ) ) ).ToArray( );
//This will compile, but explode when run.
var foobar = Activator.CreateInstance(
typeof( List<Foo> ), values );
return 1;
}
static dynamic CreateFoo( Type t ) =>
Activator.CreateInstance( t );
}
class Foo {
public Foo( ) { }
}
}
Obviously, I do not yet properly understand how to use the dynamic type, because this is still just returning an object.
是的,你不明白。 dynamic
只不过是戴着一顶花哨帽子的对象。 return 类型的 dynamic
表示 "I return object
, but callers should allow operations on the returned object that would not normally be allowed on object
, and those operations will be resolved at runtime".
如果这不是你的意思,那么不要使用dynamic
。
I get why it is doing that - there is no constructor for a list expecting an enumerable of objects when the list is defined with a type argument.
正确。在您的代码中:
var values = Enumerable.Range( 1, 50 ).Select(
I => CreateFoo( typeof( Foo ) ) ).ToArray( );
values
是 dynamic[]
,这又只是 object[]
戴着一顶滑稽的帽子。因此,您使用 object[]
调用 List<Foo>
的构造函数,而不是需要的 IEnumerable<Foo>
。
What is the proper method for what I am trying to get done here?
正确要做的事情是停止使用静态类型语言来完成动态类型的工作,但我怀疑这将是行不通的。
您手头有一个object[]
。你想要的是 t[]
其中 t
是由反射提供的类型。 解决反射问题的方法几乎总是使用更多的反射。所以这样做。不要这样做:
var values = Enumerable.Range( 1, 50 ).Select(
I => CreateFoo( typeof( Foo ) ) ).ToArray( );
反射的使用还不够。这样做:
object foos = Array.CreateInstance(typeof(Foo), 50);
太棒了。现在你手头有一个Foo[50]
,你可以使用反射再次填充它,或者使它dynamic
并使用动态运行时来调度数组索引;我假设您可以编写该代码。如果不能,再问一个问题。
现在您有一个填充的 Foo[]
,即 IEnumerable<Foo>
,然后您可以将其传递给 List<Foo>
构造函数。
你到底为什么要这样做——创建一个 Foo[]
来获得一个 IEnumerable<Foo>
来获得一个与 Foo[]
具有相同内容的 List<Foo>
- - 我不知道。这似乎 太多步骤 比制作一个 List<Foo>()
没有参数的构造函数并直接填充它更简单的步骤!但这就是你问的问题,所以这就是得到回答的问题。
这听起来很像我们所说的 "XY question"。你对如何解决你的问题有一些疯狂的想法,你问的是这个疯狂的想法而不是问问题,所以答案本身就有点疯狂。真正的问题是什么?
使用这种方法:
class Program
{
static void Main(string[] args)
{
CreateListFromType(typeof(Foo));
CreateListFromType(typeof(int));
}
static object CreateListFromType(Type t)
{
// Create an array of the required type
Array values = Array.CreateInstance(t, 50);
// and fill it with values of the required type
for (int i = 0; i < 50; i++)
{
values.SetValue(CreateFooFromType(t), i);
}
// Create a list of the required type, passing the values to the constructor
Type genericListType = typeof(List<>);
Type concreteListType = genericListType.MakeGenericType(t);
object list = Activator.CreateInstance(concreteListType, new object[] { values });
// DO something with list which is now an List<t> filled with 50 ts
return list;
}
// Create a value of the required type
static object CreateFooFromType(Type t)
{
return Activator.CreateInstance(t);
}
}
class Foo
{
public Foo() { }
}
在这种情况下不需要使用dynamic
。我们可以只使用 object
作为我们创造的价值。非引用类型将使用装箱存储在 object
中。
为了创建 List<>
类型,我们可以首先获得泛型类型的表示,然后使用它来使用 MakeGenericType
方法创建具体类型。
请注意您在创建列表的 CreateInstance 调用中犯的错误:
尝试构建列表时,您需要将值数组作为一个元素嵌入到对象数组中。因此 Activator
将在 List<t>
中寻找一个构造函数,该构造函数需要一个 IEnumerable<t>
.
类型的参数
按照您编写的方式,Activator
寻找一个需要 50 个参数的构造函数,每个参数都是 t 类型。
使用非通用 IList 接口的较短版本
using System.Collections;
static IList CreateListFromType(Type t)
{
// Create a list of the required type and cast to IList
Type genericListType = typeof(List<>);
Type concreteListType = genericListType.MakeGenericType(t);
IList list = Activator.CreateInstance(concreteListType) as IList;
// Add values
for (int i = 0; i < 50; i++)
{
list.Add(CreateFooFromType(t));
}
// DO something with list which is now an List<t> filled with 50 ts
return list;
}
更接近实际用例:动态列表类型
static void Main(string[] args)
{
CreateListFromType(typeof(List<Foo>));
CreateListFromType(typeof(ObservableCollection<int>));
}
static IList CreateListFromType(Type listType)
{
// Check we have a type that implements IList
Type iListType = typeof(IList);
if (!listType.GetInterfaces().Contains(iListType))
{
throw new ArgumentException("No IList", nameof(listType));
}
// Check we have a a generic type parameter and get it
Type elementType = listType.GenericTypeArguments.FirstOrDefault();
if (elementType == null)
{
throw new ArgumentException("No Element Type", nameof(listType));
}
// Create a list of the required type and cast to IList
IList list = Activator.CreateInstance(listType) as IList;
// Add values
for (int i = 0; i < 50; i++)
{
list.Add(CreateFooFromType(elementType));
}
// DO something with list which is now a filled object of type listType
return list;
}
我正在使用 Activator.CreateInstance
通过类型变量创建对象(在 运行 时间内未知):
static dynamic CreateFoo( Type t ) =>
Activator.CreateInstance( t );
显然,我还没有正确理解如何使用 dynamic
类型,因为这仍然只是 returning 一个对象。
我需要能够将集合传递给对 Activator.CreateInstance
的另一个调用,其中正在创建的类型可以是 List<T>
:
var values = Enumerable.Range( 1, 50 ).Select(
I => CreateFoo( typeof( Foo ) ) ).ToArray( );
//This will explode.
var foobar = Activator.CreateInstance(
typeof( List<Foo> ), values );
调用上面的代码时,它会爆炸并出现以下异常:
我明白它为什么这样做了——当用类型参数定义列表时,没有期望可枚举对象的列表的构造函数。
问题是我无法转换对象,因为我在 运行 时不知道类型。 Activator.CreateInstance
似乎只 return 一个对象,这对 List<Foo>
来说很好,因为我将使用依赖对象和属性来设置它们,所以盒装对象对它们来说非常合适,但是尝试创建列表时会破坏所有内容(并且可能会破坏任何其他具有期望类型参数的构造函数的内容)。
我在这里尝试完成的事情的正确方法是什么?
符合Minimal, Complete and Verifiable Example要求:
using System;
using System.Collections.Generic;
using System.Linq;
namespace MCVEConsole {
class Program {
static int Main( string[ ] args ) {
var values = Enumerable.Range( 1, 50 ).Select(
I => CreateFoo( typeof( Foo ) ) ).ToArray( );
//This will compile, but explode when run.
var foobar = Activator.CreateInstance(
typeof( List<Foo> ), values );
return 1;
}
static dynamic CreateFoo( Type t ) =>
Activator.CreateInstance( t );
}
class Foo {
public Foo( ) { }
}
}
Obviously, I do not yet properly understand how to use the dynamic type, because this is still just returning an object.
是的,你不明白。 dynamic
只不过是戴着一顶花哨帽子的对象。 return 类型的 dynamic
表示 "I return object
, but callers should allow operations on the returned object that would not normally be allowed on object
, and those operations will be resolved at runtime".
如果这不是你的意思,那么不要使用dynamic
。
I get why it is doing that - there is no constructor for a list expecting an enumerable of objects when the list is defined with a type argument.
正确。在您的代码中:
var values = Enumerable.Range( 1, 50 ).Select(
I => CreateFoo( typeof( Foo ) ) ).ToArray( );
values
是 dynamic[]
,这又只是 object[]
戴着一顶滑稽的帽子。因此,您使用 object[]
调用 List<Foo>
的构造函数,而不是需要的 IEnumerable<Foo>
。
What is the proper method for what I am trying to get done here?
正确要做的事情是停止使用静态类型语言来完成动态类型的工作,但我怀疑这将是行不通的。
您手头有一个object[]
。你想要的是 t[]
其中 t
是由反射提供的类型。 解决反射问题的方法几乎总是使用更多的反射。所以这样做。不要这样做:
var values = Enumerable.Range( 1, 50 ).Select(
I => CreateFoo( typeof( Foo ) ) ).ToArray( );
反射的使用还不够。这样做:
object foos = Array.CreateInstance(typeof(Foo), 50);
太棒了。现在你手头有一个Foo[50]
,你可以使用反射再次填充它,或者使它dynamic
并使用动态运行时来调度数组索引;我假设您可以编写该代码。如果不能,再问一个问题。
现在您有一个填充的 Foo[]
,即 IEnumerable<Foo>
,然后您可以将其传递给 List<Foo>
构造函数。
你到底为什么要这样做——创建一个 Foo[]
来获得一个 IEnumerable<Foo>
来获得一个与 Foo[]
具有相同内容的 List<Foo>
- - 我不知道。这似乎 太多步骤 比制作一个 List<Foo>()
没有参数的构造函数并直接填充它更简单的步骤!但这就是你问的问题,所以这就是得到回答的问题。
这听起来很像我们所说的 "XY question"。你对如何解决你的问题有一些疯狂的想法,你问的是这个疯狂的想法而不是问问题,所以答案本身就有点疯狂。真正的问题是什么?
使用这种方法:
class Program
{
static void Main(string[] args)
{
CreateListFromType(typeof(Foo));
CreateListFromType(typeof(int));
}
static object CreateListFromType(Type t)
{
// Create an array of the required type
Array values = Array.CreateInstance(t, 50);
// and fill it with values of the required type
for (int i = 0; i < 50; i++)
{
values.SetValue(CreateFooFromType(t), i);
}
// Create a list of the required type, passing the values to the constructor
Type genericListType = typeof(List<>);
Type concreteListType = genericListType.MakeGenericType(t);
object list = Activator.CreateInstance(concreteListType, new object[] { values });
// DO something with list which is now an List<t> filled with 50 ts
return list;
}
// Create a value of the required type
static object CreateFooFromType(Type t)
{
return Activator.CreateInstance(t);
}
}
class Foo
{
public Foo() { }
}
在这种情况下不需要使用dynamic
。我们可以只使用 object
作为我们创造的价值。非引用类型将使用装箱存储在 object
中。
为了创建 List<>
类型,我们可以首先获得泛型类型的表示,然后使用它来使用 MakeGenericType
方法创建具体类型。
请注意您在创建列表的 CreateInstance 调用中犯的错误:
尝试构建列表时,您需要将值数组作为一个元素嵌入到对象数组中。因此 Activator
将在 List<t>
中寻找一个构造函数,该构造函数需要一个 IEnumerable<t>
.
按照您编写的方式,Activator
寻找一个需要 50 个参数的构造函数,每个参数都是 t 类型。
使用非通用 IList 接口的较短版本
using System.Collections;
static IList CreateListFromType(Type t)
{
// Create a list of the required type and cast to IList
Type genericListType = typeof(List<>);
Type concreteListType = genericListType.MakeGenericType(t);
IList list = Activator.CreateInstance(concreteListType) as IList;
// Add values
for (int i = 0; i < 50; i++)
{
list.Add(CreateFooFromType(t));
}
// DO something with list which is now an List<t> filled with 50 ts
return list;
}
更接近实际用例:动态列表类型
static void Main(string[] args)
{
CreateListFromType(typeof(List<Foo>));
CreateListFromType(typeof(ObservableCollection<int>));
}
static IList CreateListFromType(Type listType)
{
// Check we have a type that implements IList
Type iListType = typeof(IList);
if (!listType.GetInterfaces().Contains(iListType))
{
throw new ArgumentException("No IList", nameof(listType));
}
// Check we have a a generic type parameter and get it
Type elementType = listType.GenericTypeArguments.FirstOrDefault();
if (elementType == null)
{
throw new ArgumentException("No Element Type", nameof(listType));
}
// Create a list of the required type and cast to IList
IList list = Activator.CreateInstance(listType) as IList;
// Add values
for (int i = 0; i < 50; i++)
{
list.Add(CreateFooFromType(elementType));
}
// DO something with list which is now a filled object of type listType
return list;
}