Примените значения свойств от одного объекта до другого из того же типа автоматически?

Это зависит от того, что вы пытаетесь напечатать. Вам нужно стороннее приложение для принтера pdf или, если вы печатаете свои собственные данные, вы можете использовать средство просмотра отчетов в Visual Studio. Он может выводить отчеты в Excel и PDF-файлы.

65
задан Mark Amery 10 September 2017 в 22:28
поделиться

4 ответа

У меня есть тип в MiscUtil под названием PropertyCopy , который делает нечто подобное, хотя создает новый экземпляр целевого типа и копирует свойства в

Он не требует, чтобы типы были одинаковыми - он просто копирует все читаемые свойства из «исходного» типа в «целевой» тип. Конечно, если типы одинаковы, это с большей вероятностью сработает :) Кстати, это мелкая копия

В блоке кода внизу этого ответа я расширил возможности класса. Для копирования из одного экземпляра в другой он использует простые значения PropertyInfo во время выполнения - это медленнее, чем использование дерева выражений, но альтернативой было бы написать динамический метод, который мне не слишком нравится на. Если производительность абсолютно важна для вас, дайте мне знать, и я посмотрю, что я могу сделать. Чтобы использовать этот метод, напишите что-то вроде:

MyType instance1 = new MyType();
// Do stuff
MyType instance2 = new MyType();
// Do stuff

PropertyCopy.Copy(instance1, instance2);

(где Copy - это общий метод, вызываемый с использованием вывода типа).

Я не совсем готов сделать полную версию MiscUtil, но вот обновленный код, включая комментарии. Я не собираюсь перематывать их для редактора SO - просто скопирую весь кусок.

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

#if DOTNET35
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;

namespace MiscUtil.Reflection
{
    /// <summary>
    /// Non-generic class allowing properties to be copied from one instance
    /// to another existing instance of a potentially different type.
    /// </summary>
    public static class PropertyCopy
    {
        /// <summary>
        /// Copies all public, readable properties from the source object to the
        /// target. The target type does not have to have a parameterless constructor,
        /// as no new instance needs to be created.
        /// </summary>
        /// <remarks>Only the properties of the source and target types themselves
        /// are taken into account, regardless of the actual types of the arguments.</remarks>
        /// <typeparam name="TSource">Type of the source</typeparam>
        /// <typeparam name="TTarget">Type of the target</typeparam>
        /// <param name="source">Source to copy properties from</param>
        /// <param name="target">Target to copy properties to</param>
        public static void Copy<TSource, TTarget>(TSource source, TTarget target)
            where TSource : class
            where TTarget : class
        {
            PropertyCopier<TSource, TTarget>.Copy(source, target);
        }
    }

    /// <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, TTarget>.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.
    /// Note that this type we do not have a constructor constraint on TTarget, because
    /// we only use the constructor when we use the form which creates a new instance.
    /// </summary>
    internal static class PropertyCopier<TSource, TTarget>
    {
        /// <summary>
        /// Delegate to create a new instance of the target type given an instance of the
        /// source type. This is a single delegate from an expression tree.
        /// </summary>
        private static readonly Func<TSource, TTarget> creator;

        /// <summary>
        /// List of properties to grab values from. The corresponding targetProperties 
        /// list contains the same properties in the target type. Unfortunately we can't
        /// use expression trees to do this, because we basically need a sequence of statements.
        /// We could build a DynamicMethod, but that's significantly more work :) Please mail
        /// me if you really need this...
        /// </summary>
        private static readonly List<PropertyInfo> sourceProperties = new List<PropertyInfo>();
        private static readonly List<PropertyInfo> targetProperties = new List<PropertyInfo>();
        private static readonly Exception initializationException;

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

        internal static void Copy(TSource source, TTarget target)
        {
            if (initializationException != null)
            {
                throw initializationException;
            }
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            for (int i = 0; i < sourceProperties.Count; i++)
            {
                targetProperties[i].SetValue(target, sourceProperties[i].GetValue(source, null), null);
            }

        }

        static PropertyCopier()
        {
            try
            {
                creator = BuildCreator();
                initializationException = null;
            }
            catch (Exception e)
            {
                creator = null;
                initializationException = e;
            }
        }

        private static Func<TSource, TTarget> BuildCreator()
        {
            ParameterExpression sourceParameter = Expression.Parameter(typeof(TSource), "source");
            var bindings = new List<MemberBinding>();
            foreach (PropertyInfo sourceProperty in typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                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.GetSetMethod().Attributes & MethodAttributes.Static) != 0)
                {
                    throw new ArgumentException("Property " + sourceProperty.Name + " is static 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)));
                sourceProperties.Add(sourceProperty);
                targetProperties.Add(targetProperty);
            }
            Expression initializer = Expression.MemberInit(Expression.New(typeof(TTarget)), bindings);
            return Expression.Lambda<Func<TSource, TTarget>>(initializer, sourceParameter).Compile();
        }
    }
}
#endif
67
ответ дан 24 November 2019 в 15:13
поделиться

Вы можете использовать сериализацию для глубокого клонирования объекта:

public static T DeepClone<T>(this T objectToClone) where T: BaseClass
{
    BinaryFormatter bFormatter = new BinaryFormatter();
    MemoryStream stream = new MemoryStream();
    bFormatter.Serialize(stream, objectToClone);
    stream.Seek(0, SeekOrigin.Begin);
    T clonedObject = (T)bFormatter.Deserialize(stream);
    return clonedObject;
}

Классы, конечно же, должны быть помечены как Serializable.

2
ответ дан 24 November 2019 в 15:13
поделиться

Если вам нужно что-то вроде ApplyProperties, вы можете написать метод расширения для Object, который будет делать то, что вам нужно. Просто поймите, что такой метод расширения не будет «чистым» или свободным от побочных эффектов. Но если вам нужна такая возможность, это способ ее реализовать.

0
ответ дан 24 November 2019 в 15:13
поделиться

Есть ICloneable и объект.MemberwiseClone (неглубокая копия) (они создают совершенно новый объект, поэтому могут не соответствовать вашим требованиям).

Вы можете использовать отражение, чтобы сделать это самостоятельно (унаследовать от базового класса, чтобы вам не приходилось повторно реализовывать).

Или вы можете сгенерировать его кодом.

1
ответ дан 24 November 2019 в 15:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: