1

I have two objects like this:

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

object obj2=new
            {
               Age=22                   
            }

and I want to have merged of them programmatically like this:

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

Note that it is possible each class can be initiated instead of Test class and I Don't know which class in the object I have and I found these objects just in runtime.

I read the same questions in StackOverflow and found that I should use Reflection but all of them know Type of classes and I found it just in Runtime. How it is possible to do?

4
  • 1
    What are you trying to accomplish? (Please add more details.) In some languages you might use multiple inheritance but C# does not support it - stackoverflow.com/questions/2456154/… Commented Jul 6, 2019 at 19:59
  • 1
    You could use reflection to both grab the properties and values of the two source objects as well as dynamically create a new type with the combined set of properties. However, anonymous objects are usually not a good fit for this kind of thing, may I ask what you're ultimately trying to accomplish here? It's very unusual for code to use anonymous objects in such a way that you couldn't simply do new { ID = ..., Title = ..., Test = ..., Age = ... }. Commented Jul 6, 2019 at 20:01
  • You are looking for the dynamic keyword and ExpandoObject. These allows you to add and remove properties dynamically. Very much like Javascript. Research examples to find out how they work. Reflection is a built-in feature of ExpanoObject. Commented Jul 6, 2019 at 20:19
  • 1
    @TamusJRoyce Exactly true, I need to do like JS. Can you tell me more about it? thank you Commented Jul 6, 2019 at 20:39

2 Answers 2

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

Usage

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

Note, you would need some mechanism to resolve property conflicts, if types have same property names.

Sign up to request clarification or add additional context in comments.

9 Comments

ExpandoObject is not the same as an anonymous object, and what happens if either the left or the right side is already an ExpandoObject (upvoted though, probably the simplest solution to the question)
@ Lasse Vågsæther Karlsen, thanks! Good point. There are lots of nuances to think about. It's just a thing to start with.
@Alex Thank you but I test it and returns nothing
"returns nothing", how did you verify? Given your code it gave me a perfect ExpandoObject with all the properties and values intact. How did you arrive at "returns nothing"?
@alex Never tried using an extension using a generic function. Very interesting!
|
3

Based on this answer: https://stackoverflow.com/a/3862241/9748260

I modified the provided type builder a little to look like this:

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

Then I created a method to merge the objects:

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

To test the result:

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();

The output is:

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

To explain this briefly, the idea is to create a new object which have the properties of both objects and copy their values.

2 Comments

Wonderful, Thank you so much, it's working correctly
Just an FYI this is the .NET 3 way of making dynamic objects. @alex answer is a little more up to date? Looks like both are supported by .net core.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.