How to Copy Value from Class X to Class Y with the Same Property Name in C#

How to copy value from class X to class Y with the same property name in c#?

The lists make it tricky... my earlier reply (below) only applies to like-for-like properties (not the lists). I suspect you might just have to write and maintain code:

    Student foo = new Student {
Id = 1,
Name = "a",
Courses = {
new Course { Key = 2},
new Course { Key = 3},
}
};
StudentDTO dto = new StudentDTO {
Id = foo.Id,
Name = foo.Name,
};
foreach (var course in foo.Courses) {
dto.Courses.Add(new CourseDTO {
Key = course.Key
});
}

edit; only applies to shallow copies - not lists

Reflection is an option, but slow. In 3.5 you can build this into a compiled bit of code with Expression. Jon Skeet has a pre-rolled sample of this in MiscUtil - just use as:

Student source = ...
StudentDTO item = PropertyCopy<StudentDTO>.CopyFrom(student);

Because this uses a compiled Expression it will vastly out-perform reflection.

If you don't have 3.5, then use reflection or ComponentModel. If you use ComponentModel, you can at least use HyperDescriptor to get it nearly as quick as Expression

Student source = ...
StudentDTO item = new StudentDTO();
PropertyDescriptorCollection
sourceProps = TypeDescriptor.GetProperties(student),
destProps = TypeDescriptor.GetProperties(item),
foreach(PropertyDescriptor prop in sourceProps) {
PropertyDescriptor destProp = destProps[prop.Name];
if(destProp != null) destProp.SetValue(item, prop.GetValue(student));
}

how to copy properties from one object to another with different values C#

Deep cloning can be achieved easily through serialization, however to only copy across non-null fields needs more conditional logic, In this case I call this a Coalesce so I've named my Method CoalesceTo. You could refactor this into an extension method if you wanted to, but I wouldn't recommend it, instead put this inside a static helper class. As useful as this might be, I don't encourage it as your "goto" for a production business runtime.

Using Reflection for these types of solutions is usually the most inefficient mechanism, but it gives us a lot of flexibility and is great for mocking, prototyping and quick unit test expressions.

  • Although not in this example, it would be easy to add in checks to exclude [Obsolete] properties for advanced scenarios

The following example uses Property Name comparison, so you don't have to pass in objects of the same type. Notice that IsNull and IsValueType methods have been created to encapsulate those concepts, simplifying tweaks you might want to make to this method.

  • This method also checks that properties can be read/written before proceeding, which allows us to support readonly properties on the source object, and of course we don't try to write to readonly properties.
  • The final value parse and write is wrapped in a try catch statement that is suppressing any errors, It takes a bit of tweaking to get code like this to work universally, but it should work fine for simple type definitions.
/// <summary>
/// Deep Copy the top level properties from this object only if the corresponding property on the target object IS NULL.
/// </summary>
/// <param name="source">the source object to copy from</param>
/// <param name="target">the target object to update</param>
/// <returns>A reference to the Target instance for chaining, no changes to this instance.</returns>
public static void CoalesceTo(object source, object target, StringComparison propertyComparison = StringComparison.OrdinalIgnoreCase)
{
var sourceType = source.GetType();
var targetType = target.GetType();
var targetProperties = targetType.GetProperties();
foreach(var sourceProp in sourceType.GetProperties())
{
if(sourceProp.CanRead)
{
var sourceValue = sourceProp.GetValue(source);

// Don't copy across nulls or defaults
if (!IsNull(sourceValue, sourceProp.PropertyType))
{
var targetProp = targetProperties.FirstOrDefault(x => x.Name.Equals(sourceProp.Name, propertyComparison));
if (targetProp != null && targetProp.CanWrite)
{
if (!targetProp.CanRead)
continue; // special case, if we cannot verify the destination, assume it has a value.
else if (targetProp.PropertyType.IsArray || targetProp.PropertyType.IsGenericType // It is ICollection<T> or IEnumerable<T>
&& targetProp.PropertyType.GenericTypeArguments.Any()
&& targetProp.PropertyType.GetGenericTypeDefinition() != typeof(Nullable<>) // because that will also resolve GetElementType!
)
continue; // special case, skip arrays and collections...
else
{
// You can do better than this, for now if conversion fails, just skip it
try
{
var existingValue = targetProp.GetValue(target);
if (IsValueType(targetProp.PropertyType))
{
// check that the destination is NOT already set.
if (IsNull(existingValue, targetProp.PropertyType))
{
// we do not overwrite a non-null destination value
object targetValue = sourceValue;
if (!targetProp.PropertyType.IsAssignableFrom(sourceProp.PropertyType))
{
// TODO: handle specific types that don't go across.... or try some brute force type conversions if neccessary
if (targetProp.PropertyType == typeof(string))
targetValue = targetValue.ToString();
else
targetValue = Convert.ChangeType(targetValue, targetProp.PropertyType);
}

targetProp.SetValue(target, targetValue);
}
}
else if (!IsValueType(sourceProp.PropertyType))
{
// deep clone
if (existingValue == null)
existingValue = Activator.CreateInstance(targetProp.PropertyType);

CoalesceTo(sourceValue, existingValue);
}
}
catch (Exception)
{
// suppress exceptions, don't set a field that we can't set
}

}
}
}
}
}
}

/// <summary>
/// Check if a boxed value is null or not
/// </summary>
/// <remarks>
/// Evaluate your own logic or definition of null in here.
/// </remarks>
/// <param name="value">Value to inspect</param>
/// <param name="valueType">Type of the value, pass it in if you have it, otherwise it will be resolved through reflection</param>
/// <returns>True if the value is null or primitive default, otherwise False</returns>
public static bool IsNull(object value, Type valueType = null)
{
if (value is null)
return true;

if (valueType == null) valueType = value.GetType();

if (valueType.IsPrimitive || valueType.IsEnum || valueType.IsValueType)
{
// Handle nullable types like float? or Nullable<Int>
if (valueType.IsGenericType)
return value is null;
else
return Activator.CreateInstance(valueType).Equals(value);
}

// treat empty string as null!
if (value is string s)
return String.IsNullOrWhiteSpace(s);

return false;
}
/// <summary>
/// Check if a type should be copied by value or if it is a complexe type that should be deep cloned
/// </summary>
/// <remarks>
/// Evaluate your own logic or definition of Object vs Value/Primitive here.
/// </remarks>
/// <param name="valueType">Type of the value to check</param>
/// <returns>True if values of this type can be straight copied, false if they should be deep cloned</returns>
public static bool IsValueType(Type valueType)
{
// TODO: any specific business types that you want to treat as value types?

// Standard .Net Types that can be treated as value types
if (valueType.IsPrimitive || valueType.IsEnum || valueType.IsValueType || valueType == typeof(string))
return true;

// Support Nullable Types as Value types (Type.IsValueType) should deal with this, but just in case
if (valueType.HasElementType // It is array/enumerable/nullable
&& valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(Nullable<>))
return true;

return false;
}

Because we are using reflection here, we cant take advantage of optimisations that Generics could offer us. If you wanted to adapt this to a production environment, consider using T4 templates to script out a Generic typed version of this logic as extension methods to your business types.

Deep Cloning -

You'll notice I specifically skip arrays and other IEnumerable structures... There's a whole can of worms in supporting them, it might be better to not let the one method attempt a Deep copy, so take the nested call to CoalesceTo out, then call the clone method on each object in the tree.

The problem with arrays/collections/lists is that before you could clone, you would need to identify a way to synchronise the collection in the source with the collection in the target, you could make a convention based on an Id field or some kind of attribute like [KeyAttribute] but that sort of implementation needs to be highly specific to your business logic and is outside of the scope of this already monstrous post ;)

Types like Decimal and DateTime are problematic in these types of scenarios, they should not be compared to null, instead we have to compare them to their default type states, again we can't use the generic default operator or value in this case because the type can only be resolved at runtime.

So I've changed your classes to include an example of how DateTimeOffset is handled by this logic:

public class Employee
{
public int EmployeeID { get; set; }
public string EmployeeName { get; set; }
public DateTimeOffset Date { get; set; }
public float? Capacity { get; set; }
Nullable<int> MaxShift { get; set; }
public Address ContactAddress { get; set; }
}

public class Address
{
public string Address1 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
}

public static void TestMethod1()
{
Employee employee = new Employee();
employee.EmployeeID = 100;
employee.EmployeeName = "John";
employee.Capacity = 26.2f;
employee.MaxShift = 8;
employee.Date = new DateTime(2020,1,22);
employee.ContactAddress = new Address();
employee.ContactAddress.Address1 = "Park Ave";
employee.ContactAddress.City = "New York";
employee.ContactAddress.State = "NewYork";
employee.ContactAddress.ZipCode = "10002";

Employee employeeCopy = new Employee();
employeeCopy.EmployeeID = 101;
employeeCopy.EmployeeName = "Tom";
employeeCopy.ContactAddress = new Address();

CoalesceTo(employee, employeeCopy);
}

This results in the following object graph:

{
"EmployeeID": 101,
"EmployeeName": "Tom",
"Date": "2020-01-22T00:00:00+11:00",
"Capacity":26.2,
"MaxShift":8,
"ContactAddress": {
"Address1": "Park Ave",
"City": "New York",
"State": "NewYork",
"ZipCode": "10002"
}
}

Is it possible to copy instances of types with similar fields?

The simplest way to do what you want is to use the Automapper library.

In this case you add map for these two classes:

Mapper.CreateMap<A,B>();

and then use method Map:

A a = new A();
//initialize a
B b = Mapper.Map(a);

How to Copy Common properties Listx to Listy

You can use reflection

static T2 Copy<T1, T2>(T1 from)
{
T2 to = Activator.CreateInstance<T2>();
Type toType = typeof(T2);

foreach (var p in from.GetType().GetProperties())
{
var prop = toType.GetProperty(p.Name);
if (prop != null)
prop.SetValue(to, p.GetValue(from,null),null);
}

return to;
}

SomeType2 t2 = Copy<SomeType1,SomeType2>(new SomeType1(){/*....*/});

Is it possible to copy instances of types with similar fields?

The simplest way to do what you want is to use the Automapper library.

In this case you add map for these two classes:

Mapper.CreateMap<A,B>();

and then use method Map:

A a = new A();
//initialize a
B b = Mapper.Map(a);

Select property names based on given string

If you're sure that 'option' will always be the same value as the name of the property you can use:

PropertyInfo info = typeof(<insert class y here>).GetProperty(option);
PropertyInfo info2 = typeof(<insert class x here>).GetProperty(option);
result = (double)info.GetValue(y[0]) - (double)info.GetValue(y[1]) / (double)info2.GetValue(x[0]) - (double)info2.GetValue(x[1])

This code is untested, but should work

How do you do a deep copy of an object in .NET?

Important Note

BinaryFormatter has been deprecated, and will no longer be available in .NET after November 2023. See BinaryFormatter Obsoletion Strategy


I've seen a few different approaches to this, but I use a generic utility method as such:

public static T DeepClone<T>(this T obj)
{
using (var ms = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(ms, obj);
ms.Position = 0;

return (T) formatter.Deserialize(ms);
}
}

Notes:

  • Your class MUST be marked as [Serializable] for this to work.

  • Your source file must include the following code:

     using System.Runtime.Serialization.Formatters.Binary;
    using System.IO;

Setting object properties from other object via reflection

Yes you can do it with reflection:

http://wraithnath.blogspot.com/2011/01/how-to-copy-and-object-with-reflection.html

                    //Copy the properties
foreach ( PropertyInfo oPropertyInfo in oCostDept.GetType().GetProperties() )
{
//Check the method is not static
if ( !oPropertyInfo.GetGetMethod().IsStatic )
{
//Check this property can write
if ( this.GetType().GetProperty( oPropertyInfo.Name ).CanWrite )
{
//Check the supplied property can read
if ( oPropertyInfo.CanRead )
{
//Update the properties on this object
this.GetType().GetProperty( oPropertyInfo.Name ).SetValue( this, oPropertyInfo.GetValue( oCostDept, null ), null );
}
}
}
}

[1]: http://wraithnath.blogspot.com/2011/01/how-to-copy-and-object-with-reflection.html "



Related Topics



Leave a reply



Submit