Eigenschaftswerte von einem Objekt auf ein anderes des gleichen Typs automatisch anwenden?

78

Bei 2 Objekten A und B vom Typ T möchte ich die Werte der Eigenschaften in A denselben Eigenschaften in B zuweisen, ohne für jede Eigenschaft eine explizite Zuweisung vorzunehmen.

Ich möchte Code wie folgt speichern:

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

so etwas tun

a.ApplyProperties(b);

Ist es möglich?

eKek0
quelle
Gibt es eine Lösung, die Reflection nicht verwendet?
Tharindu Sathischandra

Antworten:

75

Ich habe einen Typ in MiscUtilaufgerufen, PropertyCopyder etwas Ähnliches tut - obwohl er eine neue Instanz des Zieltyps erstellt und die Eigenschaften in diesen kopiert.

Die Typen müssen nicht identisch sein - es werden lediglich alle lesbaren Eigenschaften vom Typ "Quelle" in den Typ "Ziel" kopiert. Wenn die Typen gleich sind, funktioniert das natürlich eher :) Es ist übrigens eine flache Kopie.

Im Codeblock am Ende dieser Antwort habe ich die Funktionen der Klasse erweitert. Zum Kopieren von einer Instanz in eine andere PropertyInfowerden zur Ausführungszeit einfache Werte verwendet. Dies ist langsamer als die Verwendung eines Ausdrucksbaums. Die Alternative wäre jedoch, eine dynamische Methode zu schreiben, auf die ich nicht allzu scharf bin. Wenn die Leistung für Sie absolut kritisch ist, lassen Sie es mich wissen und ich werde sehen, was ich tun kann. Um die Methode zu verwenden, schreiben Sie etwas wie:

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

PropertyCopy.Copy(instance1, instance2);

(Wo Copyist eine generische Methode, die mit Typinferenz aufgerufen wird).

Ich bin nicht wirklich bereit, eine vollständige MiscUtil-Version zu erstellen, aber hier ist der aktualisierte Code, einschließlich der Kommentare. Ich werde sie nicht für den SO-Editor neu verpacken - kopiere einfach den ganzen Teil.

(Ich würde wahrscheinlich auch die API in Bezug auf die Benennung ein wenig neu gestalten, wenn ich von vorne anfangen würde, aber ich möchte bestehende Benutzer nicht brechen ...)

#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
Jon Skeet
quelle
Dies schlägt beim Kopieren auf ein vorhandenes Ziel fehl, wenn das Ziel keinen eigenen Konstruktor hat, z. B. IMyType instance2 = new MyType (); Ist das unvermeidlich?
Stovroz
@stovroz: Nun, Sie müssten den Typ angeben, den Sie tatsächlich instanziieren wollten.
Jon Skeet
@ Jon: Aber das Ziel ist bereits instanziiert. Ich dachte, Ihre obigen Verbesserungen sollten genau das Kopieren auf ein vorhandenes Ziel ermöglichen, aber es scheint trotzdem ein neues Ziel erstellen zu wollen: Expression.MemberInit (Expression.New (typeof (TTarget)), Bindungen).
Stovroz
@Jon: Ich habe eine Klasse, die List <> -Eigenschaften enthält, aber keine neue Instanz von List <> erstellt. Wenn ich also etwas zur Liste hinzufüge, werden sowohl Eigenschaften als auch Quelle und Ziel hinzugefügt. Es wäre schön, auch in Eigenschaften eine neue Instanz von Referenztypen zu erstellen. Nur ein Vorschlag :)
Andrija
2
@Cawas: Ah, sorry - ich habe mir die Kommentare in der Stack Overflow-Terminologie angesehen :) Zum einen würde ich ihm einen Substantivnamen geben , z PropertyCopier. Um ehrlich zu sein, müsste ich ein bisschen mehr darüber nachdenken und einige Anwendungsfälle sehen, um es richtig zu gestalten.
Jon Skeet
84

Weil ich glaube, dass Jons Version ein bisschen zu kompliziert ist und Steves Version zu einfach ist und ich Daniels Idee einer Erweiterungsklasse mag.

Außerdem ist eine generische Version hübsch, aber unnötig, da alle Elemente Objekte sind.

Ich möchte meine schlanke und gemeine Version freiwillig zur Verfügung stellen. Dank an alle oben genannten. : D.

Code:

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

Verwendung:

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

Da ich gelangweilt war und eine Linq-Version durch einen Kommentar vorgeschlagen wurde

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);
        }
    }
}
Azerothian
quelle
Haha, ich habe diesen Gedanken buchstäblich für mich selbst gedacht, als ich zu Ihrer Antwort gescrollt habe. Die Goldlöckchen-Lösung. Obwohl, wenn ich eine schreiben würde, würde es definitiv Linq verwenden :)
theMayer
Ich erhalte eine Null-Ausnahme. Ich denke, es hat etwas mit einer schreibgeschützten Eigenschaft zu tun. Wie kann ich das vermeiden?
Pedro77
Ok, das Problem wurde gelöst, indem Folgendes hinzugefügt wurde: "(targetProperty.GetSetMethod (true)! = Null &&! TargetProperty.GetSetMethod (true) .IsPrivate)"
Pedro77
23
Es sollte ein Abzeichen für Antworten geben, die 20 Stimmen zu einer Frage haben, die auch Jon Skeet beantwortet hat.
Tony L.
Warum die "! TargetProperty.GetSetMethod (true) .IsPrivate"?
Rhyous
29

Hier ist eine kurze und süße Version, da Sie sagten, dass beide Objekte vom gleichen Typ sind:

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

Aufbauend auf Steves Methode habe ich mich für den Ansatz der Erweiterungsmethode entschieden. Dies verwendet meine Basisklasse als Typ, sollte aber auch unter Verwendung von Objekt als Parametertyp verwendbar sein. Funktioniert hervorragend für meine Zwecke.

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

Die Verwendung sieht folgendermaßen aus:

item1.CopyProperties(item2);

Jetzt hat Item2 die gleichen Eigenschaftsdaten wie Item1.

Daniel
quelle
5
Der obige Code wird unterbrochen, wenn eine der Eigenschaften schreibgeschützt ist. Fügen Sie vor dem SetValue eine Prüfung hinzu: if (destinationPi.CanWrite), um zu vermeiden, dass die Ausnahme ausgelöst wird.
J Wynia
7

Änderung der Daniel-Version, um Ausnahmen zu vermeiden.

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

Diese kurze und einfache Erweiterungsmethode ermöglicht es Ihnen, übereinstimmende Eigenschaften mit der Prüfung des Nullwerts von einem Objekt in ein anderes zu kopieren und ist beschreibbar.

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);
        }
    }
Ashdeep Singh
quelle
4

Grundsätzlich sollten wir 2019 wahrscheinlich aktuellere Sprachfunktionen wie Ausdrucksbäume und kompilierte Lambda-Ausdrücke anstelle von Reflection verwenden

Da ich keinen "flachen Kloner" finden konnte, der meinen Anforderungen entspricht (vor allem Geschwindigkeit), habe ich beschlossen, selbst einen zu erstellen. Es listet alle gettable / Settable-Eigenschaften auf und erstellt dann einen BlockAusdruck, der dann kompiliert und zwischengespeichert wird. Damit ist es fast 13-mal schneller als der beliebte AutoMapper. Die Verwendung ist sehr einfach:

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

Die vollständige Quelle finden Sie hier: https://github.com/jitbit/PropMapper

Alex
quelle
1
Die MIT-Lizenz ist dort eine Art Patzer. Für eine kleine Funktionalität muss die gesamte Lizenz gebündelt und gewartet werden.
Max
@ Max Ich bin nicht wirklich gut darin, welche Lizenz soll ich wählen? Ich dachte immer, MIT sei das einfachste und freizügigste (wie in "Mach was du willst")
Alex
Es ist das einfachste und freizügigste. Alles was Sie tun müssen, ist die Lizenz in den Quellcode aufzunehmen, das ist nicht schwierig.
Nelsontruran
Was ist der Namespace? VS hat es über Nuget installiert und kann es nicht mit einer Anweisung abrufen, und Readme sagt das auch nicht.
Pawel
@Pawel Es ist "Jitbit.Utils", aber Sie können die Quelle überprüfen;)
Alex
3

Sie können die Serialisierung verwenden, um das Objekt tief zu klonen:

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

Klassen müssten natürlich nur als serialisierbar markiert werden.

Andrija
quelle
Wenn Sie eine Abhängigkeit von JSON.NET zugunsten einer Abhängigkeit von hinzufügen möchten Serializable, können Sie JSON
KyleMit
Dadurch wird eine neue Instanz erstellt, die möglicherweise nicht Ihren Vorstellungen entspricht, wenn Sie nur den Wert Ihrer Eigenschaften ändern möchten.
Matthieu
2

Es gibt ICloneable und object.MemberwiseClone (flache Kopie) (diese erstellen ein ganz neues Objekt und erfüllen möglicherweise nicht Ihre Anforderungen).

Sie können Reflection verwenden, um es selbst zu tun (von einer Basisklasse erben, damit Sie es nicht erneut implementieren müssen).

Oder Sie könnten Code generieren.

Cade Roux
quelle
2

Seit einigen Jahren verwende ich eine beliebte Bibliothek mit dem Namen 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

Beachten Sie, dass selbst wenn die grundlegenden Lösungen ziemlich einfach sind (siehe andere Antworten), es viele Randfälle (z. B. Deep Copy, Generics, Nullwerte) und Optimierungen (z. B. Cache der reflektierten Eigenschaften) gibt, sodass Sie die gepflegte Bibliothek besser verwenden können.

Aviko
quelle
1

Sie könnten so etwas versuchen ...

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);
}
Steve
quelle
1

Vielleicht ist dieser Beitrag etwas alt, die Antworten sind gut, aber wenn Sie viele Objekte einem Zieltyp zuordnen müssen, kann das Durchlaufen von Eigenschaften kostspielig sein (Imaging 100 Requisiten und mehr).

Ich verwende diese AutoMapFactory-Methode (nur LinQ erforderlich), sie iteriert nur einmal in den Eigenschaften und die Zuordnung jedes Objekts ist schnell (100000 Requisiten / Sekunden).

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

Wie man das benutzt:

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

Wenn Sie so etwas wie ApplyProperties möchten, können Sie eine Erweiterungsmethode für Object schreiben, die genau das tut, was Sie benötigen. Stellen Sie nur fest, dass eine solche Erweiterungsmethode nicht "rein" oder nebenwirkungsfrei wäre. Aber wenn Sie die Fähigkeit benötigen, ist es eine Möglichkeit, dies zu erreichen.

jrista
quelle
0

Als ich die Antwort von @Azerothians erweiterte, brauchte ich einige zusätzliche Anforderungen:

  1. brauchte eine Möglichkeit, Null-Eigenschaften für das Quellobjekt zu ignorieren
  2. brauchte eine Möglichkeit, explizite Eigenschaften zu ignorieren
  3. wollte eine Möglichkeit, den Datentyp so weit wie möglich zu erzwingen, z. B. int? zu dezimal? oder long to int (aufgrund unglücklicher früher Schemaentscheidungen eines Vorgängers)
  4. wollte wissen, ob Eigenschaften kopiert wurden oder nicht (um festzustellen, ob ein Datenbank-Update erforderlich wäre)

    /// <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;
    }
    
Justin
quelle
-3
public TestClass {
    public TestName {get;set;}
}
public void submain()
{
    var originalTestClass = new TestClass()
    {
        TestName  ="Test Name";
    };

    var newTestClass = new TestClass();
     newTestClass.CopyPropertiesFrom(originalTestClass);
}
Malik Waqas
quelle
3
Bitte beschreiben Sie, was das Problem war und wie dieses Snippet es lösen wird, damit zukünftige Leser diese Antwort verstehen. Und die Queston ist mehr als 10 Jahre alt ...
FZs