2009-08-31 3 views
6

Ich habe zwei Objekte, und ich möchte, dass sie fusionieren:Was ist der beste Weg, um zwei Objekte zur Laufzeit mit C# zusammenzuführen?

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

zu erstellen:

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

Ich werde nur die Struktur von Foo zur Laufzeit kennen. Bar kann zur Laufzeit ein beliebiger Typ sein. Ich hätte gerne eine Methode, die einen Typ bekommt und diesen Typ mit Foo kombiniert. Im obigen Szenario wurde der Methode zur Laufzeit ein Bar-Typ zugewiesen und ich habe ihn mit Foo kombiniert.

Was wäre der beste Weg, dies zu tun? Kann es mit LINQ Expressions gemacht werden oder muss ich es dynamisch generieren oder gibt es einen anderen Weg? Ich lerne immer noch den neuen LINQ-Namespace in C# 3.0, entschuldige also die Ignoranz, wenn es nicht mit LINQ Expressions gemacht werden kann. Dies ist auch das erste Mal, dass ich jemals so etwas Dynamisches mit C# machen musste, also bin ich mir nicht sicher, welche Möglichkeiten mir zur Verfügung stehen.

Danke für alle Optionen gegeben.

EDIT


Dies ist ausschließlich für die Meta-Informationen an die für die Serialisierung mir gegeben Typ hinzufügen. In diesem Szenario bleiben die Objekte des Benutzers vor der Serialisierung über die Metadaten, die hinzugefügt werden müssen, unwissend. Ich habe mir zwei Möglichkeiten ausgedacht, bevor ich diese Frage gestellt habe, und ich wollte nur sehen, ob es noch mehr gibt, bevor ich mich entscheide, welche ich verwenden soll.

Die beiden Optionen, die ich gekommen bin mit sind:

die serialisierte Zeichenfolge des mir gegebenen Typs Manipulieren, nachdem es Serialisierung durch die Meta-Informationen hinzuzufügen.

Wrapping die Art, die mir gegeben, die ähnlich wie @ Zxpro erwähnt, aber meine differierte leicht, was in Ordnung ist. Es wird nur macht den Anwender meiner API, um die Konvention zu folgen, was keine schlechte Sache ist, da jeder über Konvention über Konfiguration ist:

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

EDIT


Vielen Dank aller für ihre Antworten. Ich entschied mich dafür, das Objekt wie oben beschrieben zu umhüllen und gab die Antwort an @Zxpro, da eine Mehrheit diesen Ansatz ebenfalls mochte.

Wenn jemand anderes auf diese Frage kommt, zögern Sie nicht zu posten, wenn Sie denken, dass es einen besseren Weg geben könnte.

Antwort

8

Wenn Sie nichts dagegen haben sie eher gruppiert werden als verschmolzen:

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

Dies wäre eine ideale Lösung, aber es würde kompilieren Zeit Kenntnisse von Bar, die der ursprüngliche Fragesteller angegeben hat nur zur Laufzeit verfügbar. – Randolpho

+0

@Randalpho: Vielleicht, vielleicht nicht. Es ist vielleicht nicht bekannt * in diesem Kontext *, aber es kann gut möglich sein, abhängig von der Architektur ein generisches zu verwenden. –

+0

Ich habe darüber nachgedacht und das ist mein Rückfallplan, wenn das nicht einfach und performant gemacht werden kann. Meine unterschied sich jedoch leicht. Öffentlich machen Foo {öffentliche Zeichenfolge Name {get; einstellen; } public T Inhalt {get; einstellen; }} –

2

Leider können Sie das nicht leicht machen. Das Beste, was Sie tun können, ist, einen anonymen Typ als Teil einer LINQ-Abfrage zu erstellen, der aber nur local Geltungsbereich hat, und so wird nur für Sie in der Methode, in der Sie es erstellen, gut sein.

Wenn .NET 4 herauskommt, gibt es eine neue Dynamic Runtime Library, die Ihnen helfen könnte.

+0

Ich werde auf jeden Fall die neuen dynamischen Sachen in C# 4 überprüfen, wenn ich eine Chance bekommen. –

1

Abgesehen von der Frage „Warum“, ich die einzige Art und Weise denken können zwei Objekte zu nehmen, eine bekannte und eine unbekannte und kombinieren Sie sie zu einem neuen Typ, um mit Reflection.Emit einen neuen Typ zur Laufzeit zu generieren.

Es gibt Beispiele auf MSDN. Sie müssten bestimmen, ob Sie Felder mit demselben Namen zusammenführen möchten oder ob der bekannte Typ den unbekannten Typ ersetzt.

Soweit ich sagen kann, gibt es keine Möglichkeit, dies in LINQ zu tun.

Da Sie nur an Eigenschaften interessiert sind, sollte es ziemlich einfach zu verwenden this article als ein Beispiel sein. Lassen Sie die IL für die Erstellung von Methoden und Sie sind gut zu gehen.

+0

Das "Warum" ist für die Serialisierung in einen Typ für persistenten Speicher. Es würde mich davon abhalten, die Objekte zu verschachteln, wie in meinem Kommentar zu Zxpro. –

+0

Warum nicht einfach die erste, dann die andere in den gleichen Stream/was auch immer? –

+0

@Anton Tykhyy - Das ist eine der Möglichkeiten, die ich verwendet habe. Ich suche nur nach etwas anderem, das besser sein könnte, anstatt in meinem Fall die Saiten zu manipulieren. –

0

Wie andere darauf hingewiesen haben, gibt es keine Möglichkeit, sie "zusammenzuführen" (wenn Sie zB an eine select * mit mehreren Tabellen in SQL denken). Ihr nächstes Analog würde den von Zxpro bereitgestellten Weg nehmen und sie in einer generischen Klasse "gruppieren".

Was genau möchten Sie erreichen, indem Sie sie "zusammenführen"? Das explizite Deklarieren der Eigenschaften hätte den größten Vorteil beim Schreiben von Code und der Sicherheit beim Kompilieren, aber wenn Sie keinen Typ angeben können, gibt es dafür keine Möglichkeit. Wenn Sie nur nach einem generischen "Property Bag" -Container suchen, dann sollte eine vorhandene Datenstruktur, wie zum Beispiel Dictionary<T,T> oder Hashtable, damit umgehen können.

3

UNTESTED, aber mit der Reflection.Emit API, so etwas wie dies funktionieren sollte:

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

ANWENDUNG:

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

Danke für den Code, ich schätze Ihre Eingabe. Ich kannte Reflection immer. Emit war eine brauchbare Lösung, aber ich hatte gehofft, dass es andere Wege geben könnte. Ich werde das im Hinterkopf behalten, wenn es nicht klappt. –

0

Wenn Sie eine Methode auf die Metadaten-Klasse hinzufügen können Sie etwas tun könnte wie der folgende

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

Ich bin mir nicht sicher, ob ich diesen genauen Ansatz empfehlen würde, den ich hauptsächlich verließ es hier anonyme Typen als mögliche Option anzuzeigen, die es wert sein könnte erkunden

+0

Problem ist, dass weder Foo noch Bar vor der Laufzeit bekannt ist, und Ihr Code hängt davon ab, diese Informationen zu kennen. –

+0

Ich sehe nicht, wie die Objekte Typ zur Laufzeit unbekannt sein kann (das würde bedeuten, dass sie nicht instanziiert werden), wie immer, wenn Sie, dass nicht alle Kombinationen von foo und bar ment an _compile time_ bekannt (oder die Anzahl zu groß ist), dann das Ergebnis ist die gleichen –

+0

„weder Foo noch Bar ist vor der Laufzeit bekannt“ ... was ich meinte ist, dass Sie angenommen haben, dass Bar ein Guid und Property1, bei der Kompilierung haben, und das Plakat ausdrücklich gefragt, für wie Typen fusionieren Das ist zur Kompilierzeit unbekannt. –

0

Eine weitere Option:

die erste Klasse ändern, wie so

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

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

die zweite Klasse nehmen und es in eine XmlElement serialisiert wie so:

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; 

} 

Set Eigentum Alle auf der XmlElement und serialisiert es Sie werden die XML für die andere Klasse emb sehen edded in dem Dokument, komplett mit allen Namensräumen

Sie auch mit diesem weg erhalten können, wenn die Klasse generiert wurde mit Xsd.exe, wenn Sie die folgenden als Element verwenden:

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

Ich glaube, Sie können erhalten Sie auch ein Array von XmlElements für mehr als eine Unterklasse.

Deserializaing sollte eine Frage der Inspektion der XmlElements sein und dann für eine passende Klasse suchen, oder vielleicht den Namensraum mit der Klasse zu finden.

Das ist viel aufgeräumter als etwa mit String-Manipulation durcheinander.

Verwandte Themen