タイプ 'System.Object []'のオブジェクトを 'MyObject []'にキャストできません。
-
03-07-2019 - |
質問
シナリオ:
現在、3つの同様のWebサービスを1つの使用可能なクラスに抽象化するレイヤーを作成しています。各Webサービスは、共通性を共有するオブジェクトのセットを公開します。共通性を活用する一連の中間オブジェクトを作成しました。ただし、私のレイヤーでは、Webサービスオブジェクトとオブジェクトの間で変換する必要があります。
次のようにWebサービスを呼び出す前に、実行時に適切なタイプを作成するためにリフレクションを使用しました:
public static object[] CreateProperties(Type type, IProperty[] properties)
{
//Empty so return null
if (properties==null || properties.Length == 0)
return null;
//Check the type is allowed
CheckPropertyTypes("CreateProperties(Type,IProperty[])",type);
//Convert the array of intermediary IProperty objects into
// the passed service type e.g. Service1.Property
object[] result = new object[properties.Length];
for (int i = 0; i < properties.Length; i++)
{
IProperty fromProp = properties[i];
object toProp = ReflectionUtility.CreateInstance(type, null);
ServiceUtils.CopyProperties(fromProp, toProp);
result[i] = toProp;
}
return result;
}
サービス実装の1つからの呼び出しコードは次のとおりです。
Property[] props = (Property[])ObjectFactory.CreateProperties(typeof(Property), properties);
_service.SetProperties(folderItem.Path, props);
したがって、各サービスは異なる&quot; Property&quot;を公開します。 IPropertyインターフェイスの独自の実装の背後に隠すオブジェクト。
リフレクションコードは、適切な型の要素を持つオブジェクトの配列を生成する単体テストで機能します。しかし、呼び出しコードは失敗します:
System.InvalidCastException:できません 'System.Object []'型のキャストオブジェクト 入力し 'MyProject.Property []
アイデアはありますか
含まれているオブジェクトが変換可能である限り、Objectからのキャストは動作するという印象を受けましたか?
解決
別の答え:ジェネリック。
public static T[] CreateProperties<T>(IProperty[] properties)
where T : class, new()
{
//Empty so return null
if (properties==null || properties.Length == 0)
return null;
//Check the type is allowed
CheckPropertyTypes("CreateProperties(Type,IProperty[])",typeof(T));
//Convert the array of intermediary IProperty objects into
// the passed service type e.g. Service1.Property
T[] result = new T[properties.Length];
for (int i = 0; i < properties.Length; i++)
{
T[i] = new T();
ServiceUtils.CopyProperties(properties[i], t[i]);
}
return result;
}
その後、呼び出しコードは次のようになります。
Property[] props = ObjectFactory.CreateProperties<Property>(properties);
_service.SetProperties(folderItem.Path, props);
かなりクリーナー:)
他のヒント
基本的に、いいえ。配列共分散の使用法はいくつかありますが、限られていますが、どのタイプの配列が必要かを単純に知る方が良いでしょう。十分に簡単な汎用のArray.ConvertAllがあります(少なくとも、C#3.0では簡単です):
Property[] props = Array.ConvertAll(source, prop => (Property)prop);
C#2.0バージョン(意味が同じ)は、眼球にやさしくない:
Property[] props = Array.ConvertAll<object,Property>(
source, delegate(object prop) { return (Property)prop; });
または適切なサイズの新しいProperty []を作成し、手動で(または Array.Copy
を介して)コピーします。
配列共分散を使用してできることの例:
Property[] props = new Property[2];
props[0] = new Property();
props[1] = new Property();
object[] asObj = (object[])props;
ここでは、「asObj」 still は Property []
です- object []
として簡単にアクセスできます。 C#2.0以降では、ジェネリックは通常、配列共分散よりも優れたオプションになります。
他の人が言ったように、配列は最初から正しい型でなければなりません。他の回答では、事後の本物のオブジェクト[]の変換方法を示しましたが、 Array.CreateInstance :
object[] result = (object[]) Array.CreateInstance(type, properties.Length);
type
が参照型であると仮定すると、これは動作するはずです-配列は正しい型になりますが、 object []
としてそれを使用するだけですデータを入力します。
そのような配列は変換できません-オブジェクトとは異なるオブジェクトの配列を返します。 Array.ConvertAll
を試してください。それは正しいですが、Object型のコンテナーを他の型のコンテナーにキャストできるという意味ではありません。 Object []はObjectと同じものではありません(ただし、奇妙なことに、Object []をObjectにキャストできます)。
C#2.0では、ジェネリックを使用せず、コンパイル時に目的の型を知らなくても、リフレクションを使用してこれを できます。
//get the data from the object factory
object[] newDataArray = AppObjectFactory.BuildInstances(Type.GetType("OutputData"));
if (newDataArray != null)
{
SomeComplexObject result = new SomeComplexObject();
//find the source
Type resultTypeRef = result.GetType();
//get a reference to the property
PropertyInfo pi = resultTypeRef.GetProperty("TargetPropertyName");
if (pi != null)
{
//create an array of the correct type with the correct number of items
pi.SetValue(result, Array.CreateInstance(Type.GetType("OutputData"), newDataArray.Length), null);
//copy the data and leverage Array.Copy's built in type casting
Array.Copy(newDataArray, pi.GetValue(result, null) as Array, newDataArray.Length);
}
}
すべてのType.GetType(&quot; OutputData&quot;)呼び出しとGetProperty(&quot; PropertyName&quot;)を、構成ファイルから読み取るコードに置き換えます。
SomeComplexObjectの作成を指示するためにジェネリックも使用します
T result = new T();
Type resultTypeRef = result.GetType();
しかし、ジェネリックを使用する必要はないと言いました。
パフォーマンス/効率に関する保証はありませんが、機能します。