2009-08-31 206 views
6

Tengo dos objetos y quiero unirlos:¿Cuál es la mejor manera de unir dos objetos durante el tiempo de ejecución usando C#?

public class Foo 
{ 
    public string Name { get; set; } 
} 

public class Bar 
{ 
    public Guid Id { get; set; } 
    public string Property1 { get; set; } 
    public string Property2 { get; set; } 
    public string Property3 { get; set; } 
    public string Property4 { get; set; } 
} 

crear:

public class FooBar 
{ 
    public string Name { get; set; } 
    public Guid Id { get; set; } 
    public string Property1 { get; set; } 
    public string Property2 { get; set; } 
    public string Property3 { get; set; } 
    public string Property4 { get; set; } 
} 

sólo conocerán la estructura de Foo en tiempo de ejecución. Bar puede ser de cualquier tipo en tiempo de ejecución. Me gustaría tener un método al que se le dé un tipo y combine ese tipo con Foo. Por ejemplo, el escenario anterior, el método recibió un tipo de barra en tiempo de ejecución y lo combiné con Foo.

¿Cuál sería la mejor manera de hacerlo? ¿Se puede hacer utilizando LINQ Expressions o tengo que generarlo dinámicamente o hay otra manera? Todavía estoy aprendiendo el nuevo espacio de nombres LINQ en C# 3.0, así que disculpe la ignorancia si no se puede hacer utilizando expresiones LINQ. Esta es también la primera vez que tengo que hacer algo dinámico como este con C#, así que no estoy seguro de todas las opciones que tengo disponibles.

Gracias por cualquier opción.

EDITAR


Esto es estrictamente para añadir información de metadatos para el tipo que me ha dado para la serialización. Este escenario evita que los objetos del usuario ignoren la metainformación que debe agregarse, antes de que se serialice. He encontrado dos opciones antes de hacer esta pregunta y solo quería ver si había más, antes de decidir cuál usar.

Las dos opciones que he llegado con son:

La manipulación de la cadena serializada del tipo dado a mí después de la serialización de que, mediante la adición de la meta-información.

Envolviendo el tipo que me dieron, que es similar a lo que @Zxpro mencionó, pero el mío difiere ligeramente, lo cual está bien. Se acaba de hacer que el usuario de mi API tiene que seguir la convención, que no es una mala cosa, porque todo el mundo está a punto convención sobre configuración:

public class Foo<T> 
{ 
    public string Name { get; set; } 
    public T Content { get; set; } 
} 

EDITAR


Gracias a todo el mundo por sus respuestas. Decidí envolver el objeto como arriba y di la respuesta a @Zxpro, ya que a la mayoría también le gustaba ese enfoque.

Si alguien más se encuentra con esta pregunta, no dude en publicar, si cree que podría haber una manera mejor.

Respuesta

8

Si no le importa que se agrupan en lugar de fusionaron:

public class FooEx<T> 
{ 
    public Foo Foo { get; set; } 
    public T Ex { get; set; } 
} 
+1

Esta sería una solución ideal, pero requeriría conocimiento en tiempo de compilación de Bar, que el asker original ha declarado que solo está disponible en tiempo de ejecución. – Randolpho

+0

@Randalpho: Tal vez, tal vez no. Puede que no se sepa * en ese contexto *, pero puede ser posible, dependiendo de la arquitectura, usar un genérico. –

+0

He pensado en eso y ese es mi plan de retroceso, si esto no se puede hacer de manera fácil y efectiva. Sin embargo, el mío era un poco diferente. Básicamente haciendo público Foo {public string Name {get; conjunto; } public T Content {get; conjunto; }} –

2

Desafortunadamente, esto no es algo que pueda hacer fácilmente. Lo mejor que puede hacer es crear un tipo anónimo como parte de una consulta LINQ, pero tendrá el alcance local local, por lo que solo será bueno para usted en el método en el que lo haga.

Cuando sale .NET 4, hay una nueva biblioteca de Dynamic Runtime Library que puede ser de ayuda.

+0

Definitivamente veré las nuevas cosas dinámicas en C# 4 cuando tenga la oportunidad. –

1

Aparte de la pregunta "¿Por qué", la única manera que puedo pensar para tomar dos objetos, uno conocido y una incógnita y combinarlos en un nuevo tipo sería usar Reflection.Emit para generar un nuevo tipo en tiempo de ejecución.

Hay ejemplos en MSDN. Tendría que determinar el tiempo que desea combinar los campos que tienen el mismo nombre o que el tipo conocido sustituya al tipo desconocido.

Por lo que yo sé, no hay forma de hacerlo en LINQ.

Como todo lo que le interesa son las propiedades, debería ser muy fácil usar this article como ejemplo. Olvídate de il para crear métodos y listo.

+0

El "Por qué", es para la serialización en un tipo para el almacenamiento persistente. Me evitaría tener que anidar los objetos, como en mi comentario a Zxpro. –

+0

¿Por qué no simplemente serializar primero uno, luego el otro en la misma secuencia/lo que sea? –

+0

@ Anton Tykhyy: Esa es una de las opciones que he usado. Solo busco cualquier cosa que sea mejor, en lugar de manipular cadenas, en mi caso. –

0

Como han señalado otros, no hay forma de "fusionarlos" (si está pensando en un select * con varias tablas en SQL, por ejemplo). Su análogo más cercano tomaría la ruta que Zxpro proporcionó y los "agrupará" en una clase genérica.

¿Qué, exactamente, te gustaría lograr con "fusionarlos"? Declarar explícitamente las propiedades tendría el mayor efecto de conveniencia en la escritura de código y la seguridad en tiempo de compilación, pero si no puede especificar un tipo, entonces no hay oportunidad para eso. Si solo está buscando un contenedor genérico de "bolsa de propiedades", entonces una estructura de datos existente, como Dictionary<T,T> o Hashtable, debería ser capaz de manejar eso.

3

sin probar, pero utilizando la API Reflection.Emit, algo como esto debería funcionar:

public Type MergeTypes(params Type[] types) 
{ 
    AppDomain domain = AppDomain.CurrentDomain; 
    AssemblyBuilder builder = 
     domain.DefineDynamicAssembly(new AssemblyName("CombinedAssembly"), 
     AssemblyBuilderAccess.RunAndSave); 
    ModuleBuilder moduleBuilder = builder.DefineDynamicModule("DynamicModule"); 
    TypeBuilder typeBuilder = moduleBuilder.DefineType("CombinedType"); 
    foreach (var type in types) 
    { 
     var props = GetProperties(type); 
     foreach (var prop in props) 
     { 
      typeBuilder.DefineField(prop.Key, prop.Value, FieldAttributes.Public); 
     } 
    } 

    return typeBuilder.CreateType(); 


} 

private Dictionary<string, Type> GetProperties(Type type) 
{ 
    return type.GetProperties().ToDictionary(p => p.Name, p => p.PropertyType); 
} 

USO:

Type combinedType = MergeTypes(typeof(Foo), typeof(Bar)); 
+0

Gracias por el código, agradezco su entrada. Siempre supe que Reflection.Emit era una solución viable, pero esperaba que hubiera otras formas. Definitivamente voy a tener esto en cuenta, si no funciona el ajuste del tipo. –

0

Si se puede añadir un método a la clase de metadatos se podría hacer algo como el siguiente

public class Foo 
{ 
    public string Name { get; set; } 
    public void SerializeWithMetadata(Bar bar) 
    { 
     var obj = new { 
         Name = this.Name, 
         Guid = bar.Guid, 
         Property1 = Bar.Property1 
         } 
     //Serialization code goes here 
    } 
} 

public class Bar 
{ 
    public Guid Id { get; set; } 
    public string Property1 { get; set; } 
    public string Property2 { get; set; } 
    public string Property3 { get; set; } 
    public string Property4 { get; set; } 
} 

No estoy seguro de que recomendaría este enfoque exacto que principalmente dejé aquí para visualizar los tipos anónimos como una posible opción que podría valer la pena explorar

+0

El problema es que ni Foo ni Bar se conocen antes del tiempo de ejecución, y su código depende de conocer esa información. –

+0

No puedo ver cómo el tipo de objetos puede ser desconocido en tiempo de ejecución (eso significa que no se crearán instancias) sin embargo si se sabe que no todas las combinaciones de foo y bar son conocidas en _compile time_ (o el número es demasiado grande) el resultado es el mismo –

+0

"ni Foo ni Bar se conocen antes del tiempo de ejecución" ... lo que quise decir es que ha asumido que Bar tiene un Guid y Property1, en tiempo de compilación, y el cartel preguntó específicamente cómo fusionar tipos que se desconocen en tiempo de compilación. –

0

Otra opción:

modificar la primera clase como así

public class Foo 
{ 
    public string Name { get; set; } 

    [System.Xml.Serialization.XmlAnyElementAttribute()] 
    public XmlElement Any {get;set;} 
} 

Tome la segunda clase y serializarlo en un XmlElement como por lo que:

XmlElement SerializeToElement(Type t, object obj) 
{ 
    XmlSerializer ser = new XmlSerializer(t); 
    StringWriter sw = new StringWriter(); 
    using (XmlWriter writer = XmlWriter.Create(sw, settings)) 
     ser.Serialize(writer, obj); 

    string val = sw.ToString(); 

    XmlDocument doc = new XmlDocument(); 
    doc.LoadXml(xmlString); 

    return (XmlElement)doc.DocumentElement; 

} 

conjunto de propiedades Cualquier al XmlElement y serializarlo verá el XML para la otra clase emb edded en el documento, completo con todos los espacios de nombres

Incluso puede salirse con la suya si la clase se generó usando Xsd.exe si se utiliza la siguiente como un elemento:

<xs:any namespace="##any" processContents="lax" /> 

Creo que se puede también salga con una matriz de XmlElements para más de una subclase.

Deserializar debe ser una cuestión de inspeccionar los elementos XmlElements y luego buscar una clase coincidente, o quizás usar el espacio de nombres para encontrar la clase.

Esto es mucho más ordenado que jugar con la manipulación de cadenas.

Cuestiones relacionadas