简体   繁体   English

使用C#在运行时合并两个对象的最佳方法是什么?

[英]What is the best way to merge two objects during runtime using C#?

I have two objects and I want to merge them: 我有两个对象,我想合并它们:

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

To create: 创造:

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

I will only know the structure of Foo at runtime. 我只会在运行时知道Foo的结构。 Bar can be any type at runtime. Bar可以是运行时的任何类型。 I would like to have a method that will be given a type and it combine that type with Foo. 我想有一个方法,将给出一个类型,它将该类型与Foo结合起来。 For instance, the scenario above, the method was given a Bar type at runtime and I combined it with Foo. 例如,上面的场景,该方法在运行时被赋予了Bar类型,我将它与Foo结合起来。

What would be the best way to do this? 最好的方法是什么? Can it be done using LINQ Expressions or do I have to generate it Dynamically or is there another way? 可以使用LINQ表达式完成,还是必须动态生成它还是有另一种方式? I am still learning the new LINQ namespace in C# 3.0, so excuse the ignorance if it can't be done using LINQ Expressions. 我仍在学习C#3.0中新的LINQ命名空间,所以如果无法使用LINQ表达式完成无知,请原谅。 This is also the first time I have ever had to do something dynamic like this with C#, so I am not quite sure of all the options I have available to me. 这也是我第一次用C#做这样的动态,所以我不太确定我可以使用的所有选项。

Thanks for any options given. 感谢您提供的任何选项。

EDIT 编辑


This is strictly for adding meta information to the type given to me for serialization. 这严格用于将元信息添加到为我提供的序列化类型。 This scenario keeps the user's objects ignorant of the meta information that needs to be added, before it is serialized. 在序列化之前,此方案使用户的对象不知道需要添加的元信息。 I have come up with two options before asking this question and I just wanted to see if there was anymore, before deciding on which one to use. 在提出这个问题之前,我已经提出了两个选项,我只是想看看是否还有,在决定使用哪一个之前。

The two options I have come up with are: 我提出的两个选项是:

Manipulating the serialized string of the type given to me after serializing it, by adding the meta information. 通过添加元信息,操作序列化后给我的类型的序列化字符串。

Wrapping the type given to me, which is similar to what @Zxpro mentioned, but mine differed slightly, which is fine. 包含给我的类型,类似于@Zxpro提到的类型,但我的略有不同,这很好。 It will just make the user of my API have to follow the convention, which isn't a bad thing, since everybody is about convention over configuration: 它只会使我的API用户必须遵循约定,这不是一件坏事,因为每个人都是关于配置的约定:

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

EDIT 编辑


Thanks everybody for their answers. 谢谢大家的回答。 I decided on wrapping the object like above and I gave the answer to @Zxpro, since a majority liked that approach also. 我决定像上面一样包装对象,我给了@Zxpro的答案,因为大多数人都喜欢这种方法。

If anybody else comes across this question, feel free to post, if you think there might be a better way. 如果有其他人遇到这个问题,请随意发帖,如果您认为可能有更好的方法。

If you don't mind them being grouped rather than merged : 如果您不介意将它们分组而不是合并

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

UNTESTED, but using the Reflection.Emit API, something like this should work: 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);
}

USAGE: 用法:

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

Unfortunately, this is not something you can do easily. 不幸的是,这不是你可以轻易做到的事情。 The best you can do is to create an anonymous type as part of a LINQ query, but that will have local scope only, and so will only be good for you in the method wherein you make it. 你可以做的最好的事情是创建一个匿名类型作为LINQ查询的一部分,但它只有本地范围,所以只有你做的方法才对你有好处。

When .NET 4 comes out, there's a new Dynamic Runtime Library that might help you out. 当.NET 4发布时,有一个新的动态运行时库可能会帮助你。

Aside from the question "Why", the only way I can think to take two objects, one known and one unknown, and combine them into a new type would be to use Reflection.Emit to generate a new type at runtime. 除了“为什么”这个问题之外,我可以考虑采用两个对象,一个已知对象和一个未知对象,并将它们组合成一个新类型的唯一方法是使用Reflection.Emit在运行时生成一个新类型。

There are examples on MSDN. MSDN上有一些例子。 You would have to determine weather you wanted to merge fields that had the same name or have the known type supersede the unknown type. 您必须确定要合并具有相同名称或已知类型的字段取代未知类型的天气。

As far as I can tell, there is no way to do this in LINQ. 据我所知,在LINQ中无法做到这一点。

Since all you're interested in is Properties it should be pretty easy to use this article as an example. 既然你感兴趣的只是属性,那么以这篇文章为例应该很容易。 Leave out the il for creating methods and you're good to go. 遗漏了用于创建方法的il,你很高兴。

As others have pointed out, there is no way to "merge" them (if you're thinking of a select * with multiple tables in SQL, for example). 正如其他人所指出的那样,没有办法“合并”它们(例如,如果您考虑使用SQL中的多个表的select * )。 Your closest analog would be taking the route that Zxpro has provided and "group" them in a generic class. 你最接近的模拟将采用Zxpro提供的路线并将它们“分组”在一个通用类中。

What, exactly, would you want to accomplish with "merging" them, though? 但是,你想要通过“合并”它们来完成什么呢? Declaring the properties explicitly would have the biggest convenience effect on writing code and compile-time safety, but if you can't specify a type then there's no opportunity for that. 明确声明属性会对编写代码和编译时安全性产生最大的便利影响,但是如果你不能指定类型,那么就没有机会。 If you're just looking for a generic "property bag" container, then an existing data structure, such as a Dictionary<T,T> or Hashtable should be able to handle that. 如果您只是在寻找通用的“属性包”容器,那么现有的数据结构(如Dictionary<T,T>Hashtable应该能够处理它。

If you can add a method to the metadata class you could do something like the following 如果您可以向元数据类添加方法,则可以执行以下操作

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

I'm not sure I would recommend this exact approach I mainly left it here to display anonymous types as a possible option that might be worth exploring 我不确定我会推荐这种确切的方法我主要把它留在这里显示匿名类型作为可能值得探索的选项

Another Option: 另外一个选项:

Modify the first class like so 像这样修改第一个类

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

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

Take the second class and serialize it into an XmlElement like so: 取第二个类并将其序列化为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;

}

Set property Any to the XmlElement, and serialize it You'll see the XML for the other class embedded in the document, complete with all the namespaces 将属性Any设置为XmlElement,并对其进行序列化您将看到文档中嵌入的其他类的XML,以及所有命名空间的完整性

You can even get away with this if the class was generated using Xsd.exe if you use the following as an element: 如果使用以下作为元素使用Xsd.exe生成类,您甚至可以使用此方法:

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

I believe you can also get away with an array of XmlElements for more than one sub-class. 我相信你也可以为一个以上的子类逃脱一系列XmlElements。

Deserializaing should be a matter of inspecting the XmlElements and then looking for a matching class, or perhaps using the namespace to find the class. Deserializaing应该是检查XmlElements然后寻找匹配的类,或者可能使用命名空间来查找类。

This is a lot tidier than messing about with string manipulation. 这比搞乱字符串操作要简单得多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM