2

I'd need to copy some properties of one object to another. However, some of the properties need a type conversion from decimal to int.

I've found this question very useful: Copy values from one object to another

However, I can't figure out how to modify the code in Jon Skeet and Marc Gravell's MiscUtil to check the property types and if the source is decimal and the target is int, to call Convert.ToIn32().

Here is the code from MiscUtil that I'd like to figure out how to modify:

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;

namespace MiscUtil.Reflection
{
    /// <summary>
    /// Generic class which copies to its target type from a source
    /// type specified in the Copy method. The types are specified
    /// separately to take advantage of type inference on generic
    /// method arguments.
    /// </summary>
    public static class PropertyCopy<TTarget> where TTarget : class, new()
    {
        /// <summary>
        /// Copies all readable properties from the source to a new instance
        /// of TTarget.
        /// </summary>
        public static TTarget CopyFrom<TSource>(TSource source) where TSource : class
        {
            return PropertyCopier<TSource>.Copy(source);
        }

        /// <summary>
        /// Static class to efficiently store the compiled delegate which can
        /// do the copying. We need a bit of work to ensure that exceptions are
        /// appropriately propagated, as the exception is generated at type initialization
        /// time, but we wish it to be thrown as an ArgumentException.
        /// </summary>
        private static class PropertyCopier<TSource> where TSource : class
        {
            private static readonly Func<TSource, TTarget> copier;
            private static readonly Exception initializationException;

            internal static TTarget Copy(TSource source)
            {
                if (initializationException != null)
                {
                    throw initializationException;
                }
                if (source == null)
                {
                    throw new ArgumentNullException("source");
                }
                return copier(source);
            }

            static PropertyCopier()
            {
                try
                {
                    copier = BuildCopier();
                    initializationException = null;
                }
                catch (Exception e)
                {
                    copier = null;
                    initializationException = e;
                }
            }

            private static Func<TSource, TTarget> BuildCopier()
            {
                ParameterExpression sourceParameter = Expression.Parameter(typeof(TSource), "source");
                var bindings = new List<MemberBinding>();
                foreach (PropertyInfo sourceProperty in typeof(TSource).GetProperties())
                {
                    if (!sourceProperty.CanRead)
                    {
                        continue;
                    }
                    PropertyInfo targetProperty = typeof(TTarget).GetProperty(sourceProperty.Name);
                    if (targetProperty == null)
                    {
                        throw new ArgumentException("Property " + sourceProperty.Name + " is not present and accessible in " + typeof(TTarget).FullName);
                    }
                    if (!targetProperty.CanWrite)
                    {
                        throw new ArgumentException("Property " + sourceProperty.Name + " is not writable in " + typeof(TTarget).FullName);
                    }
                    if (!targetProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
                    {
                        throw new ArgumentException("Property " + sourceProperty.Name + " has an incompatible type in " + typeof(TTarget).FullName);
                    }
                    bindings.Add(Expression.Bind(targetProperty, Expression.Property(sourceParameter, sourceProperty)));
                }
                Expression initializer = Expression.MemberInit(Expression.New(typeof(TTarget)), bindings);
                return Expression.Lambda<Func<TSource,TTarget>>(initializer, sourceParameter).Compile();
            }
        }
    }
}
Community
  • 1
  • 1
Steve
  • 35
  • 1
  • 6
  • 2
    You could take a look at AutoMapper. It's a very usefull utillity for this kind of stuff. – w00 Dec 26 '12 at 21:35
  • I considered AutoMapper, but I don't want to add another DLL to my project. If AutoMapper was a drop in single class like Massive I would do it. – Steve Dec 27 '12 at 02:04

3 Answers3

4

If you have

public class Foo
{
    public decimal Value { get; set; }
}

public class Bar
{
    public int Value { get; set; }
}

Then with AutoMapper (available from NuGet) you can map object Foo to object Bar like this:

Mapper.CreateMap<Foo, Bar>();
Foo foo = new Foo() { Value = 10.5M };
var bar = Mapper.Map<Bar>(foo);
// bar.Value = 10;
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
1

We can use System.Reflection for the same. Following this function:

 public static T CloneData<T>(object source)
    {
        var target = (T)Activator.CreateInstance(typeof(T));

        Type objTypeBase = source.GetType();
        Type objTypeTarget = target.GetType();

        PropertyInfo _propinfo = null;
        var propInfos = objTypeBase.GetProperties(BindingFlags.Instance | BindingFlags.Public);
        foreach (var propInfo in propInfos)
        {
            try
            {
                _propinfo = objTypeTarget.GetProperty(propInfo.Name, BindingFlags.Instance | BindingFlags.Public);
                if (_propinfo != null)
                {
                    _propinfo.SetValue(target, propInfo.GetValue(source));
                }
            }
            catch (ArgumentException aex) { if (!string.IsNullOrEmpty(aex.Message)) continue; }
            catch (Exception ex) { if (!string.IsNullOrEmpty(ex.Message)) return default(T);  }
        }

        return target; 
    }     

Suppose we have two classes:

public class A
{
    public string Prop1 {get ; set; }
    public string Prop2 {get ; set; }
    public string Prop3 {get ; set; }
    public string Prop4 {get ; set; }
}

public class B
{
    public string Prop2 {get ; set; }
    public string Prop3 {get ; set; }
}

and we write:

var a = new A
        {
            Prop1 = "A",
            Prop2 = "B",
            Prop3 = "C",
            Prop4 = "D",
        };

var b = CloneData<B>(a);

we will get an instance of class B having Prop2 and Prop3 values copied from A.

Venugopal M
  • 2,280
  • 1
  • 20
  • 30
0

You can check the type like

        int x = 4;
        if(x.GetType()== typeof(int))
        {
            ///DO Stuff
        }

        if(x.GetType()==typeof(decimal)
           {
                ///Do stuff
             }
Dan Hunex
  • 5,172
  • 2
  • 27
  • 38
  • I'm not sure where to add that kind of code in the BuildCopier method above. Any suggestions? – Steve Jan 25 '13 at 19:39