我正在使用Activator.CreateInstance
通过类型变量(在运行时未知)创建对象:
static dynamic CreateFoo( Type t ) =>
Activator.CreateInstance( t );
很显然,我还没有正确理解如何使用dynamic
类型,因为这仍然只是返回一个对象。
我需要能够将集合传递给另一个对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
似乎只会返回一个对象,这对List<Foo>
很好,因为我将使用Dependency Objects和Properties对其进行设置,因此,装箱的对象对它们而言完全合适,但是在尝试时会破坏所有内容创建列表(可能还有其他构造函数需要类型参数的东西)。
什么是我要在这里完成的正确方法?
符合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( ) { }
}
}
答案 0 :(得分:6)
很显然,我还没有正确理解如何使用动态类型,因为它仍然只是返回一个对象。
是的,您不理解。 dynamic
只不过是戴着花哨帽子的对象。返回类型为dynamic
的意思是“我返回了object
,但是调用者应允许对返回的对象执行通常在object
上不允许的操作,并且这些操作将在运行时解析”
如果这不是您的意思,那么请勿使用dynamic
。
我知道为什么这样做-当使用类型实参定义列表时,列表的构造函数没有可枚举的对象。
正确。在您的代码中:
var values = Enumerable.Range( 1, 50 ).Select(
I => CreateFoo( typeof( Foo ) ) ).ToArray( );
values
是dynamic[]
,它又是object[]
,上面戴着滑稽的帽子。因此,您正在使用List<Foo>
而不是需要的object[]
来调用IEnumerable<Foo>
的构造函数。
什么是我要在这里完成的正确方法?
正确的 要做的是停止使用静态类型的语言来进行动态类型的工作,但我怀疑这将是一个不起眼的事情。
您手中有一个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>
以获得List<Foo>
,其内容与Foo[]
相同-我不知道。在制作List<Foo>()
且不向构造函数添加任何参数并直接填充它的简单步骤中,这似乎太多步骤。但这是您提出的问题,所以这就是已回答的问题。
这听起来很像我们所说的“ XY问题”。您对如何解决问题有一些疯狂的想法,而您在问的是疯狂的想法,而不是在问问题,因此答案本身就有点疯狂。真正的问题是什么?
答案 1 :(得分:2)
使用这种方法:
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
方法使用泛型类型来创建具体类型。
在尝试构建列表时,您需要将values数组作为元素嵌入到对象数组中。这样Activator
会在List<t>
中寻找一个构造器,该构造器需要一个类型为IEnumerable<t>
的单个参数。
按照您编写它的方式,Activator
寻找一个构造函数,该构造函数需要50个参数,每个参数的类型均为t。
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;
}