2

次のようなリフレクションを使用して配列フィールドを設定しようとしています:

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

フィールドのタイプはB[]です。Bから継承されA、の正確な型はBコンパイル時にわかりません。 GetElementsInSomeWay()を返しますA[]が、内部の実際の要素はすべてBです。GetElementsInSomeWay()はライブラリ メソッドであり、変更できません。

私ができることは、せいぜいBwithを取得することSystem.Type type = field.FieldType.GetElementType()です。ただし、配列型を宣言する前に正確な型が必要なsomeArray as type[]ため、配列を必要な型にキャストできません 。[]それとも、ここで何か不足していますか? System.Type型が変数を使用して実行時に認識されるようになった場合、その型の配列を宣言できますか?

直接の方法で実行すると、次のエラーが発生します (ここにAある isUnityEngine.ComponentBis はAbilityResult、数十個の他のクラスの 1 つである場合もあり、すべてが から (おそらく長い継承チェーンを介して) 継承されます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

配列の分散をよりよく理解する必要があると思います。配列はobject[]string[]ジェネリック (.NET 2) よりもずっと前に、.NET 1 で既に使用されていました。そうでなければ、 and と呼ばれていた可能性がArray<object>ありArray<string>ます。

これで、 anystringobject. この事実が、ある「構造」Xxxに対して anyXxx<string>が であることを意味する場合、これを共分散Xxx<object>と呼びます。.NET 4 以降、一部のジェネリック インターフェイスとジェネリック デリゲート型は covarinat になる可能性があり、これは次のように定義内でキーワードでマークされています。out

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

「適用」時に関係が逆転する場合Xxx<>、それは反変性と呼ばれます。したがって、" stringis object" で反変性は " Xxx<object>is Xxx<string>" になります。

さて、配列に戻ります。ここでの自然な疑問は次のとおりです: 配列は共変ですか、反変ですか、それともどちらでもない (「不変」) ですか? 配列内の各「セル」からの読み取りと書き込みの両方ができるため、完全に共変または反変になることはありません。しかし、このコードを試してください:

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

したがって、T[].NET では a は共変です。しかし、私は配列に書き込むことができると言っただけではありませんinか? outでは、次のように続けるとどうなるでしょうか。

arr2[0] = new object();

aobjectではないstringan を0 番目のスロットに入れようとしています。上記の行はコンパイルする必要があります (コンパイル時の型はarr2ですobject[])。ただし、例外ランタイムも発生させる必要があります。それが配列と共分散の問題です。

では、反変性はどうでしょうか。これを試して:

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

object[]from からto への明示的なキャストを行ってstring[]も、実行時は機能しません。

そして今、あなたの質問への答え:配列に反変性を適用しようとしています。AbilityResultですがComponent、それはそれが意味するものではありませ。メソッドを作成した人は誰でも、 . 彼が入れたすべてのコンポーネントがであっても、反変性はありません。の作成者は、代わりに を作成することを選択できた可能性があります。.NET 配列の共分散のために、彼はまだ戻り値の型を持つことができました。Component[]AbilityResult[]GetElementsInSomeWay()new Component[]AbilityResultGetElementsInSomeWay() new AbilityResult[]Component[]

学ぶべき教訓: 配列の実際の型 ( によって明らかにされる実行時の型.GetType()) は、キャストしたからといって変更されるわけではありません。.NET は共分散を許可します (型の変数はComponent[]、実際の型が であるオブジェクトを保持する場合がありますAbilityResult[])。最後に、.NET は反変性を許可しませ(型の変数はAbilityResult[]、実数型のオブジェクトへの参照を決して保持しませんComponent[])。

お役に立てれば。それ以外の場合、私の答えは、私の説明よりも優れた説明を見つけるためにグーグルで検索できるいくつかの用語を提供するはずです。

于 2012-12-09T20:42:26.893 に答える
1

いくつかの検索の後、私はこの質問に出くわしました:リフレクションを使用してC#配列を作成し、情報のみを入力するにはどうすればよいですか?

私の問題に対する可能な解決策は次のとおりです。

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 に答える