2008-10-17 9 views
10

Escenario:No se puede lanzar el objeto de tipo 'System.Object []' a 'MyObject []', ¿qué ofrece?

Actualmente estoy escribiendo una capa para abstraer 3 servicios web similares en una clase utilizable. Cada servicio web expone un conjunto de objetos que comparten características comunes. He creado un conjunto de objetos intermedios que explotan lo que es común. Sin embargo, en mi capa, necesito convertir los objetos del servicio web a mis objetos.

He usado la reflexión para crear el tipo adecuado en tiempo de ejecución antes de hacer la llamada al servicio web de esta manera:

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; 
    } 

Aquí está mi código de llamada, de uno de mis implementaciones de servicios:

Property[] props = (Property[])ObjectFactory.CreateProperties(typeof(Property), properties); 
_service.SetProperties(folderItem.Path, props); 

De modo que cada servicio expone un objeto "Propiedad" diferente que oculto detrás de mi propia implementación de mi interfaz IProperty.

El código de reflexión funciona en pruebas unitarias produciendo una matriz de objetos cuyos elementos son del tipo apropiado. Sin embargo, el código de llamada falla:

System.InvalidCastException: No se puede fundido objeto de tipo 'System.Object []' para escribir 'MyProject.Property []

¿Alguna idea?

¿Estaba bajo la impresión de que cualquier yeso de Object funcionará mientras el objeto contenido sea convertible?

Respuesta

9

Respuesta alternativa: generics.

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; 
} 

A continuación, el código de llamada se convierte en:

Property[] props = ObjectFactory.CreateProperties<Property>(properties); 
_service.SetProperties(folderItem.Path, props); 

Mucho más limpio :)

+0

Bueno, dije "En C# 2.0 y superior, los genéricos suelen ser una mejor opción que la matriz de covarianzas". Me temo que tuve que cancelar mi +1 en su última publicación en +1 esto ... Estoy fuera por hoy ... –

+0

Podría decirse que devuelve algo un poco dudoso para el caso Length == 0 - I Devolvería una matriz vacía yo mismo ... –

+0

Editado para eliminar la acusación :) Y sí, devolver nulo es posiblemente un poco dudoso, pero esa es una cuestión que el OP debe decidir :) –

4

No se puede convertir una matriz como esa: devolver una matriz de objetos, que es diferente de un objeto. Pruebe Array.ConvertAll

1

Eso es correcto, pero eso no significa que pueda enviar contenedores de tipo Object a contenedores de otros tipos. Un Objeto [] no es lo mismo que un Objeto (aunque, curiosamente, usted podría lanzar Objeto [] a Objeto).

+0

Bueno, ser pedante, y Object [] * es * un Objeto ... por lo que el molde no es tan extraño. Todo es un objeto en .NET –

9

Básicamente, no. Existen algunos usos limitados de la covarianza de matriz, pero es mejor simplemente saber qué tipo de matriz desea. Hay una Array.ConvertAll genérico que es bastante fácil (por lo menos, es más fácil con C# 3.0):

Property[] props = Array.ConvertAll(source, prop => (Property)prop); 

La versión de C# 2.0 (idéntico en significado) es mucho menos globo ocular-amigable:

Property[] props = Array.ConvertAll<object,Property>(
    source, delegate(object prop) { return (Property)prop; }); 

O simplemente crea una nueva Propiedad [] del tamaño correcto y copia manualmente (o a través de Array.Copy).

Como un ejemplo de las cosas que puede hacer con matriz de covarianza:

Property[] props = new Property[2]; 
props[0] = new Property(); 
props[1] = new Property(); 

object[] asObj = (object[])props; 

Aquí, "asObj" es todavía un Property[] - es simplemente accesible como object[]. En C# 2.0 y superior, los genéricos suelen ser una mejor opción que la matriz de covarianza.

+0

Gracias, esto funciona. ¿Cuál es el impacto en el rendimiento de convertall vs. copy? –

+0

ConvertAll involucrará una invocación de delegado cada vez, donde, como Copy, solo debería usar Buffer.BlockCopy bajo el aro. Entonces la copia debería ser más rápida. ConvertAll es más fácil de escribir (y fino para tamaños pequeños a medianos), y más flexible: puede hacer proyecciones no triviales. –

+0

Además, ConvertAll lidiará con las conversiones en lugar de conversiones; Array.Copy no puede hacer esto, ya que no hay garantía de que los objetos originales y de destino sean del mismo tamaño (lo cual es necesario para Buffer.BlockCopy) –

5

Como han dicho otros, la matriz tiene que ser del tipo correcto para empezar.Las otras respuestas han demostrado cómo convertir un objeto genuino [] después de los hechos, pero se puede crear el tipo correcto de la matriz para comenzar con el uso de Array.CreateInstance:

object[] result = (object[]) Array.CreateInstance(type, properties.Length); 

Suponiendo type es un tipo de referencia, esto debería funcionar - la matriz será del tipo correcto, pero la usará como object[] solo para rellenarla.

+0

Tantas veces simplemente caigo en esta trampa con tipos de valores, entonces recuerdo:) – leppie

+0

¡Excelente información! gracias, StackOverflow necesita respuestas asistidas. Sería bueno saber el tipo, pero estoy tratando de escribir la menor cantidad de código posible. De esta forma puedo agregar más servicios más tarde sin tener que volver a escribir el código de mapeo. Tendría que escribir 3 bucles en 3 métodos actualmente. –

+0

Rob: Ya está pasando el tipo como un parámetro para el método, ¿verdad? ¿He entendido mal algo? –

1

en C# 2.0 se puede hacer esto utilizando la reflexión, sin el uso de medicamentos genéricos y sin saber el tipo deseado en tiempo de compilación.

//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); 
    } 
} 

Usted quiere reemplazar todos los Type.GetType ("datosSalida") llama y el GetProperty ("PropertyName") con un código que se lee de un archivo de configuración.

También podría usar un genérico para dictar la creación de SomeComplexObject

T result = new T(); 
Type resultTypeRef = result.GetType(); 

Pero me dijo que no era necesario utilizar los genéricos.

Sin garantías de rendimiento/eficiencia, pero funciona.

Cuestiones relacionadas