akarim134
akarim134

Reputation: 11

Convert object by reflection

I want to convert an object A to object B. The classes A and B have the same properties, just the names are changed.

I use this method:

 /// <summary>

    internal static T objectMapper<T>(object objectSource, T objectTarget)
    {
        dynamic o = objectSource;

        Type typeA = objectSource.GetType();
        Type typeB = objectTarget.GetType();
        IList<PropertyInfo> propsA = new List<PropertyInfo>(typeA.GetProperties());
        IList<PropertyInfo> propsB = new List<PropertyInfo>(typeB.GetProperties());


        dynamic s;

        ArrayList listArray = new ArrayList();

        foreach (var prop in propsA)
        {
            s = objectSource.GetType().GetProperty(prop.Name).GetValue(objectSource, null);
            listArray.Add(s);
        }
        int i = 0;
        foreach (var prop in propsB)
        {
            prop.SetValue(objectTarget, listArray[i], null);
            i++;
        }
        return objectTarget;


    }

How can I edit properties of objectB in the foreach loop? I want to use a generic method for different objects.

Upvotes: 0

Views: 2226

Answers (3)

d219
d219

Reputation: 2834

You might find automapper of use here (see https://github.com/AutoMapper/AutoMapper/wiki/Getting-started).

You would need to create a line for each object mapping in a startup file to set it up but if the properties are the same this would be as simple as:

mapper.CreateMap<ClassA, ClassB>().ReverseMap();

And then a single line to resolve the mapping when needed

mapper.Map(objectOfClassA, new ClassB());

Upvotes: 0

CubanTurin
CubanTurin

Reputation: 177

I feel there might be a deeper architecture issue here. I'm failing to imagine why would you want to "copy" the values of the properties from one object of a class to another of a different class with the same property names.

If you're trying to "shape" the object maybe just passing an interface will do the work

Anyhow, see if this helps:

public static class ObjectMorpher
{
    public class InvalidMorphException : Exception
    {
    }

    [AttributeUsage(AttributeTargets.Property)]
    public class IgnoredOnMorphAttribute : Attribute
    {

    }

    public static TargetType Morph<TargetType>(this object source, TargetType dest, Func<string, string> propertyMatcher = null, bool failOnNoMatch = false)
        where TargetType : class
    {
        if (source == null || dest == null)
            throw new ArgumentNullException();
        foreach (var sourceProp in source.GetType().GetProperties().Where(x => x.GetCustomAttributes<IgnoredOnMorphAttribute>().Any() == false))
        {
            var destProp = typeof(TargetType).GetProperties().Where(x => x.Name == ((propertyMatcher == null) ? sourceProp.Name : propertyMatcher(sourceProp.Name))).FirstOrDefault();
            //check property exists
            if (destProp == null)
            {
                if (failOnNoMatch)
                    throw new InvalidMorphException();
                else
                    continue;
            }
            //check value type is assignable
            if (!destProp.GetType().IsAssignableFrom(sourceProp.GetType()))
            {
                if (failOnNoMatch)
                    throw new InvalidMorphException();
                else
                    continue;
            }
            destProp.SetValue(dest, sourceProp.GetValue(source));
        }
        return dest;
    }
}

Usage example:

var A = new ClassA();
var B = new ClassB();
B = A.Morph(B);

Optionally you can set a property match for the case when properties doesn't have the exact same name. Also notice the use of the IgnoredOnMorph attribute to mark properties as not morph-able (like calculated properties)

Upvotes: 1

Michael
Michael

Reputation: 2113

This solution provides both your reflection-way and an alternative way by defining and implementing a copy method CopyFrom. To reduce code you could make the interface a base-class so you don't need to implement CopyFrom in the sub-classes....

public interface MyInterface
{
    int Prop1 { get; set; }
    string Prop2 { get; set; }
    void CopyFrom(MyInterface obj);
}

public class A: MyInterface
{
    public int Prop1 { get; set; }
    public string Prop2 { get; set; }
    public void CopyFrom(MyInterface obj)
    {
        this.Prop1 = obj.Prop1;
        this.Prop2 = obj.Prop2;
    }
}
public class B: MyInterface
{
    public int Prop1 { get; set; }
    public string Prop2 { get; set; }
    public void CopyFrom(MyInterface obj)
    {
        this.Prop1 = obj.Prop1;
        this.Prop2 = obj.Prop2;
    }
}

public static class CopyUtils
{
    public static void Copy(MyInterface src, MyInterface dst)
    {
        var props = typeof(MyInterface).GetProperties();
        foreach(var prop in props)
        {
            prop.SetValue(dst, prop.GetValue(src, null), null);
        }        
    }
}

Upvotes: 1

Related Questions