Копировать значения из одного объекта в другой - PullRequest
14 голосов
/ 12 апреля 2010

У кого-нибудь есть предложение для хорошего класса утилит, который отображает значения из одного объекта в другой? Мне нужен служебный класс, который использует отражение и переносит два объекта и копирует значения из 1-го объекта во второй, если есть открытое свойство с тем же именем.

У меня есть две сущности, которые сгенерированы из прокси-сервера веб-службы, поэтому я не могу изменить родительский класс или использовать интерфейс или что-то в этом роде. Но я знаю, что два объекта имеют одинаковые общедоступные свойства.

Ответы [ 6 ]

41 голосов
/ 12 апреля 2010

Должно быть довольно просто бросить вместе ...

public static void CopyPropertyValues(object source, object destination)
{
    var destProperties = destination.GetType().GetProperties();

    foreach (var sourceProperty in source.GetType().GetProperties())
    {
        foreach (var destProperty in destProperties)
        {
            if (destProperty.Name == sourceProperty.Name && 
        destProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
            {
                destProperty.SetValue(destination, sourceProperty.GetValue(
                    source, new object[] { }), new object[] { });

                break;
            }
        }
    }
}
18 голосов
/ 12 апреля 2010

У Джона Скита и Марка Гравелла есть библиотека с именем MiscUtil . Внутри MiscUtil.Reflection есть класс PropertyCopy, который делает именно то, что вы описываете. Это работает только для .NET 3.5.

Он работает путем запуска открытых свойств SourceType, сопоставляет их по имени с общими свойствами TargetType, гарантирует, что каждое свойство может быть назначено от источника к цели, а затем создает и кэширует функцию копира. для этих двух типов (так что вы не делаете все это отражение каждый раз). Я использовал его в рабочем коде и могу ручаться за его совершенство.

Что за эй, я подумал, что просто выложу их краткий код (это менее 100 строк с комментариями). Лицензию для этого кода можно найти здесь :

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();
            }
        }
    }
}
11 голосов
/ 12 апреля 2010

Мы используем Automapper для этого. Это работает очень хорошо.

5 голосов
/ 15 июня 2013

Я улучшил ответ Робинсона и преобразовал его в метод расширения для типа Объект, очень удобно:

    public static void CopyPropertyValues( this object destination, object source )
    {
        if ( !( destination.GetType ().Equals ( source.GetType () ) ) )
            throw new ArgumentException ( "Type mismatch" );
        if ( destination is IEnumerable )
        {
            var dest_enumerator = (destination as IEnumerable).GetEnumerator();
            var src_enumerator = (source as IEnumerable).GetEnumerator();
            while ( dest_enumerator.MoveNext () && src_enumerator.MoveNext () )
                dest_enumerator.Current.CopyPropertyValues ( src_enumerator.Current );
        }
        else
        {
            var destProperties = destination.GetType ().GetRuntimeProperties ();
            foreach ( var sourceProperty in source.GetType ().GetRuntimeProperties () )
            {
                foreach ( var destProperty in destProperties )
                {
                    if ( destProperty.Name == sourceProperty.Name 
                        && destProperty.PropertyType.GetTypeInfo ()
                            .IsAssignableFrom ( sourceProperty.PropertyType.GetTypeInfo () ) )
                    {
                        destProperty.SetValue ( destination, sourceProperty.GetValue (
                            source, new object[] { } ), new object[] { } );
                        break;
                    }
                }
            }
        }
    }
2 голосов
/ 12 апреля 2010

Я написал в блоге о , используя Expression<T>, чтобы сделать это вскоре после прочтения этой статьи Марком Гравеллом.

Похоже (основываясь на другом ответе), что он может быть похож на тот, который есть у Джона Скита и MiscUtil от Марка.

1 голос
/ 02 ноября 2016

Что делать с Json.net?

static T CopyPropertiesJson<T>(object source)
{
    string jsonsource = JsonConvert.SerializeObject(source);
    return JsonConvert.DeserializeObject<T>(jsonsource);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...