2011-07-19 17 views
24

Jak scalić dwa anonimowe typy, aby wynik zawierał właściwości obu obiektów źródłowych?Scalanie typów anonimowych

var source1 = new 
{ 
    foo = "foo", 
    bar = "bar" 
} 

var source2 = new 
{ 
    baz = "baz" 
} 

var merged = Merge(source1, source2) // <-- here's where the magic should happen 

// merged: 
// { 
//  foo = "foo", 
//  bar = "bar", 
//  baz = "baz" 
// } 
+0

Jeśli używasz C# 4, użyj 'dynamic' do tworzenia obiektów z członkami dynamicznymi. – BoltClock

+0

@Dave, czy możesz podać metodę, którą wymyśliłeś jako odpowiedź? Byłoby bardziej widoczne. Podobało mi się, chcę to zrobić. :) – BrunoLM

+0

@BrunoLM, właśnie napisałem moje rozwiązanie. Idź zagłosuj na to :-) – davehauser

Odpowiedz

30

Więc oto, co w końcu wymyślił (inspirowane przez użytkownika @ BlueMonkMN odpowiedź):

public dynamic Merge(object item1, object item2) 
{ 
    if (item1 == null || item2 == null) 
     return item1 ?? item2 ?? new ExpandoObject(); 

    dynamic expando = new ExpandoObject(); 
    var result = expando as IDictionary<string, object>; 
    foreach (System.Reflection.PropertyInfo fi in item1.GetType().GetProperties()) 
    { 
     result[fi.Name] = fi.GetValue(item1, null); 
    } 
    foreach (System.Reflection.PropertyInfo fi in item2.GetType().GetProperties()) 
    { 
     result[fi.Name] = fi.GetValue(item2, null); 
    } 
    return result; 
} 
+0

+1 Niezła metoda, prosta i działa bardzo dobrze. – BrunoLM

+0

Możesz dodać: '.Where (x => x.CanRead)' do twojego wywołania 'GetProperties()', aby uniknąć przypadku, w którym masz tylko właściwości zapisu. –

+1

To jest anonimowy obiekt. Wszystkie właściwości powinny być czytelne. –

-1
 var merged = new 
    { 
     source1 = source1, 
     source2 = source2 
    }; 

lub

 var merged2 = new 
    { 
     foo = source1.foo, 
     bar = source1.bar, 
     baz = source2.baz 
    }; 
+0

Właściwie, tylko drugi fragment. Pierwszy jest całkowicie błędny w odniesieniu do pytania. – BoltClock

+1

Myślę, że chodzi o to, aby stworzyć ogólną metodę "scalania", która nie działałaby tutaj, gdy nie znasz wcześniej typów. –

+0

@Jon Skeet, tak, masz rację. – davehauser

12

Nie możesz. Najbliższy można pochodzić będzie:

var merged = Tuple.Create(source1, source2); 

Console.WriteLine(merged.Item1.foo); 
Console.WriteLine(merged.Item1.bar); 
Console.WriteLine(merged.Item2.baz); 

Należy pamiętać, że anonimowe typy są tworzone w kompilacji. To nie jest tak, że są to typy "dynamiczne". Można użyć ExpandoObject w .NET 4 za to, ale to nie to samo, co anonimowego typu ze wszystkimi odpowiednimi właściwościami w.

+2

Dlatego nie znalazłem odpowiedzi w twojej książce ... :-) – davehauser

+0

Czy można przekonwertować anonimowy typ na ExpandoObject? Mogę więc je połączyć (obsada jako 'IDictionary ' powinna zrobić). Jeśli wyjście jest ExpandoObject, to również działałoby w moim przypadku. – davehauser

+0

Hej, w rzeczywistości możesz * możesz *! Spójrz na drugą odpowiedź, którą napisałem (znacznie lepiej niż moja pierwsza) :) – BlueMonkMN

14
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Dynamic; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
    static void Main(string[] args) 
    { 
     var source1 = new 
     { 
      foo = "foo", 
      bar = "bar" 
     }; 

     var source2 = new 
     { 
      baz = "baz" 
     }; 

     dynamic merged = Merge(source1, source2); 

     Console.WriteLine("{0} {1} {2}", merged.foo, merged.bar, merged.baz); 
    } 

    static MergedType<T1, T2> Merge<T1, T2>(T1 t1, T2 t2) 
    { 
     return new MergedType<T1, T2>(t1, t2); 
    } 
    } 

    class MergedType<T1, T2> : DynamicObject 
    { 
    T1 t1; 
    T2 t2; 
    Dictionary<string, object> members = new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase); 

    public MergedType(T1 t1, T2 t2) 
    { 
     this.t1 = t1; 
     this.t2 = t2; 
     foreach (System.Reflection.PropertyInfo fi in typeof(T1).GetProperties()) 
     { 
      members[fi.Name] = fi.GetValue(t1, null); 
     } 
     foreach (System.Reflection.PropertyInfo fi in typeof(T2).GetProperties()) 
     { 
      members[fi.Name] = fi.GetValue(t2, null); 
     } 
    } 

    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 
     string name = binder.Name.ToLower(); 
     return members.TryGetValue(name, out result); 
    } 
    } 
} 
+0

Jeszcze raz dziękuję. Zagrałem trochę z tym. Czy istnieje jakiś powód, dla którego stworzysz rodzaj "MergedType"? Działa również z 'obiektem' dla źródeł (' static MergedType Merge (object1, object item2) ', a następnie' ... fi in item1.GetType(). GetProperties() '. Wróciłem do ExpandoObject , która wydaje się zapewniać podobne rozwiązanie, ale z mniejszym kodem Dodałem moje ostateczne rozwiązanie do mojego pytania – davehauser

+0

Nie, myślałem o tym po tym, jak napisałem i zdałem sobie sprawę, że moje pierwsze próby stworzenia silniejszego rozwiązania były bezużyteczne i używanie szablonów było prawdopodobnie całkowicie niepotrzebne – BlueMonkMN

+0

Jednakże, jeśli miałeś powody, aby nadal móc uzyskać dostęp do członków T1 i T2 przez MergedType, prawdopodobnie mógłbyś to zrobić, gdybyś zachował szablony. Intellisense prawdopodobnie byłby w stanie aby pokazać, że MergedType ma właściwość typu T1, jeśli ją utworzyłeś, oraz jej członków: – BlueMonkMN

2

Następujące prace w .NET 3.5 (i prawdopodobnie również 2.0). Zmieniłem odpowiedź davehausera.

public static object MergeJsonData(object item1, object item2) 
    { 
     if (item1 == null || item2 == null) 
      return item1 ?? item2 ?? new object(); 

     var result = new Dictionary<string, object>(); 
     foreach (System.Reflection.PropertyInfo fi in item1.GetType().GetProperties().Where(x => x.CanRead)) 
     { 
      var Value = fi.GetValue(item1, null); 
      result[fi.Name] = Value; 
     } 
     foreach (System.Reflection.PropertyInfo fi in item2.GetType().GetProperties().Where(x => x.CanRead)) 
     { 
      var Value = fi.GetValue(item2, null); 
      result[fi.Name] = Value; 
     } 
     return result; 
    } 
Powiązane problemy