Застосувати значення властивостей від одного об’єкта до іншого того самого типу автоматично?


79

Враховуючи 2 об’єкти A і B типу T, я хочу присвоїти значення властивостей в A тим самим властивостям у B, не роблячи явного призначення для кожної властивості.

Я хочу зберегти такий код:

b.Nombre = a.Nombre;
b.Descripcion = a.Descripcion;
b.Imagen = a.Imagen;
b.Activo = a.Activo;

робити щось подібне

a.ApplyProperties(b);

Це можливо?


Чи є рішення, яке не використовує Reflection?
Тарінду Сатісчандра

Відповіді:


76

У мене є тип в 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

Це не вдається скопіювати на існуючу ціль, якщо ціль не має власного конструктора, наприклад IMyType instance2 = new MyType (); Це не уникнути?
стовроз

@stovroz: Ну, вам доведеться вказати тип, який ви насправді хотіли створити.
Джон Скіт,

@Jon: Але ціль вже встановлена. Я думав, що ваші вищевказані вдосконалення мали саме для того, щоб дозволити копіювання до існуючої цілі, але, схоже, все одно хочуть створити нову за адресою: Expression.MemberInit (Expression.New (typeof (TTarget)), прив'язки).
стовроз

@Jon: У мене є клас, що містить властивості List <>, але він не створює нового екземпляра List <>, тому, якщо я додаю щось до списку, він додасть і властивості, і джерело, і призначення. Було б непогано створити новий екземпляр типів посилань у властивостях. Просто пропозиція :)
Андрія,

2
@Cawas: Ах, вибачте - я дивився на коментарі в термінології Stack Overflow :) З одного боку я б назвав це іменником , наприклад PropertyCopier. Мені довелося б трохи більше подумати над цим і побачити деякі випадки використання, щоб переробити його належним чином, чесно кажучи.
Джон Скіт,

85

Тому що я вважаю, що версія Джона занадто складна, а версія Стіва занадто проста, і мені подобається ідея Даніеля про клас розширення.

Плюс загальна версія досить гарна, але непотрібна, оскільки всі елементи є об’єктами.

Я хотів би надати добровільну свою худу та підлу версію. Подяки всім вищезазначеним. : D

Код:

using System;
using System.Reflection;
/// <summary>
/// A static class for reflection type functions
/// </summary>
public static class Reflection
{
    /// <summary>
    /// Extension for 'Object' that copies the properties to a destination object.
    /// </summary>
    /// <param name="source">The source.</param>
    /// <param name="destination">The destination.</param>
    public static void CopyProperties(this object source, object destination)
    {
        // If any this null throw an exception
        if (source == null || destination == null)
            throw new Exception("Source or/and Destination Objects are null");
            // Getting the Types of the objects
        Type typeDest = destination.GetType();
        Type typeSrc = source.GetType();

        // Iterate the Properties of the source instance and  
        // populate them from their desination counterparts  
        PropertyInfo[] srcProps = typeSrc.GetProperties();
        foreach (PropertyInfo srcProp in srcProps)
        {
            if (!srcProp.CanRead)
            {
                continue;
            }
            PropertyInfo targetProperty = typeDest.GetProperty(srcProp.Name);
            if (targetProperty == null)
            {
                continue;
            }
            if (!targetProperty.CanWrite)
            {
                continue;
            }
            if (targetProperty.GetSetMethod(true) != null && targetProperty.GetSetMethod(true).IsPrivate)
            {
                continue;
            }
            if ((targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) != 0)
            {
                continue;
            }
            if (!targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType))
            {
                continue;
            }
            // Passed all tests, lets set the value
            targetProperty.SetValue(destination, srcProp.GetValue(source, null), null);
        }
    }
}

Використання:

/// <summary>
/// ExampleCopyObject
/// </summary>
/// <returns></returns>
public object ExampleCopyObject()
{
    object destObject = new object();
    this.CopyProperties(destObject); // inside a class you want to copy from

    Reflection.CopyProperties(this, destObject); // Same as above but directly calling the function

    TestClass srcClass = new TestClass();
    TestStruct destStruct = new TestStruct();
    srcClass.CopyProperties(destStruct); // using the extension directly on a object

    Reflection.CopyProperties(srcClass, destObject); // Same as above but directly calling the function

    //so on and so forth.... your imagination is the limits :D
    return srcClass;
}

public class TestClass
{
    public string Blah { get; set; }
}
public struct TestStruct
{
    public string Blah { get; set; }
}

Оскільки мені було нудно, і коментар запропонував версію linq

using System;
using System.Linq;
using System.Reflection;
/// <summary>
/// A static class for reflection type functions
/// </summary>
public static class Reflection
{
    /// <summary>
    /// Extension for 'Object' that copies the properties to a destination object.
    /// </summary>
    /// <param name="source">The source.</param>
    /// <param name="destination">The destination.</param>
    public static void CopyProperties(this object source, object destination)
    {
        // If any this null throw an exception
        if (source == null || destination == null)
            throw new Exception("Source or/and Destination Objects are null");
        // Getting the Types of the objects
        Type typeDest = destination.GetType();
        Type typeSrc = source.GetType();
        // Collect all the valid properties to map
        var results = from srcProp in typeSrc.GetProperties()
                                    let targetProperty = typeDest.GetProperty(srcProp.Name)
                                    where srcProp.CanRead
                                    && targetProperty != null
                                    && (targetProperty.GetSetMethod(true) != null && !targetProperty.GetSetMethod(true).IsPrivate)
                                    && (targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) == 0
                                    && targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType)
                                    select new { sourceProperty = srcProp, targetProperty = targetProperty };
        //map the properties
        foreach (var props in results)
        {
            props.targetProperty.SetValue(destination, props.sourceProperty.GetValue(source, null), null);
        }
    }
}

Ха-ха, я буквально думав про цю саму думку, коли прокручував вашу відповідь. Рішення Goldilocks. Хоча, якби я писав його, він би точно використовував Linq :)
theMayer

Я отримую нульовий виняток. Я думаю, це пов’язано із властивістю лише для читання. Як я можу цього уникнути?
Pedro77

Гаразд, вирішено, додавши відразу після: "(targetProperty.GetSetMethod (true)! = Null &&! TargetProperty.GetSetMethod (true) .IsPrivate)"
Pedro77

23
На запитання, на яке також відповів Джон Скіт, повинен бути значок для відповідей, які мають 20 голосів.
Тоні Л.

Чому "! TargetProperty.GetSetMethod (true) .IsPrivate"?
Rhyous

30

Ось коротка і солодка версія, оскільки ви сказали, що обидва ваші об’єкти одного типу:

foreach (PropertyInfo property in typeof(YourType).GetProperties().Where(p => p.CanWrite))
{
    property.SetValue(targetObject, property.GetValue(sourceObject, null), null);
}

22

Спираючись на метод Стіва, я пішов із методом розширення. Це використовує мій базовий клас як тип, але повинен бути корисним, навіть використовуючи об'єкт як типи параметрів. Чудово працює для мого використання.

using System.Reflection;
//*Namespace Here*
public static class Ext
{
    public static void CopyProperties(this EntityBase source, EntityBase destination)
    {
        // Iterate the Properties of the destination instance and  
        // populate them from their source counterparts  
        PropertyInfo[] destinationProperties = destination.GetType().GetProperties(); 
        foreach (PropertyInfo destinationPi in destinationProperties)
        {
            PropertyInfo sourcePi = source.GetType().GetProperty(destinationPi.Name);     
            destinationPi.SetValue(destination, sourcePi.GetValue(source, null), null);
        } 
    }
}

Використання виглядає так:

item1.CopyProperties(item2);

Тепер Item2 має ті самі дані властивостей, що і item1.


6
Наведений вище код зламається, якщо одне з властивостей доступне лише для читання. Додайте перевірку: if (destinationPi.CanWrite) перед значенням SetValue, щоб уникнути виникнення винятку.
J Wynia

8

Змінення версії Даніеля, щоб уникнути винятків.

foreach (PropertyInfo property in typeof(YourType).GetProperties())
{
  if (property.CanWrite)
  {
    property.SetValue(marketData, property.GetValue(market, null), null);
  }
}

5

Цей короткий і простий метод розширення дозволить вам скопіювати відповідні властивості з одного об'єкта на інший за допомогою перевірки значення Null і доступний для запису.

public static void CopyPropertiesTo(this object fromObject, object toObject)
    {
        PropertyInfo[] toObjectProperties = toObject.GetType().GetProperties();
        foreach (PropertyInfo propTo in toObjectProperties)
        {
            PropertyInfo propFrom = fromObject.GetType().GetProperty(propTo.Name);
            if (propFrom!=null && propFrom.CanWrite)
                propTo.SetValue(toObject, propFrom.GetValue(fromObject, null), null);
        }
    }

4

В основному в 2019 році ми, мабуть, повинні використовувати більш сучасні мовні функції, такі як дерева виразів та скомпільовані лямбда-вирази, замість Reflection

Оскільки мені не вдалося знайти "неглибокий клонер", який відповідає моїм вимогам (швидкість найбільше), я вирішив створити його сам. Він перераховує всі властивості gettable / settable, а потім створює Blockвираз, який потім компілюється та кешується. Що робить його майже в 13 разів швидшим за популярний AutoMapper. Використання дуже просте:

DestType destObject = PropMapper<SourceType, DestType>.From(srcObj);

Ви можете переглянути повне джерело тут: https://github.com/jitbit/PropMapper


1
Ліцензія MIT - це свого роду облом. .. Для невеликих функціональних можливостей вся ліцензія повинна бути в комплекті та обслуговуватися.
Макс.

@Max Я не дуже вмію в цьому, яку ліцензію вибрати? Я завжди думав, що MIT - це найпростіший і найдопустиміший варіант (як у "роби все, що хочеш")
Алекс,

Це найпростіший і дозвільний. Все, що вам потрібно зробити, це включити ліцензію до вихідного коду, це не складно.
nelsontruran

Що таке простір імен? Встановивши його через Nuget, VS не може взяти за допомогою оператора, і readme також не говорить цього.
Павел,

@Pawel Це "Jitbit.Utils", але ви можете перевірити джерело;)
Олексій,

3

Ви можете використовувати серіалізацію для глибокого клонування об’єкта:

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

На заняттях, звичайно, просто потрібно було б позначити серіалізацію.


Крім того, якщо ви хочете додати залежність від JSON.NET на користь залежності від Serializable, ви можете серіалізувати та десеріалізувати в JSON
KyleMit

Це створить новий екземпляр, який може бути не тим, що ви збираєтеся зробити, якщо ви хочете лише змінити значення властивостей.
Матьє

2

Там в ICloneable і object.MemberwiseClone (поверхневе копіювання) (це створює абсолютно новий об'єкт, так що не може задовольнити ваші вимоги).

Ви можете використовувати рефлексію, щоб зробити це самостійно (успадковуйте від базового класу, щоб вам не потрібно було повторно реалізовувати).

Або ви можете створити його за допомогою коду.


2

Протягом декількох років я користуюсь популярною бібліотекою для такої назви ValueInjecter

nuget: https://www.nuget.org/packages/ValueInjecter/

github: https://github.com/omuleanu/ValueInjecter

target.InjectFrom(source);
target.InjectFrom<Injection>(source);
target.InjectFrom(new Injection(parameters), source);
target.InjectFrom<Injection>(); // without source

Зауважте, що навіть якщо основні рішення досить прості (див. Інші відповіді), існує безліч випадків ребер (наприклад, глибока копія, загальні відомості, нульові значення) та оптимізацій (наприклад, кешування відображених властивостей), тому краще використовувати для цього підтримувану бібліотеку.


1

Ви можете спробувати щось подібне ....

MyType destination = new MyType();
MyType source = new MyType();

// Iterate the Properties of the destination instance and 
// populate them from their source counterparts

PropertyInfo[] destinationProperties = destination.GetType().GetProperties();
foreach (PropertyInfo destinationPI in destinationProperties)
{
    PropertyInfo sourcePI = source.GetType().GetProperty(destinationPI.Name);

    destinationPI.SetValue(destination,
                           sourcePI.GetValue(source, null), 
                           null);
}

1

Можливо, цей пост трохи старий, відповіді хороші, але коли вам потрібно зіставити багато об’єктів у цільовий тип - перегляд властивостей може бути дорогим (Imaging 100 реквізитів та більше).

Я використовую цей метод AutoMapFactory (потрібен лише LinQ), він повторюватиметься лише один раз у властивостях, і відображення кожного об’єкта буде швидким (100000 пропсів / секунд)

private Func<S,T> AutoMapFactory<S,T>() where T: class, new() where S : class
        {
            List<Action<T, S>> mapActions = typeof(T).GetProperties().Where(tp => tp.CanWrite)
                .SelectMany(tp => typeof(S).GetProperties().Where(sp => sp.CanRead)
                .Where(sp => sp.Name == tp.Name && tp.PropertyType.IsAssignableFrom(sp.PropertyType))
                .Select(sp => (Action<T,S>)((targetObj, sourceObj) => 
                    tp.SetValue(targetObj, sp.GetValue(sourceObj)))))
                .ToList();

            return sourceObj => {
                if (sourceObj == null) return null;

                T targetObj = new T();
                mapActions.ForEach(action => action(targetObj, sourceObj));
                return targetObj;
            };
        }

Як використовувати це:

...
var autoMapper = AutoMapFactory<SourceType, TargetType>(); //Get Only 1 instance of the mapping function
...
someCollection.Select(item => autoMapper(item)); //Almost instantaneous
...

0

Якщо ви хочете щось на зразок ApplyProperties, ви можете написати метод розширення на Object, який буде робити те, що вам потрібно. Просто зрозумійте, що такий метод розширення не був би "чистим" чи побічним ефектом. Але якщо вам потрібна можливість, це спосіб її досягти.


0

Розширюючи відповідь @Azerothians, мені потрібні були додаткові вимоги:

  1. потрібен спосіб ігнорування нульових властивостей вихідного об'єкта
  2. потрібен спосіб ігнорування явних властивостей
  3. хотіли спосіб примусити тип даних, де це можливо, відповідати, наприклад, int? до десяткової? або довго до int (через нещасні ранні рішення щодо схем від попередника)
  4. хотіли знати, скопійовано властивості чи ні (щоб визначити, чи буде потрібно оновлення баз даних)

    /// <summary>
    /// Extension for 'Object' that copies the properties to a destination object.
    /// </summary>
    /// <param name="source">The source.</param>
    /// <param name="destination">The destination.</param>
    /// <param name="PropertiesToIgnore">an optional list of property names which will NOT be copied</param>
    /// <param name="IgnoreNullProperties">when true will not update properties where the source is null</param>
    /// <param name="CoerceDataType">when true, will attempt to coerce the source property to the destination property (e.g. int to decimal) </param>
    /// <param name="ThrowOnTypeMismatch">when true, will throw a InvalidCastException if the data cannot be coerced</param>
    /// <exception cref="InvalidCastException">if there is a data type mismatch between source/destination and ThrowOnTypeMismatch is enabled and unable to coerce the data type.</exception>
    /// <returns>true if any properties were changed</returns>
    public static bool CopyProperties(this object source, object destination, IEnumerable<string> PropertiesToIgnore = null, bool IgnoreNullProperties = false, bool ThrowOnTypeMismatch = true, bool CoerceDataType = true)
    {
        if (source is null)
            throw new ArgumentNullException(nameof(source));
    
        if (destination is null)
            throw new ArgumentNullException(nameof(destination));
    
        // Getting the Types of the objects
        Type typeDest = destination.GetType();
        Type typeSrc = source.GetType();
    
        // Collect all the valid properties to map
        var results = (from srcProp in typeSrc.GetProperties()
                       let targetProperty = typeDest.GetProperty(srcProp.Name)
                       where srcProp.CanRead
                       && targetProperty != null
                       && (targetProperty.GetSetMethod(true) != null && !targetProperty.GetSetMethod(true).IsPrivate)
                       && (targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) == 0
                       && !(
                           from i in PropertiesToIgnore ?? Enumerable.Empty<string>()
                           select i
                         ).Contains(srcProp.Name)
                       && (!IgnoreNullProperties || srcProp.GetValue(source, null) != null)
                       select new { sourceProperty = srcProp, targetProperty = targetProperty }).ToList();
    
        bool PropertyChanged = false;
        //map the properties
        foreach (var props in results)
        {
            var srcValue = props.sourceProperty.GetValue(source, null);
            var dstValue = props.targetProperty.GetValue(destination, null);
            if (props.targetProperty.PropertyType.IsAssignableFrom(props.sourceProperty.PropertyType))
                props.targetProperty.SetValue(destination, srcValue, null);
            else
            {
                try
                {
                    if (!CoerceDataType)
                        throw new InvalidCastException($"Types do not match, source: {props.sourceProperty.PropertyType.FullName}, target: {props.targetProperty.PropertyType.FullName}.");
    
                    if (srcValue != null)
                    {
                        // determine if nullable type
                        Type tgtType = Nullable.GetUnderlyingType(props.targetProperty.PropertyType);
                        // if it is, use the underlying type
                        // without this we cannot convert int? -> decimal? when value is not null
                        if (tgtType != null)
                            props.targetProperty.SetValue(destination, Convert.ChangeType(srcValue, tgtType, CultureInfo.InvariantCulture), null);
                        else // otherwise use the original type
                            props.targetProperty.SetValue(destination, Convert.ChangeType(srcValue, props.targetProperty.PropertyType, CultureInfo.InvariantCulture), null);
                    }
                    else // if null we can just set it as null
                        props.targetProperty.SetValue(destination, null, null);
                }
                catch (Exception ex)
                {
                    if (ThrowOnTypeMismatch)
                        throw new InvalidCastException($"Unable to copy property {props.sourceProperty.Name} with value {srcValue} from object of type ({typeSrc.FullName}) to type ({typeDest.FullName}), Error: {ex.Message}");
                    // else ignore update
                }
                var newdstValue = props.targetProperty.GetValue(destination, null);
                if (newdstValue == null && dstValue != null || !newdstValue.Equals(dstValue))
                    PropertyChanged = true;
            }
        }
    
        return PropertyChanged;
    }
    

-3
public TestClass {
    public TestName {get;set;}
}
public void submain()
{
    var originalTestClass = new TestClass()
    {
        TestName  ="Test Name";
    };

    var newTestClass = new TestClass();
     newTestClass.CopyPropertiesFrom(originalTestClass);
}

3
Будь ласка, опишіть, у чому була проблема та як цей фрагмент її вирішить, щоб допомогти майбутнім читачам зрозуміти цю відповідь. І, квестону більше 10 років ...
ФЗ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.