【问题标题】:Copy values from one object to another (different type)将值从一个对象复制到另一个对象(不同类型)
【发布时间】:2012-12-26 21:33:58
【问题描述】:

我需要将一个对象的一些属性复制到另一个对象。但是,某些属性需要从十进制到整数的类型转换。

我发现这个问题非常有用: Copy values from one object to another

但是,我不知道如何修改 Jon Skeet 和 Marc Gravell 的 MiscUtil 中的代码以检查属性类型以及如果源是十进制而目标是 int,则调用 Convert.ToIn32()。

这是来自 MiscUtil 的代码,我想弄清楚如何修改:

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

【问题讨论】:

  • 你可以看看 AutoMapper。对于这类东西,它是一个非常有用的实用程序。
  • 我考虑过 AutoMapper,但我不想在我的项目中添加另一个 DLL。如果 AutoMapper 是像 Massive 这样的单一课程的下降,我会这样做。

标签: c#


【解决方案1】:

如果你有

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

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

然后使用AutoMapper(可从 NuGet 获得)您可以像这样将对象 Foo 映射到对象 Bar:

Mapper.CreateMap<Foo, Bar>();
Foo foo = new Foo() { Value = 10.5M };
var bar = Mapper.Map<Bar>(foo);
// bar.Value = 10;

【讨论】:

    【解决方案2】:

    我们也可以使用 System.Reflection。 按照这个函数:

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

    假设我们有两个类:

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

    我们写道:

    var a = new A
            {
                Prop1 = "A",
                Prop2 = "B",
                Prop3 = "C",
                Prop4 = "D",
            };
    
    var b = CloneData<B>(a);
    

    我们将获得一个类 B 的实例,该实例具有从 A 复制的 Prop2 和 Prop3 值。

    【讨论】:

      【解决方案3】:

      你可以检查类型像

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

      【讨论】:

      • 我不确定在上面的 BuildCopier 方法中在哪里添加这种代码。有什么建议吗?
      猜你喜欢
      • 2011-02-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-04
      • 2021-07-09
      • 1970-01-01
      • 2020-01-02
      相关资源
      最近更新 更多