Есть ли что-нибудь встроенное в .NET/С# для копирования значений между объектами?

Предположим, что у вас есть 2 класса:

public class ClassA {
    public int X { get; set; }
    public int Y { get; set; }
    public int Other { get; set; }
}

public class ClassB {
    public int X { get; set; }
    public int Y { get; set; }
    public int Nope { get; set; }
}

Теперь представьте, что у вас есть экземпляр каждого класса, и вы хотите скопировать значения из a в b. Есть ли что-то вроде MemberwiseClone, которое скопирует значения, в которых имена свойств совпадают (и, конечно, отказоустойчивы - у каждого есть get, а другой - и т.д.)?

var a = new ClassA(); var b = new classB();
a.CopyTo(b); // ??

Что-то вроде этого довольно легко на языке JavaScript.

Я предполагаю, что ответ отрицательный, но, возможно, есть и простая альтернатива. Я написал библиотеку отражений, чтобы сделать это, но если встроенный в С#/.NET на более низком уровне, вероятно, будет более эффективным (и зачем изобретать колесо).

Ответ 1

Нет ничего в каркасе для сопоставления объектных объектов, но там есть очень популярная библиотека, которая делает это: AutoMapper.

AutoMapper - это простая небольшая библиотека, созданная для обманчивой сложная проблема - избавление от кода, который сопоставил один объект с другой. Этот тип кода довольно скучный и скучный для написания, поэтому почему бы не придумать инструмент для этого?

Кстати, просто для обучения, вот простой способ реализовать то, что вы хотите. Я не тестировал его полностью, и он нигде не является надежным/гибким/исполненным как AutoMapper, но, надеюсь, есть что-то, чтобы выбраться из общей идеи:

public void CopyTo(this object source, object target)
{
    // Argument-checking here...

    // Collect compatible properties and source values
    var tuples = from sourceProperty in source.GetType().GetProperties()
                 join targetProperty in target.GetType().GetProperties() 
                                     on sourceProperty.Name 
                                     equals targetProperty.Name

                 // Exclude indexers
                 where !sourceProperty.GetIndexParameters().Any()
                    && !targetProperty.GetIndexParameters().Any()

                 // Must be able to read from source and write to target.
                 where sourceProperty.CanRead && targetProperty.CanWrite

                 // Property types must be compatible.
                 where targetProperty.PropertyType
                                     .IsAssignableFrom(sourceProperty.PropertyType)

                 select new
                 {
                     Value = sourceProperty.GetValue(source, null),
                     Property = targetProperty
                 };

    // Copy values over to target.
    foreach (var valuePropertyTuple in tuples)
    {
        valuePropertyTuple.Property
                          .SetValue(target, valuePropertyTuple.Value, null);

    }
}

Ответ 2

В .NET нет ничего подобного, о котором я знаю, но одна библиотека, способная это сделать (и многое другое), AutoMapper. Для вашего случая, что-то вроде:

_mapper.Map<A, B> (a, b);

Ответ 3

Насколько я знаю, этого не существует. То, как я это делаю, - это:

public static T DeepCopy(T oldclass)
{
    using (MemoryStream stream = new MemoryStream())
    {
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(stream, oldclass);
        ms.Position = 0;
        return (T)formatter.Deserialize(stream);
    }
}

Ответ 4

См. интерфейс System.ICloneable, а метод System.Object.MemberwiseClone(). Как отмечено в документах MSDN,

Метод MemberwiseClone создает мелкую копию, создавая новый объект, а затем копирует нестатические поля текущего объекта в новый объект. Если поле является типом значения, выполняется побитовая копия поля. Если поле является ссылочным типом, ссылка копируется, но упомянутый объект не является; поэтому исходный объект и его клон относятся к одному и тому же объекту.