簡體   English   中英

使用C#在運行時合並兩個對象的最佳方法是什么?

[英]What is the best way to merge two objects during runtime using 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; }
}

創造:

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的結構。 Bar可以是運行時的任何類型。 我想有一個方法,將給出一個類型,它將該類型與Foo結合起來。 例如,上面的場景,該方法在運行時被賦予了Bar類型,我將它與Foo結合起來。

最好的方法是什么? 可以使用LINQ表達式完成,還是必須動態生成它還是有另一種方式? 我仍在學習C#3.0中新的LINQ命名空間,所以如果無法使用LINQ表達式完成無知,請原諒。 這也是我第一次用C#做這樣的動態,所以我不太確定我可以使用的所有選項。

感謝您提供的任何選項。

編輯


這嚴格用於將元信息添加到為我提供的序列化類型。 在序列化之前,此方案使用戶的對象不知道需要添加的元信息。 在提出這個問題之前,我已經提出了兩個選項,我只是想看看是否還有,在決定使用哪一個之前。

我提出的兩個選項是:

通過添加元信息,操作序列化后給我的類型的序列化字符串。

包含給我的類型,類似於@Zxpro提到的類型,但我的略有不同,這很好。 它只會使我的API用戶必須遵循約定,這不是一件壞事,因為每個人都是關於配置的約定:

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

編輯


謝謝大家的回答。 我決定像上面一樣包裝對象,我給了@Zxpro的答案,因為大多數人都喜歡這種方法。

如果有其他人遇到這個問題,請隨意發帖,如果您認為可能有更好的方法。

如果您不介意將它們分組而不是合並

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

UNTESTED,但使用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);
}

用法:

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

不幸的是,這不是你可以輕易做到的事情。 你可以做的最好的事情是創建一個匿名類型作為LINQ查詢的一部分,但它只有本地范圍,所以只有你做的方法才對你有好處。

當.NET 4發布時,有一個新的動態運行時庫可能會幫助你。

除了“為什么”這個問題之外,我可以考慮采用兩個對象,一個已知對象和一個未知對象,並將它們組合成一個新類型的唯一方法是使用Reflection.Emit在運行時生成一個新類型。

MSDN上有一些例子。 您必須確定要合並具有相同名稱或已知類型的字段取代未知類型的天氣。

據我所知,在LINQ中無法做到這一點。

既然你感興趣的只是屬性,那么以這篇文章為例應該很容易。 遺漏了用於創建方法的il,你很高興。

正如其他人所指出的那樣,沒有辦法“合並”它們(例如,如果您考慮使用SQL中的多個表的select * )。 你最接近的模擬將采用Zxpro提供的路線並將它們“分組”在一個通用類中。

但是,你想要通過“合並”它們來完成什么呢? 明確聲明屬性會對編寫代碼和編譯時安全性產生最大的便利影響,但是如果你不能指定類型,那么就沒有機會。 如果您只是在尋找通用的“屬性包”容器,那么現有的數據結構(如Dictionary<T,T>Hashtable應該能夠處理它。

如果您可以向元數據類添加方法,則可以執行以下操作

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,如下所示:

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;

}

將屬性Any設置為XmlElement,並對其進行序列化您將看到文檔中嵌入的其他類的XML,以及所有命名空間的完整性

如果使用以下作為元素使用Xsd.exe生成類,您甚至可以使用此方法:

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

我相信你也可以為一個以上的子類逃脫一系列XmlElements。

Deserializaing應該是檢查XmlElements然后尋找匹配的類,或者可能使用命名空間來查找類。

這比搞亂字符串操作要簡單得多。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM