2009-08-31 11 views
6

私は2つのオブジェクトを持っていると私はそれらをマージする:作成するにはC#を使用して実行時に2つのオブジェクトをマージする最適な方法は何ですか?

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

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

私は、実行時にのみはFooの構造を知ることができます。バーは実行時に任意のタイプにすることができます。タイプを与えられ、そのタイプをFooと組み合わせるメソッドを持っていたいと思います。たとえば、上記のシナリオでは、実行時にメソッドにBar型が与えられ、Fooと組み合わせました。

これを行うにはどうすればよいでしょうか?それはLINQ式を使用して行うことができますか、それを動的に生成する必要がありますか、別の方法がありますか?私はまだC#3.0で新しいLINQ名前空間を学んでいるので、LINQ式を使用して実行できない場合は無知を言い訳します。これはC#でこのような動的なことをしなければならなかったのは初めてのことなので、私が利用できるすべてのオプションについてはあまりよく分かりません。

ありがとうございました。

EDIT


これは、直列化のために私に与えられた型にメタ情報を付加するために厳密です。このシナリオでは、シリアライズされる前に、追加する必要のあるメタ情報をユーザーのオブジェクトに無関係に保持します。私はこの質問をする前に2つの選択肢を考え出しており、使用するものを決める前に、もう何があったかを見たいと思っていました。

私が出ている2つのオプションがあります。メタ情報を付加することで、それをシリアル化した後、私に与えられたタイプのシリアル化された文字列を操作

私に与えられたタイプをラップします。これは、@ Zxproと同じですが、私のものはわずかに異なりますが、これは問題ありません。それはちょうど、誰もが設定より規約についてですので、私のAPIのユーザーは、悪いことではありません慣例を、従わなければならないでしょう。

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

EDIT


おかげで皆を彼らの答えは私は上記のようにオブジェクトをラップすることに決めました。そして大多数がこのアプローチを好んだので、@ Zxproに答えを出しました。

他の誰かがこの質問に遭遇した場合は、より良い方法があると思われる場合は投稿してください。

答えて

8

あなたはグループ化されたのではなくをマージされるそれらを気にしない場合:

脇質問「なぜ」、私は2つのオブジェクト、公知のものを取るために考えることができる唯一の方法は、未知の1から
public class FooEx<T> 
{ 
    public Foo Foo { get; set; } 
    public T Ex { get; set; } 
} 
+1

これは理想的な解決策ですが、Barのコンパイル時の知識が必要です。元のAskerは実行時にしか利用できないと述べています。 – Randolpho

+0

@Randalpho:たぶん、そうではないかもしれない。この文脈では* *知られていないかもしれませんが、アーキテクチャに応じてジェネリックを使用することも可能でしょう。 –

+0

私はそれについて考えてきました。これが簡単で効果的なことができなければ、それは私の秋の計画です。私のところは少し違いました。基本的にpublic Fooを作成する {public string Name {get;セット; }パブリックTコンテンツ{get;セット; }} –

2

残念ながら、これは簡単には行えません。あなたができることは、LINQクエリの一部として匿名型を作成することですが、ローカルのスコープにしかなりません。

.NET 4がリリースされたとき、あなたに役立つ新しい動的ランタイムライブラリがあります。

+0

私はチャンスが得られたら、C#4の新しい動的なものを確かにチェックします。 –

1

それらを新しいタイプに組み合わせると、実行時にReflection.Emitを使用して新しいタイプを生成することになります。

MSDNには例があります。同じ名前を持つフィールドをマージするか、または既知のタイプを未知のタイプに置き換える天気を決定する必要があります。

私が知る限り、LINQでこれを行う方法はありません。

あなたが興味を持っているのはプロパティですから、例としてthis articleを使用するのは簡単です。メソッドを作成するためにilを残しておいてください。

+0

「なぜ」は、永続的なストレージの1つのタイプにシリアライズするためのものです。 Zxproへの私のコメントのように、オブジェクトを入れ子にする必要がなくなります。 –

+0

最初のものをシリアル化してから、もう一方を同じストリームにシリアル化するのはなぜですか? –

+0

@Anton Tykhyy - それは私が使ったオプションの一つです。私は、私の場合、文字列を操作する代わりに、他のものを探しています。 –

0

他の人が指摘しているように、(たとえば、select *をSQLの複数のテーブルと考える場合は)それらをマージする方法はありません。あなたの最も近いアナログは、Zxproが提供したルートを取って、ジェネリッククラスでそれらを「グループ化」します。

正確には、それらを「マージ」することで達成したいことはありますか?明示的にプロパティを宣言すると、コードとコンパイル時の安全性を書く上で最も大きな利便性が得られますが、型を指定できない場合はそのための機会はありません。一般的な「プロパティバッグ」コンテナを探しているだけの場合は、Dictionary<T,T>Hashtableなどの既存のデータ構造で対応できます。

3

テストされていないが、Reflection.EmitのAPIを使用して、このようなものは動作するはずです:

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

USAGE:

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

コードをお寄せいただき、ありがとうございます。私はいつもReflectionを知っていました.Emitは実行可能な解決策でしたが、私は他の方法があることを望んでいました。タイプをラップすることがうまくいかない場合、私はこれを覚えておいてください。 –

0

あなたが何かを行うことができ、メタデータクラスにメソッドを追加することができた場合次のように

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

私は主にこのアプローチをお勧めしますそう

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

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

のような最初のクラスは、第二のクラスを取るとのXmlElementのようにそれをシリアライズ変更

:それはここ

+0

問題は実行時にFooもBarも知られておらず、あなたのコードはその情報を知っていることに依存しています。 –

+0

私はfooとbarのすべての組み合わせが_compile time_(またはその数が大きすぎる)で知られていないとしたら、実行時にオブジェクト型がどのように不明であるかを知ることができません(インスタンス化されていないことを意味します)結果は同じ –

+0

"実行前にFooもBarも知られていません" ...私が言ったのは、コンパイル時にBarにGuidとProperty1があると仮定したことと、ポスターがタイプをマージする方法コンパイル時には不明です。 –

0

別のオプションを探検する価値があるかもしれない可能なオプションとして、匿名型を表示しますそう:

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; 

} 

設定されたプロパティのXmlElementに任意の、そしてあなたが他のクラスのEMBのためのXMLが表示されます それをシリアル化クラスが生成された場合は、要素として、以下を使用する場合でもXsd.exeではを使用してこれを離れて得ることができるすべての名前空間

を完備し、文書にedded:

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

私はあなたができると信じて複数のサブクラスのXmlElementsの配列を取得することもできます。

Deserializaingは、XmlElementsを調べて、一致するクラスを探したり、おそらくクラスを見つけるために名前空間を使用する必要があります。

これは、文字列の操作に悩まされるよりもはるかに控えめです。