2

我正在尝试使用反射设置一个数组字段,如下所示:

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

该字段具有类型B[]B继承自,并且在编译时不知道 A确切的类型。返回,但里面的真正元素都是's. 是一种库方法,不能更改。BGetElementsInSomeWay()A[]BGetElementsInSomeWay()

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

以直接方式执行此操作会产生以下错误(这里AisUnityEngine.ComponentBisAbilityResult也可以是其他几十个类之一,都继承自(可能通过长继承链)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)
4

2 回答 2

3

我认为您需要更好地了解数组的方差。数组和.NET 1 一样object[]string[]早在泛型(.NET 2)之前就已经出现了。否则他们可能被称为Array<object>and Array<string>

现在,我们都知道 anystringobject. 如果这个事实意味着,对于某些“构造” Xxx, anyXxx<string>是一个Xxx<object>,那么我们称之为covariance。从 .NET 4 开始,一些泛型接口和泛型委托类型可以是协变的,这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[]

所以 aT[]在 .NET 中是协变的。但是我不是说我可以写(比如innot out)到一个数组吗?那么如果我继续这样呢:

arr2[0] = new object();

我正在尝试将不是objecta放入第零个插槽。上面的行必须编译(编译时类型为is )。但它也必须提高运行时间。这就是数组和协方差的问题。stringarr2object[]

那么逆变呢?试试这个:

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

object[]从to进行显式转换string[]仍然无法在运行时工作。

现在,您的问题的答案是:您正在尝试对数组应用逆变。AbilityResultComponent,但这并不意味着Component[]AbilityResult[]。编写该GetElementsInSomeWay()方法的人选择创建一个new Component[]. 即使他放入其中AbilityResult的所有组件都是 ,仍然没有逆变。作者本GetElementsInSomeWay() 可以选择制作一个new AbilityResult[]Component[]由于 .NET 数组的协方差,他仍然可以有返回类型。

经验教训:数组的真实类型(由 显示的运行时类型)不会仅仅因为您进行转换而改变。.GetType().NET 确实允许协变(类型的变量Component[]可能包含实际类型为 的对象AbilityResult[])。最后,.NET不允许逆变(类型变量AbilityResult[]从不包含对真实类型对象的引用Component[])。

希望这可以帮助。否则,我的回答应该给你一些术语,你可以在谷歌上找到优于我的解释。

于 2012-12-09T20:42:26.893 回答
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 个元素),但如果有一个更清洁的解决方案,仍然会很高兴。

于 2012-12-09T19:14:59.270 回答