2008-10-17 17 views
10

Szenario:Kann Objekt vom Typ 'System.Object []' nicht in 'MyObject []' umgewandelt werden, was gibt es?

Ich schreibe gerade eine Schicht, um 3 ähnliche Webservices in eine verwendbare Klasse zu abstrahieren. Jeder Webservice macht eine Reihe von Objekten verfügbar, die Gemeinsamkeiten aufweisen. Ich habe eine Reihe von Zwischenobjekten geschaffen, die die Gemeinsamkeit ausnutzen. In meinem Layer muss ich jedoch zwischen den Web-Service-Objekten und meinen Objekten konvertieren.

Ich habe Reflexion verwendet, um den geeigneten Typen zur Laufzeit zu erstellen, bevor ich den Anruf an den Web-Service zu machen, wie so:

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

Hier ist meine Berufung Code, von einem meiner Service-Implementierungen:

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

So stellt jeder Dienst ein anderes "Property" -Objekt aus, das ich hinter meiner eigenen Implementierung meiner IProperty-Schnittstelle versteckt habe.

Der Reflexionscode funktioniert in Komponententests und erzeugt ein Array von Objekten, deren Elemente den entsprechenden Typ aufweisen. Aber der aufrufende Code fehlschlägt:

System.InvalidCastException: Kann nicht Gussobjekt des Typs 'System.Object []' eingeben ‚MyProject.Property []

Irgendwelche Ideen?

Ich hatte den Eindruck, dass jede Umwandlung von Object funktioniert, solange das enthaltene Objekt konvertierbar ist?

Antwort

9

Alternative Antwort: Generika.

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

Dann wird Ihr Telefonvorwahl wird:

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

Viele sauber :)

+0

Nun, ich habe gesagt "In C# 2.0 und höher, Generika machen in der Regel eine bessere Option als Array Kovarianz.". Ich fürchte, ich musste meine +1 in Ihrem letzten Beitrag annullieren, um +1 zu geben ... Ich bin für heute out ... –

+0

Wohl, es gibt etwas ein wenig zwielichtig für die Länge == 0 Fall - I würde ein leeres Array selbst zurückgeben ... –

+0

Bearbeitet, um Anklage zu entfernen :) Und ja, die Rückgabe von null ist möglicherweise ein bisschen zweifelhaft - aber das ist eine Frage für das OP zu entscheiden :) –

4

Sie können ein Array nicht so konvertieren - es gibt ein Array von Objekten zurück, das sich von einem Objekt unterscheidet. Versuchen Sie Array.ConvertAll

1

Das ist richtig, aber das bedeutet nicht, dass Sie Container des Typs Objekt auf Container anderer Typen umwandeln können. Ein Objekt [] ist nicht dasselbe wie ein Objekt (obwohl Sie seltsamerweise Objekt [] auf Objekt anwenden könnten).

+0

Nun, um pedantisch zu sein, und Objekt [] * ist * ein Objekt ... also ist die Besetzung nicht so seltsam. Alles ist ein Objekt in .NET –

9

Grundsätzlich keine. Es gibt ein paar begrenzte Anwendungen der Array-Kovarianz, aber es ist besser, einfach zu wissen, welche Art von Array Sie wollen. Es ist eine allgemeine Array.ConvertAll, die einfach genug ist (zumindest ist es einfacher, mit C# 3.0):

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

Der C# 2.0-Version (identisch in ihrer Bedeutung) ist viel weniger Augapfel freundlich:

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

Oder erstellen Sie einfach eine neue Eigenschaft [] der richtigen Größe und kopieren Sie sie manuell (oder über Array.Copy).

Als ein Beispiel für das, was Sie mit Matrixkovarianz tun können:

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

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

Hier "asObj" ist noch ein Property[] - es es einfach zugänglich wie object[]. In C# 2.0 und höher machen Generika normalerweise eine bessere Option als die Array-Kovarianz.

+0

Danke, das funktioniert. Was ist der Leistungseinfluss von Convertall gegenüber Kopie? –

+0

Bei ConvertAll wird jedes Mal ein Delegat aufgerufen, während Copy einfach Buffer.BlockCopy unter dem Rahmen verwenden soll. Also sollte Kopieren schneller sein. ConvertAll ist einfacher zu tippen (und gut für kleine bis mittlere Größen) und flexibler: Sie können nicht-triviale Projektionen durchführen. –

+0

Auch - ConvertAll wird mit Konvertierungen und nicht mit Umwandlungen umgehen; Array.Copy kann dies nicht tun, da es keine Garantie gibt, dass die Original- und Zielobjekte dieselbe Größe haben (was für Buffer.BlockCopy notwendig ist) –

5

Wie andere schon gesagt haben, muss das Array vom richtigen Typ sein, um damit zu beginnen.Die anderen Antworten haben gezeigt, wie ein echtes Objekt [] nach der Tat konvertieren, aber man kann die richtige Art von Array erstellen mit der Verwendung von Array.CreateInstance zu starten:

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

Unter der Annahme, type ist ein Referenztyp, sollte diese Arbeit - Das Array wird vom richtigen Typ sein, aber Sie werden es als object[] nur verwenden, um es zu füllen.

+0

So oft falle ich mit Werttypen in diese Falle, dann erinnere ich mich:) – leppie

+0

Tolle Infos! Danke, StackOverflow benötigt Antworten auf die Fragen. Es wäre nett, den Typ zu kennen, aber ich versuche, so wenig Code wie möglich zu schreiben. Auf diese Weise kann ich später weitere Dienste hinzufügen, ohne den Mapping-Code erneut schreiben zu müssen. Ich müsste 3 Loops in 3 Methoden schreiben. –

+0

Rob: Du gibst bereits den Typ als Parameter für die Methode ein, nicht wahr? Habe ich etwas falsch verstanden? –

1

in C# 2.0 Sie kann tun dies mit Reflexion, ohne die Verwendung von Generika und ohne die gewünschte Art zu wissen, an Kompilierzeit

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

Sie wollen würden alle Type.GetType („Output“) nennt und die GetProperty („Propertyname“) mit einigem Code ersetzen, die aus einer Konfigurationsdatei liest.

Ich würde auch eine generische verwenden, um die Schaffung von SomeComplexObject

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

zu diktieren Aber ich sagte Ihnen nicht Generika verwenden mussten.

Keine Garantie für Leistung/Effizienz, aber es funktioniert.

Verwandte Themen