2012-12-09 29 views
1

C#FieldInfo.SetValue我试图使用反射像这样设置的阵列字段:与阵列参数和任意元素类型

FieldInfo field = ... 
A[] someArray = GetElementsInSomeWay(); 
field.SetValue(this, someArray); 

的字段的类型B[]B继承自A,编译时不知道B的确切类型。 GetElementsInSomeWay()返回A[]但里面的真实元素都是B的。 GetElementsInSomeWay()是库方法,不能更改。

我最多只能做的是让BSystem.Type type = field.FieldType.GetElementType()。 但是我无法将阵列转换为所需的类型,例如 someArray as type[],因为[]在声明数组类型之前需要一个确切的类型。或者我在这里错过了什么?如果在运行时使用System.Type变量知道类型,我可以声明某种类型的数组吗?

做它直接的方式产生以下错误(这里AUnityEngine.ComponentBAbilityResult也可以是数十个其他类中的一个,所有继承(可能通长继承链)从UnityEngine.Component):

ArgumentException: Object type UnityEngine.Component[] cannot be converted to target type: AbilityResult[] 
Parameter name: val 
System.Reflection.MonoField.SetValue (System.Object obj, System.Object val, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Globalization.CultureInfo culture) (at /Applications/buildAgent/work/3df08680c6f85295/mcs/class/corlib/System.Reflection/MonoField.cs:133) 
System.Reflection.FieldInfo.SetValue (System.Object obj, System.Object value) (at /Applications/buildAgent/work/3df08680c6f85295/mcs/class/corlib/System.Reflection/FieldInfo.cs:150) 
+0

要么我不明白,要么这是不可能的。该字段不能是'B []'类型,其中'B'在编译时不知道。 –

回答

3

我想你需要更好地了解阵列方差。类似于object[]string[]的数组已经出现在.NET 1中,这是很久以前的泛型(.NET 2)。否则,他们可能被称为Array<object>Array<string>

现在,我们都知道,任何string是一个object。如果这个事实暗示,对于一些“建设”Xxx,任何Xxx<string>Xxx<object>,那么我们称之为covaraince。由于.NET 4中,一些通用的接口和泛型委托类型可以covarinat,这是标有其定义的out关键字,如

public interface IEnumerable<out T> 
{ 
    // ... 
} 

如果关系在扭转“应用” Xxx<>,那么这就是称为逆转。所以“stringobject”与变相变成“Xxx<object>Xxx<string>”。

现在回到数组。这里的自然问题是:数组是协变还是逆变,还是两者都不变(“不变”)?既然你可以读写数组中的每个“单元”,它们就不能完全协变或逆变。不过,试试这个代码:

string[] arr1 = { "these", "are", "strings", }; 
object[] arr2 = arr1;       // works! covariance! 
var runtimeType = arr2.GetType();    // System.String[] 

所以一个T[]是在.NET协变。但是,我不是说我可以写数组(如in而不是out)吗?所以,如果我继续这样下去什么:

arr2[0] = new object(); 

我试图把一个object这是一个string分成零插槽。上面的行必须编译(编译时类型arr2object[])。但它也必须提高运行时间。这是数组和协方差的问题。

那么反变换呢?试试这个:

object[] arrA = { new object(), DateTime.Now, "hello", }; 
string[] arrB = arrA; // won't compile, no cotravariance 

object[]进行明确的强制转换为string[]仍然无法正常工作运行。

现在,你的问题的答案:你正试图对数组应用逆变。 AbilityResultComponent,但是不是意味着Component[]AbilityResult[]。谁写了GetElementsInSomeWay()方法,选择创建new Component[]。即使他把它放入的所有组件都是AbilityResult,仍然没有反转。作者GetElementsInSomeWay()可能已经选择做一个new AbilityResult[]来代替。他仍然可以返回类型为.NET数组协方差的类型Component[]

的教训:一个数组的真正类型(所揭示的.GetType()运行时类型)不会改变,只是因为你投。 .NET确实允许协变(类型为Component[]的变量可能包含实际类型为AbilityResult[]的对象)。最后,.NET确实允许而不是允许反转(类型AbilityResult[]的变量永远不会持有对实际类型Component[]的对象的引用)。

希望这会有所帮助。否则,我的答案应该给你一些条件,你可以谷歌找到优于我的解释。

+0

谢谢,我想我理解了这个问题。从Component []到AbilityResult []的转换是不可能的,因为反转是禁止的,但是field.SetValue需要的字段的类型完全相同。这意味着可能没有别的办法,只能创建一个所需类型AbilityResult []的新数组并将所有元素复制到它(这将从第一个Component []数组开始保存AbilityResult对象开始工作) – iseeall

1

经过一番搜索我无意中发现了这个问题:How do I create a C# array using Reflection and only type info?

一个可能的解决我的问题是:

A[] someArray = GetElementsInSomeWay(); 
System.Type type = field.FieldType.GetElementType(); 
Array filledArray = Array.CreateInstance(type, someArray.Length); 
Array.Copy(someArray, filledArray, someArray.Length); 
field.SetValue(this, filledArray); 

我刚刚测试过,它的工作原理。

不过,我还是想避免复制的元素。在我的情况下,阵列非常小(最多3-5个元素),但如果有更清晰的解决方案,将会很高兴。