Impossibile trasmettere l'oggetto del tipo "System.Object []" su "MyObject []", cosa offre?
-
03-07-2019 - |
Domanda
Scenario:
Attualmente sto scrivendo un livello per astrarre 3 servizi Web simili in una classe utilizzabile. Ogni servizio web espone un insieme di oggetti che condividono la comunanza. Ho creato un insieme di oggetti intermedi che sfruttano la comunanza. Tuttavia nel mio livello devo convertire tra gli oggetti del servizio Web e i miei oggetti.
Ho usato la riflessione per creare il tipo appropriato in fase di esecuzione prima di effettuare la chiamata al servizio Web in questo modo:
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;
}
Ecco il mio codice chiamante, da una delle mie implementazioni di servizio:
Property[] props = (Property[])ObjectFactory.CreateProperties(typeof(Property), properties);
_service.SetProperties(folderItem.Path, props);
Quindi ogni servizio espone una diversa "Proprietà" oggetto che nascondo dietro la mia implementazione della mia interfaccia IProperty.
Il codice di riflessione funziona in unit test producendo una matrice di oggetti i cui elementi sono del tipo appropriato. Ma il codice chiamante fallisce:
System.InvalidCastException: impossibile oggetto cast di tipo 'System.Object []' digitare 'MyProject.Property []
Qualche idea?
Ho avuto l'impressione che qualsiasi cast di Object funzionerà finché l'oggetto contenuto è convertibile?
Soluzione
Risposta alternativa: generici.
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;
}
Quindi il tuo codice chiamante diventa:
Property[] props = ObjectFactory.CreateProperties<Property>(properties);
_service.SetProperties(folderItem.Path, props);
Molto più pulito :)
Altri suggerimenti
In sostanza, no. Esistono pochi, limitati usi della covarianza dell'array, ma è meglio semplicemente sapere quale tipo di array si desidera. Esiste un Array.ConvertAll generico che è abbastanza semplice (almeno, è più facile con C # 3.0):
Property[] props = Array.ConvertAll(source, prop => (Property)prop);
La versione C # 2.0 (identica nel significato) è molto meno compatibile con il bulbo oculare:
Property[] props = Array.ConvertAll<object,Property>(
source, delegate(object prop) { return (Property)prop; });
O semplicemente crea una nuova proprietà [] della giusta dimensione e copia manualmente (o tramite Array.Copy
).
Come esempio delle cose che puoi fare con la covarianza dell'array:
Property[] props = new Property[2];
props[0] = new Property();
props[1] = new Property();
object[] asObj = (object[])props;
Qui, " asObj " è ancora una proprietà []
- è semplicemente accessibile come oggetto []
. In C # 2.0 e versioni successive, i generici di solito fanno un'opzione migliore della covarianza dell'array.
Come altri hanno già detto, l'array deve essere del tipo giusto per cominciare. Le altre risposte hanno mostrato come convertire un oggetto autentico [] dopo il fatto, ma è possibile creare il giusto tipo di array per iniziare utilizzando Array.CreateInstance :
object[] result = (object[]) Array.CreateInstance(type, properties.Length);
Supponendo che type
sia un tipo di riferimento, dovrebbe funzionare: l'array sarà del tipo corretto, ma lo userai come oggetto []
solo per popolarlo.
Non puoi convertire un array come quello: restituisce un array di oggetti, che è diverso da un oggetto. Prova Array.ConvertAll
È corretto, ma ciò non significa che puoi lanciare contenitori di tipo Object su contenitori di altri tipi. Un oggetto [] non è la stessa cosa di un oggetto (sebbene tu, stranamente, potresti lanciare Object [] su Object).
in C # 2.0 puoi puoi farlo usando la riflessione, senza usare generici e senza conoscere il tipo desiderato al momento della compilazione.
//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);
}
}
Dovresti sostituire tutte le chiamate Type.GetType (" OutputData ") e GetProperty (" PropertyName ") con del codice che legge da un file di configurazione.
Vorrei anche usare un generico per dettare la creazione di SomeComplexObject
T result = new T();
Type resultTypeRef = result.GetType();
Ma ho detto che non hai bisogno di usare generici.
Nessuna garanzia su prestazioni / efficienza, ma funziona.