Tự động áp dụng các giá trị thuộc tính từ đối tượng này sang đối tượng khác cùng loại?


79

Cho 2 đối tượng A và B kiểu T, tôi muốn gán giá trị của các thuộc tính trong A cho các thuộc tính tương tự trong B mà không cần thực hiện phép gán rõ ràng cho từng thuộc tính.

Tôi muốn lưu mã như thế này:

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

làm một cái gì đó như

a.ApplyProperties(b);

Có khả thi không?


Có giải pháp nào không sử dụng Reflection không?
Tharindu Sathischandra

Câu trả lời:


76

Tôi có một kiểu MiscUtilđược gọi là PropertyCopythực hiện một cái gì đó tương tự - mặc dù nó tạo ra một thể hiện mới của kiểu đích và sao chép các thuộc tính vào đó.

Nó không yêu cầu các kiểu phải giống nhau - nó chỉ sao chép tất cả các thuộc tính có thể đọc được từ kiểu "nguồn" sang kiểu "đích". Tất nhiên nếu các loại giống nhau, điều đó có nhiều khả năng hoạt động hơn :) Đó là một bản sao nông cạn, btw.

Trong khối mã ở cuối câu trả lời này, tôi đã mở rộng khả năng của lớp. Để sao chép từ thể hiện này sang thể hiện khác, nó sử dụng PropertyInfocác giá trị đơn giản tại thời điểm thực thi - điều này chậm hơn so với sử dụng cây biểu thức, nhưng cách thay thế sẽ là viết một phương thức động, điều này tôi không quá nóng. Nếu hiệu suất hoàn toàn quan trọng đối với bạn, hãy cho tôi biết và tôi sẽ xem mình có thể làm gì. Để sử dụng phương thức này, hãy viết một cái gì đó như:

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

PropertyCopy.Copy(instance1, instance2);

(đâu Copylà một phương pháp chung được gọi là sử dụng kiểu suy luận).

Tôi chưa thực sự sẵn sàng để phát hành MiscUtil đầy đủ, nhưng đây là mã được cập nhật, bao gồm cả nhận xét. Tôi sẽ không viết lại chúng cho trình soạn thảo SO - chỉ cần sao chép toàn bộ.

(Tôi cũng có thể thiết kế lại API một chút về cách đặt tên nếu tôi bắt đầu từ đầu, nhưng tôi không muốn phá vỡ những người dùng hiện tại ...)

#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

Điều này không thành công khi sao chép vào một mục tiêu hiện có nếu mục tiêu không có hàm tạo riêng của nó, ví dụ: IMyType instance2 = new MyType (); Điều này là không thể tránh khỏi?
stovroz

@stovroz: Chà, bạn phải cung cấp loại mà bạn thực sự muốn khởi tạo.
Jon Skeet

@Jon: Nhưng mục tiêu đã được khởi tạo. Tôi nghĩ rằng các cải tiến ở trên của bạn chính xác là để cho phép sao chép vào một mục tiêu hiện có, nhưng có vẻ như nó muốn tạo một mục tiêu mới tại: Expression.MemberInit (Expression.New (typeof (TTarget)), bindings).
stovroz

@Jon: Tôi có lớp chứa thuộc tính List <>, nhưng nó không tạo ra thể hiện mới của List <>, vì vậy nếu tôi thêm thứ gì đó vào danh sách, nó sẽ thêm vào cả thuộc tính, nguồn và đích. Sẽ rất tuyệt nếu bạn cũng tạo một phiên bản mới của các kiểu tham chiếu trong các thuộc tính. Chỉ là một gợi ý :)
Andrija

2
@Cawas: À, xin lỗi - tôi đang xem các nhận xét trong thuật ngữ Stack Overflow :) Vì một điều tôi muốn đặt cho nó một tên danh từ , ví dụ PropertyCopier. Tôi sẽ phải suy nghĩ thêm một chút về nó và xem một số trường hợp sử dụng để thiết kế lại nó cho phù hợp, thành thật mà nói.
Jon Skeet

85

Bởi vì tôi tin rằng phiên bản của Jon quá phức tạp và phiên bản của Steve quá đơn giản, và tôi thích ý tưởng của Daniel về một lớp mở rộng.

Thêm vào đó, một phiên bản Chung khá đẹp nhưng không cần thiết vì tất cả các mục đều là đối tượng.

Tôi muốn tình nguyện phiên bản gầy và xấu tính của tôi. Tín dụng cho tất cả những điều trên. : D

Mã:

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

Sử dụng:

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

Vì tôi đã cảm thấy nhàm chán và một phiên bản linq được đề xuất bởi một bình luận

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

Haha, tôi thực sự đang nghĩ như vậy với chính mình khi tôi cuộn xuống câu trả lời của bạn. Giải pháp Goldilocks. Mặc dù, nếu tôi viết một cái thì chắc chắn nó sẽ sử dụng Linq :)
theMayer 13/1213

Tôi nhận được một ngoại lệ rỗng. Tôi nghĩ nó có liên quan gì đó đến thuộc tính chỉ đọc. Làm thế nào tôi có thể tránh điều đó?
Pedro77

Ok, giải quyết nó thêm ngay sau đó: "(targetProperty.GetSetMethod (true) = null && targetProperty.GetSetMethod (true) .IsPrivate!)"
Pedro77

23
Cần có một huy hiệu cho các câu trả lời có 20 phiếu bầu cho một câu hỏi mà Jon Skeet cũng đã trả lời.
Tony L.

Tại sao lại có "! TargetProperty.GetSetMethod (true) .IsPrivate"?
Rhyous

30

Đây là một phiên bản ngắn và ngọt ngào, vì bạn đã nói rằng cả hai đối tượng của bạn đều thuộc cùng một loại:

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

22

Dựa trên phương pháp của Steve, tôi đã sử dụng phương pháp mở rộng. Điều này sử dụng lớp cơ sở của tôi làm kiểu nhưng có thể sử dụng được ngay cả khi sử dụng đối tượng làm kiểu tham số. Hoạt động tốt cho mục đích sử dụng của tôi.

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

Cách sử dụng trông như thế này:

item1.CopyProperties(item2);

Bây giờ Item2 có dữ liệu thuộc tính giống như item1.


6
Đoạn mã trên sẽ bị hỏng nếu một trong các thuộc tính ở chế độ chỉ đọc. Thêm dấu kiểm: if (destinationPi.CanWrite) trước SetValue để tránh trường hợp ngoại lệ được ném ra.
J Wynia

8

Sửa đổi phiên bản Daniel để tránh ngoại lệ.

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

5

Phương thức Extension ngắn và đơn giản này sẽ cho phép bạn sao chép các thuộc tính phù hợp từ đối tượng này sang đối tượng khác với việc kiểm tra giá trị Null và có thể ghi được.

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

Về cơ bản vào năm 2019, chúng ta có thể nên sử dụng các tính năng ngôn ngữ cập nhật hơn, như cây biểu thức và biểu thức lambda đã biên dịch, thay vì Phản chiếu

Vì tôi không thể tìm thấy một "máy nhân bản nông" đáp ứng yêu cầu của tôi (hầu hết là tốc độ), tôi quyết định tự tạo một cái. Nó liệt kê tất cả các thuộc tính gettable / setting và sau đó tạo một Blockbiểu thức sau đó được biên dịch và lưu vào bộ nhớ đệm. Điều này làm cho nó nhanh hơn gần 13 lần so với AutoMapper phổ biến. Cách sử dụng rất đơn giản:

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

Bạn có thể xem toàn bộ nguồn tại đây: https://github.com/jitbit/PropMapper


1
Giấy phép MIT là một thứ vô ích ở đó .. đối với một chức năng nhỏ, toàn bộ giấy phép phải được đóng gói và vận hành.
Tối đa

@Max Tôi không thực sự giỏi trong việc này, tôi nên chọn giấy phép nào? Tôi luôn nghĩ MIT là đơn giản và dễ dãi nhất (như trong "làm bất cứ điều gì bạn muốn")
Alex

Nó là đơn giản và dễ dãi nhất. Tất cả những gì bạn phải làm là đưa giấy phép vào mã nguồn, điều này không khó.
nelsontruran

Không gian tên là gì? Đã cài đặt nó qua Nuget, VS không thể nhận bằng câu lệnh và readme cũng không nói điều đó.
Pawel

@Pawel Đó là "Jitbit.Utils" nhưng bạn có thể kiểm tra nguồn;)
Alex

3

Bạn có thể sử dụng tuần tự hóa để nhân bản sâu đối tượng:

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

Tất nhiên, các lớp học sẽ phải được đánh dấu là Có thể hóa nối tiếp.


Ngoài ra, nếu bạn muốn thêm một phụ thuộc vào JSON.NET có lợi cho một phụ thuộc vào Serializable, bạn có thể serialize và deserialize thành JSON
KyleMit

Thao tác này sẽ tạo một phiên bản mới, có thể không phải là những gì bạn định làm, nếu bạn chỉ muốn thay đổi giá trị thuộc tính của mình.
Matthieu

2

ICloneableobject.MemberwiseClone (bản sao cạn) (chúng tạo ra một đối tượng hoàn toàn mới, vì vậy có thể không đáp ứng yêu cầu của bạn).

Bạn có thể sử dụng phản chiếu để tự làm điều đó (kế thừa từ một lớp cơ sở để bạn không phải triển khai lại).

Hoặc bạn có thể mã tạo ra nó.


2

Trong vài năm, tôi đang sử dụng một thư viện phổ biến có tên 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

Lưu ý rằng ngay cả khi các giải pháp cơ bản khá đơn giản (xem các câu trả lời khác), có rất nhiều trường hợp cạnh (ví dụ: bản sao sâu, số liệu chung, giá trị null) và tối ưu hóa (ví dụ: bộ nhớ cache các thuộc tính được phản ánh) vì vậy tốt hơn nên sử dụng thư viện được duy trì cho điều đó.


1

Bạn có thể thử một cái gì đó như thế này ...

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

Có thể bài viết này hơi cũ, câu trả lời tốt nhưng khi bạn cần ánh xạ nhiều đối tượng vào một Loại mục tiêu - việc lặp qua các thuộc tính có thể tốn kém (Hình ảnh 100 đạo cụ và hơn thế nữa).

Tôi sử dụng phương thức AutoMapFactory này (chỉ yêu cầu LinQ), nó sẽ lặp lại chỉ một lần trong các thuộc tính và ánh xạ của mỗi đối tượng sẽ nhanh chóng (100000 đạo cụ / giây)

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

Làm thế nào để sử dụng cái này:

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

0

Nếu bạn muốn một cái gì đó giống như ApplyProperties, bạn có thể viết một phương thức mở rộng trên Object để làm những gì bạn cần. Chỉ cần nhận ra rằng một phương pháp mở rộng như vậy sẽ không "thuần túy" hoặc không có tác dụng phụ. Nhưng nếu bạn cần khả năng, đó là một cách để hoàn thành nó.


0

Mở rộng trên câu trả lời @Azerothians, tôi cần một số yêu cầu bổ sung:

  1. cần một cách để bỏ qua các thuộc tính null trên đối tượng nguồn
  2. cần một cách để bỏ qua các thuộc tính rõ ràng
  3. muốn một cách để cưỡng chế kiểu dữ liệu ở nơi có thể để khớp với ví dụ: int? sang số thập phân? hoặc long thành int (do các quyết định sơ đồ ban đầu không may từ người tiền nhiệm)
  4. muốn biết nếu thuộc tính đã được sao chép hay không (để xác định xem có cần cập nhật db hay không)

    /// <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
Vui lòng mô tả, vấn đề là gì và đoạn mã này sẽ giải quyết nó như thế nào, để giúp người đọc trong tương lai hiểu câu trả lời này. Và, queston là hơn 10 tuổi ...
FZS
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.