合并多个C#对象

时间:2019-07-06 19:53:06

标签: c# object merge combinations

我有两个这样的对象:

object obj1=new
            {
               ID=1,
               Title="text",
               Test= new Test(){ 
                        Number=20, IsSomething=false
                     }
            }

object obj2=new
            {
               Age=22                   
            }

我想像这样以编程方式将它们合并:

object obj3=new
            {
               ID=1,
               Title="text",
               Test= new Test(){ 
                        Number=20, IsSomething=false
                     },
               Age=22                   
            }

请注意,可能可以启动每个类而不是Test类,并且我不知道对象中具有哪个类,而我只是在运行时才找到这些对象。

我在StackOverflow中阅读了相同的问题,发现我应该使用Reflection,但是他们所有人都知道类的类型,而我只是在运行时才发现它。 怎么可能?

2 个答案:

答案 0 :(得分:4)

static class MergeExtension
{
    public static ExpandoObject Merge<TLeft, TRight>(this TLeft left, TRight right)
    {
        var expando = new ExpandoObject();
        IDictionary<string, object> dict = expando;
        foreach (var p in typeof(TLeft).GetProperties())
            dict[p.Name] = p.GetValue(left);
        foreach (var p in typeof(TRight).GetProperties())
            dict[p.Name] = p.GetValue(right);
        return expando;
    }
}

用法

var obj1 = new
{
    ID = 1,
    Title = "text",
    Test = new Test()
    {
        Number = 20,
        IsSomething = false
    }
};

var obj2 = new
{
    Age = 22
};

dynamic obj3 = obj1.Merge(obj2);

Console.WriteLine(obj1.ID.Equals(obj3.ID)); // True
Console.WriteLine(obj1.Title.Equals(obj3.Title)); // True
Console.WriteLine(obj1.Test.Equals(obj3.Test)); // True
Console.WriteLine(obj2.Age.Equals(obj3.Age)); // True

注意,如果类型具有相同的属性名称,则需要某种机制来解决属性冲突。

答案 1 :(得分:1)

基于以下答案:https://stackoverflow.com/a/3862241/9748260

我对提供的类型生成器进行了一些修改,如下所示:

public static class MyTypeBuilder
{
  public static Type CompileResultType(List<PropertyInfo> yourListOfFields, string typeName)
  {
    TypeBuilder tb = GetTypeBuilder(typeName);
    ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

    // NOTE: assuming your list contains Field objects with fields FieldName(string) and FieldType(Type)
    foreach (var field in yourListOfFields)
      CreateProperty(tb, field.Name, field.PropertyType);

    Type objectType = tb.CreateType();
    return objectType;
  }

  private static TypeBuilder GetTypeBuilder(string typeSignature)
  {
    var an = new AssemblyName(typeSignature);
    AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
    ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
    TypeBuilder tb = moduleBuilder.DefineType(typeSignature,
            TypeAttributes.Public |
            TypeAttributes.Class |
            TypeAttributes.AutoClass |
            TypeAttributes.AnsiClass |
            TypeAttributes.BeforeFieldInit |
            TypeAttributes.AutoLayout,
            null);
    return tb;
  }

  private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
  {
    FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);

    PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
    MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
    ILGenerator getIl = getPropMthdBldr.GetILGenerator();

    getIl.Emit(OpCodes.Ldarg_0);
    getIl.Emit(OpCodes.Ldfld, fieldBuilder);
    getIl.Emit(OpCodes.Ret);

    MethodBuilder setPropMthdBldr =
        tb.DefineMethod("set_" + propertyName,
          MethodAttributes.Public |
          MethodAttributes.SpecialName |
          MethodAttributes.HideBySig,
          null, new[] { propertyType });

    ILGenerator setIl = setPropMthdBldr.GetILGenerator();
    Label modifyProperty = setIl.DefineLabel();
    Label exitSet = setIl.DefineLabel();

    setIl.MarkLabel(modifyProperty);
    setIl.Emit(OpCodes.Ldarg_0);
    setIl.Emit(OpCodes.Ldarg_1);
    setIl.Emit(OpCodes.Stfld, fieldBuilder);

    setIl.Emit(OpCodes.Nop);
    setIl.MarkLabel(exitSet);
    setIl.Emit(OpCodes.Ret);

    propertyBuilder.SetGetMethod(getPropMthdBldr);
    propertyBuilder.SetSetMethod(setPropMthdBldr);
  }
}

然后我创建了一种合并对象的方法:

public static object Merge(object obj1, object obj2, string newTypeName)
{
  var obj1Properties = obj1.GetType().GetProperties();
  var obj2Properties = obj2.GetType().GetProperties();
  var properties = obj1Properties.Concat(obj2Properties).ToList();
  Type mergedType = MyTypeBuilder.CompileResultType(properties, newTypeName);
  object mergedObject = Activator.CreateInstance(mergedType);
  var mergedObjectProperties = obj2.GetType().GetProperties();

  foreach(var property in obj1Properties)
  {
    mergedObject.GetType().GetProperty(property.Name).SetValue(mergedObject, obj1.GetType().GetProperty(property.Name).GetValue(obj1, null) , null);
  }

  foreach(var property in obj2Properties)
  {
    mergedObject.GetType().GetProperty(property.Name).SetValue(mergedObject, obj2.GetType().GetProperty(property.Name).GetValue(obj2, null) , null);
  }

  return mergedObject;
}

要测试结果:

object obj1 = new
{
  ID = 1,
  Title = "text",
  Test = new
  {
    Number = 20,
    IsSomething = false
  }
};

object obj2 = new
{
  Age = 22
};
object merged = Merge(obj1, obj2, "merged");

foreach(var x in merged.GetType().GetProperties())
{
  Console.WriteLine($"{x.Name} = {x.GetValue(merged, null)}");
}

Console.ReadLine();

输出为:

ID = 1
Title = text
Test = { Number = 20, IsSomething = False }
Age = 22

为简要说明这一点,其想法是创建一个同时具有两个对象的属性的新对象并复制其值。