Home > Blockchain >  Merging two class instances C#
Merging two class instances C#

Time:03-21

I have two instances of the same class (config). The class has many properties, and some properties are other classes/enums/arrays. The first instance of the class is a fully populated config. Every property is filled here. The second instance of the class is the config of what I want to change.

How can I merge two instances so that the first instance gets the properties of the second one?

public void CopyValues<T>(T target, T source)
{
    Type t = typeof(T);

    var properties = t.GetProperties().Where(prop => prop.CanRead && prop.CanWrite);

    foreach (var prop in properties)
    {
        var value = prop.GetValue(source, null);
        if (value != null)
             prop.SetValue(target, value, null);
    }
}

I tried to do it with this code, but it doesn't work if one of the instance properties is a subclass. For example:

public class Person
    {
        public string Id { get; set; }

        public string Name { get; set; }

        public Nationality? Nationality { get; set; } //enum

        public Address Address { get; set; } //class
    }

public class Address
    {
        public string Country { get; set; }

        public string City { get; set; } 
    }

Here is what I want:

public void UpdatePerson()
        {
            Person actualPerson = GetPerson(); //full config

            Person newConfig = new Person()
            {
                Address = new Address()
                {
                    City = "Berlin"
                }
            }

            CopyValues(actualPerson, newConfig);
        }

CodePudding user response:

As far as I know, you're probably going to have to write a method for this.

Something like this:

public class Person {
    //...

    public void MergeWith(Person mergeInstance) {
        // compare the values here
    }
}

CodePudding user response:

I think this problem statement is flawed -- it makes it impossible to use null as valid value and attaches special meaning to it (no intention to change during patch).

However, if that's okay for your problem you can extend your solution adding recursion to support nested data structures.

Try something along the following lines:

enum MyEnum
{
    A,
    B,
    C,
}

class SubNestedData
{
    public string StringProp { get; set; }
    public int? IntProp { get; set; }
    public double? DoubleProp { get; set; }
    public MyEnum? EnumProp { get; set; }
}

class NestedData
{
    public string StringProp { get; set; }
    public int? IntProp { get; set; }
    public double? DoubleProp { get; set; }
    public MyEnum? EnumProp { get; set; }
    public SubNestedData SubNestedProp { get; set; }
}

class Data
{
    public string StringProp { get; set; }
    public int? IntProp { get; set; }
    public double? DoubleProp { get; set; }
    public MyEnum? EnumProp { get; set; }
    public NestedData NestedProp { get; set; }
}

void PatchImpl(object target, object patch)
{
    if (target.GetType() != patch.GetType())
        throw new InvalidOperationException("Types mismatch");
    
    foreach (var prop in target.GetType().GetProperties())
    {
        if (!prop.CanRead || !prop.CanWrite)
            continue;

        Type propType = prop.PropertyType;
        
        Type nullableUnderlyingType = Nullable.GetUnderlyingType(propType);
        bool isNullable = nullableUnderlyingType != null;
        bool isReferenceType = propType.IsClass;
        bool isPrimitive = propType.IsPrimitive;

        if (!isNullable && !isReferenceType)
            continue; // null value is not supported
            
        object patchValue = prop.GetValue(patch);

        if (patchValue == null)
            continue;
            
        if (isPrimitive || propType == typeof(string)
            || (isNullable && (nullableUnderlyingType.IsPrimitive || nullableUnderlyingType.IsEnum)))
        {
            prop.SetValue(target, patchValue);
        } else // recoursively process nested types
        {
            object targetValue = prop.GetValue(target);
            
            // create instance if missing
            if (targetValue == null)
            {
                targetValue = Activator.CreateInstance(propType);
                prop.SetValue(target, targetValue);
            }
            
            PatchImpl(targetValue, patchValue);
        }
    }
}

void Patch<T>(T target, T patch)
{
    PatchImpl(target, patch);
}
  • Related