6

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

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 提供了答案,因为大多数人也喜欢这种方法。

如果其他人遇到这个问题,请随时发布,如果您认为可能有更好的方法。

4

7 回答 7

8

如果您不介意将它们分组而不是合并

public class FooEx<T>
{
    public Foo Foo { get; set; }
    public T Ex { get; set; }
}
于 2009-08-31T19:00:54.947 回答
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);
}

用法:

Type combinedType = MergeTypes(typeof(Foo), typeof(Bar));
于 2009-08-31T19:09:53.337 回答
2

不幸的是,这不是你可以轻易做到的。您可以做的最好的事情是创建一个匿名类型作为 LINQ 查询的一部分,但这将仅具有本地范围,因此仅在您创建它的方法中对您有好处。

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

于 2009-08-31T18:56:47.203 回答
1

除了“为什么”这个问题之外,我能想到的唯一方法是将两个对象,一个已知的,一个未知的,并将它们组合成一个新类型,那就是使用 Reflection.Emit 在运行时生成一个新类型。

MSDN上有例子。您必须确定要合并具有相同名称或已知类型取代未知类型的字段的天气。

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

由于您只对属性感兴趣,因此使用本文作为示例应该很容易。省略 il 来创建方法,你就可以开始了。

于 2009-08-31T19:04:44.220 回答
0

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

但是,您究竟想通过“合并”它们来完成什么?显式声明属性将对编写代码和编译时安全性产生最大的便利效果,但如果您不能指定类型,那么就没有机会了。如果您只是在寻找一个通用的“property bag”容器,那么一个现有的数据结构,例如 a Dictionary<T,T>orHashtable应该能够处理它。

于 2009-08-31T19:05:45.763 回答
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; }
}

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

于 2009-08-31T20:08:21.940 回答
0

另外一个选项:

像这样修改第一个类

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。

反序列化应该是检查 XmlElements 然后查找匹配的类,或者可能使用命名空间来查找类。

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

于 2009-09-01T00:36:16.947 回答