代码之家  ›  专栏  ›  技术社区  ›  Dale Ragan

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

  •  6
  • Dale Ragan  · 技术社区  · 16 年前

    我有两个对象,我想合并它们:

    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做这种动态的事情,所以我不太确定我可以选择的所有选项。

    感谢您提供的任何选择。

    编辑


    这是为了将元信息添加到为序列化而提供给我的类型。此方案使用户的对象在序列化之前不知道需要添加的元信息。在问这个问题之前,我已经想出了两个选择,我只是想在决定使用哪一个之前看看是否还有其他选择。

    我提出的两种选择是:

    在序列化后,通过添加元信息来操作给定类型的序列化字符串。

    包装给我的类型,类似于@zxpro提到的,但是我的有点不同,这很好。它只会让我的API用户必须遵循约定,这不是一件坏事,因为每个人都在关注约定而不是配置:

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

    编辑


    谢谢大家的回答。我决定像上面那样包装这个对象,我把答案给了@zxpro,因为大多数人也喜欢这个方法。

    如果有人遇到这个问题,请随时发帖,如果你认为有更好的方法。

    7 回复  |  直到 14 年前
        1
  •  8
  •   Bryan Menard    14 年前

    如果你不介意他们分组而不是 合并 :

    public class FooEx<T>
    {
        public Foo Foo { get; set; }
        public T Ex { get; set; }
    }
    
        2
  •  3
  •   BFree    16 年前

    未测试,但使用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));
    
        3
  •  2
  •   Randolpho    16 年前

    不幸的是,这不是你能轻易做到的。最好的方法是创建一个匿名类型作为LINQ查询的一部分,但是这样做会 地方的 仅限于范围,因此仅适用于您所采用的方法。

    当.NET 4出现时,会有一个新的动态运行库来帮助您。

        4
  •  1
  •   Blue Toque    16 年前

    除了“为什么”这个问题,我唯一能想到的方法是将两个对象(一个已知对象和一个未知对象)合并成一个新类型,那就是使用reflection.emit在运行时生成一个新类型。

    在msdn上有一些例子。您必须确定是否要合并具有相同名称或已知类型取代未知类型的字段。

    据我所知,在林肯没有办法做到这一点。

    因为你只对属性感兴趣,所以应该很容易使用 this article 作为一个例子。去掉创建方法的IL,你就可以走了。

        5
  •  0
  •   Adam Robinson    16 年前

    正如其他人所指出的,没有办法“合并”它们(如果你正在考虑 select * 例如,在SQL中有多个表)。您最接近的模拟将采用zxpro提供的路由,并将它们“分组”到一个通用类中。

    但是,您希望通过“合并”它们来完成什么呢?显式声明属性对编写代码和编译时安全性的影响最大,但如果不能指定类型,则没有机会这样做。如果您只是在寻找一个通用的“属性包”容器,那么一个现有的数据结构,例如 Dictionary<T,T> Hashtable 应该能够处理。

        6
  •  0
  •   Rune FS    16 年前

    如果可以向元数据类添加方法,可以执行以下操作

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

    我不确定我是否会推荐这种确切的方法,我主要是把它留在这里,以显示匿名类型作为可能的选项,这可能值得研究。

        7
  •  0
  •   Blue Toque    16 年前

    另一种选择:

    像这样修改第一个类

    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" />
    

    我相信您还可以为多个子类使用一个XML元素数组。

    反序列化应该是检查XML元素,然后查找匹配的类,或者使用命名空间查找类。

    这比乱搞字符串操作要整洁得多。