2013-06-14 12 views
27

Se si dispone di un oggetto dinamico o di un oggetto anonimo, la cui struttura corrisponde esattamente a quella di un oggetto fortemente tipizzato, esiste un metodo .NET per creare un oggetto digitato dall'oggetto dinamico?Esiste un modo per convertire un oggetto dinamico o anonimo in un oggetto dichiarato e fortemente digitato?

So che posso usare una cosa di tipo LINQ dynamicList.Select(dynamic => new Typed { .... } oppure posso usare Automapper, ma mi chiedo se non esiste qualcosa appositamente creato per questo?

risposta

35

Si potrebbe serializzare in un formato intermedio, solo per deserializzare bene da allora in poi . Non è il modo più elegante ed efficiente, ma potrebbe ottenere il vostro lavoro fatto:

Supponiamo questa è la tua classe:

// Typed definition 
class C 
{ 
    public string A; 
    public int B; 
} 

e questa è l'istanza anonima:

// Untyped instance 
var anonymous = new { 
    A = "Some text", 
    B = 666 
}; 

Puoi serializza la versione anonima in un formato intermedio e poi deserializza nuovamente in una versione tipizzata.

var serializer = new System.Web.Script.Serialization.JavaScriptSerializer(); 
var json = serializer.Serialize(anonymous); 
var c = serializer.Deserialize<C>(json); 

noti che questo è in teoria possibile con qualsiasi serializzatore/deserializzatore (XmlSerializer, serializzazione binaria, altre librerie JSON), finché l'andata e ritorno è sincrona.

+0

+1. Bel pensiero, anche se utilizza la riflessione dietro le quinte. – Tengiz

+2

Ho pensato un po ': immagino che avrai sempre una rappresentazione intermedia quando faresti questo e avresti sempre una riflessione su entrambi i lati (oggetto anonimo e dattilografato). Quindi suppongo che per ottimizzare questo, hai solo bisogno di trovare il modulo intermedio più efficiente: http://www.servicestack.net/benchmarks/ –

1

Se il tuo oggetto eredita da MarshalByRefObject puoi usare RealProxy.

Un'altra alternativa è usare Reflection ma si può essere limitati da elementi non contrassegnati come virtuali e/o che devono utilizzare le interfacce. Puoi anche fare in modo che copi i valori, assumendo che le proprietà siano scrivibili e che il costruttore vuoto funzioni per il tuo caso.

La risposta effettiva alla domanda è no, non esiste un modo automatico per trattare un oggetto dinamico come un tipo specifico a meno che non si tratti di un'istanza di quel tipo, né esiste una funzionalità automatica per copiare i valori da una dinamica/oggetto anonimo in un'istanza di una classe denominata.

Il runtime non ha idea di cosa stia succedendo nel costruttore o di come la classe sia implementata internamente, quindi qualsiasi impianto di questo tipo distruggerebbe la sicurezza del tipo fuori dall'acqua. L'intero punto di dinamica è quello di consentire la digitazione anatra/dispatch runtime/ecc.

modifica: Se ho frainteso la domanda fammelo sapere, ma sto assumendo che tu voglia trattare un oggetto dinamico come se fosse un'istanza di SomeType.

Nei miei progetti, ho usato una classe Object Mapper per questo dove associa i nomi delle proprietà per le proprietà scrivibili e i tipi identici o coercibili, quindi almeno non ho dovuto scrivere 10.000 righe di boilerplate, sebbene la mia fonte non era dinamica, quindi ho usato Reflection.Emit/DynamicMethod per velocizzarlo notevolmente.

+0

Mi hai leggermente frainteso. Non voglio trattare automaticamente un oggetto dinamico come un oggetto tipizzato staticamente. Voglio convertire un oggetto dinamico in un oggetto tipizzato staticamente. I casi d'uso saranno probabilmente principalmente per DTO dinamici e simili, quindi la logica del costruttore non è un fattore di rischio tanto elevato come potrebbe essere. – ProfK

+0

Ok, mi dispiace per quello; non esiste un modo automatico per farlo. La soluzione di jherax funziona ovviamente per convertire in un dizionario, ma la riflessione sarà lenta. Nel mio scenario, ho creato un ObjectMapper che memorizza nella cache un DynamicMethod (costruito utilizzando IL) emesso che copia tra i tipi in modo che sia estremamente veloce dopo il primo utilizzo, ma abbiamo un sacco di tipi di DTO. Per un oggetto dinamico che non funzionerà, anche se ci sono alcuni potenziali aumenti se ne hai bisogno. – russbishop

10

La tua domanda si riduce alla domanda: posso convertire una staticamente tipizzato variabile in un altro staticamente tipizzato variabile (da diverse catene di ereditarietà)? e la risposta, ovviamente, è n..

Perché la tua domanda arriva alla domanda precedente?

  • tipo dinamico Utilizzo compila nel usando l'oggetto con la riflessione.
  • Il codice riceve oggetto in realtà, che (in realtà) contiene il valore di un particolare tipo statico.

Quindi, in realtà, il codice ha a che fare con il valore tipizzato staticamente, assegnato all'oggetto e trattato come dinamico al momento della compilazione. Pertanto, l'unico caso in cui è possibile convertire un valore tipizzato staticamente in un altro [senza riflessione] è quando fanno parte della stessa catena di ereditarietà. Altrimenti, si è obbligati a usare la riflessione esplicitamente (scritta da voi) o implicitamente (scritta da MS con utilizzo dinamico).

In altre parole, il seguente codice funziona solo se il valore assegnato alla dinamica è una base o tipo derivato della Persona (inventato per l'esempio):

dynamic dPerson = GetDynamicPerson(); 
Person thePerson = (Person)dPerson; 

che è praticamente esso.

Va detto, è possibile copiare i valori byte per byte con codice non sicuro che accede agli indirizzi di memoria (come in C++), ma che per me non è altro che la riflessione.

+1

Ma molto più avventuroso del riflesso :-) – ProfK

+0

Grazie per aver chiarito come funziona sotto il cofano - buona spiegazione! – infl3x

3

Qui possiamo convertire un oggetto anonimo al dizionario

Dictionary<string, object> dict = 
    obj.GetType() 
     .GetProperties() 
     .ToDictionary(p => p.Name, p => p.GetValue(obj, null)); 

Inoltre è possibile lanciare un oggetto utilizzando LINQ:

List<MyType> items = anonymousType.Select(t => new MyType(t.Some, t.Other)).ToList(); 
+1

Questa è la soluzione più veloce qui. –