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

У меня есть тип в MiscUtil под названием .cs-file PropertyCopy, который делает нечто подобное, хотя c#-language создает новый экземпляр целевого linq-to-sql типа и копирует в него свойства.

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

В блоке кода sql-to-linq внизу этого ответа я расширил c# возможности класса. Для копирования c# из одного экземпляра в другой c-sharp он использует простые значения csharp PropertyInfo во время выполнения - это c#.net медленнее, чем использование visual-c# дерева выражений, но альтернативой c#.net было бы написать динамический .cs-file метод, который мне не очень visual-c# нравится. Если производительность csharp для вас абсолютно критична, дайте visual-c# мне знать, и я посмотрю, что linq-to-sql я могу сделать. Чтобы использовать c# метод, напишите что-нибудь c#.net вроде:

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

PropertyCopy.Copy(instance1, instance2);

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

Я .cs-file не совсем готов к выпуску c#.net полной версии MiscUtil, но linq2sql вот обновленный код, включая c# комментарии. Я не собираюсь visual-c# перематывать их для редактора c-sharp SO - просто скопируйте весь linq-to-sql кусок.

(Я бы также, вероятно, немного c#-language изменил дизайн API с точки c#-language зрения именования, если бы c#.net я начинал с нуля, но я не linq-to-sql хочу нарушать работу существующих c#.net пользователей ...)

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

namespace MiscUtil.Reflection
{
    /// 
    /// Non-generic class allowing properties to be copied from one instance
    /// to another existing instance of a potentially different type.
    /// 
    public static class PropertyCopy
    {
        /// 
        /// 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.
        /// 
        /// Only the properties of the source and target types themselves
        /// are taken into account, regardless of the actual types of the arguments.
        /// Type of the source
        /// Type of the target
        /// Source to copy properties from
        /// Target to copy properties to
        public static void Copy(TSource source, TTarget target)
            where TSource : class
            where TTarget : class
        {
            PropertyCopier.Copy(source, target);
        }
    }

    /// 
    /// 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.
    /// 
    public static class PropertyCopy where TTarget : class, new()
    {
        /// 
        /// Copies all readable properties from the source to a new instance
        /// of TTarget.
        /// 
        public static TTarget CopyFrom(TSource source) where TSource : class
        {
            return PropertyCopier.Copy(source);
        }
    }

    /// 
    /// 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.
    /// 
    internal static class PropertyCopier
    {
        /// 
        /// 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.
        /// 
        private static readonly Func creator;

        /// 
        /// 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...
        /// 
        private static readonly List sourceProperties = new List();
        private static readonly List targetProperties = new List();
        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 BuildCreator()
        {
            ParameterExpression sourceParameter = Expression.Parameter(typeof(TSource), "source");
            var bindings = new List();
            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>(initializer, sourceParameter).Compile();
        }
    }
}
#endif

c#

linq-to-sql

2022-11-13T09:13:50+00:00