Ну, на ум приходят 4 метода.
(13.11.2008) Отредактировано: добавлен метод 4
Метод 1 :
Код для ручного извлечения каждого отдельного свойства из другого и сохранения его в текущем экземпляре объекта.
расквитаться:
- (относительно) Быстро
- Явный контроль над тем, какие свойства передаются (если вы
хотите исключить некоторые свойства)
- Явный контроль глубины и мелкости каждого свойства
Downsides:
- Техническое обслуживание
- Легко пропустить, когда будущий разработчик (может быть, вы, а может и нет) добавляет новое поле / свойство в класс
Это может выглядеть примерно так:
class MyObj
{
public void SetEquals(MyObj other)
{
if (object.ReferenceEquals(this, other)) return; // We are equal by reference, so do nothing.
if (other == null) return; // Throw ArgumentException? Up to you.
this.Property1 = other.Property1;
this.Property2 = other.Property2;
this.Property3 = other.Property3;
// ...
}
}
Метод 2 :
Напишите пользовательский класс-помощник отражения. Я бы, вероятно, сделал это статичным, используя несколько открытых статических методов, и внутренне сохранял отраженные типы и необходимые данные в словаре или чем-то, что было бы ключом Type, и содержал отраженную информацию, чтобы вы не повторяли отражение для одного и того же типа при каждом вызове. Это все равно будет дороже в вычислительном отношении при первом использовании его для любого данного типа, но впоследствии это будет быстрее, если вы используете какой-то кэш. Кроме того, вы можете посмотреть на создание пользовательского атрибута, который будет указывать вашему вспомогательному классу отражения для OverrrideObjectByValue для игнорирования определенных свойств / полей.
расквитаться
- Почти не требует обслуживания
- Может быть написано с атрибутами, которые вы украшаете свойства / поля
чтобы направлять отражатель на работу
Downsides
- Медленно (по крайней мере, для начального отражения, но если вы кешируете, это будет
быстрее)
- Сложно написать, если у вас мало или нет опыта с Reflection
- Запись его для поддержки глубоких и мелких копий вложенных типов во вложенных типах может стать изначально рекурсивной проблемой со сложными системами атрибутов, обеспечивающими гранулярный контроль глубоких и мелких случаев
Вы могли бы сделать это примерно так ...
using System.Reflection;
public static class OverrideObjectValues
{
private static Dictionary<Type, Tuple<PropertyInfo[], FieldInfo[]>> cachedLookup = new Dictionary<Type, Tuple<PropertyInfo[], FieldInfo[]>>
// Copies fields and properties from the right object into the left object.
// Could be extended to support attribute-level customization
// guiding this reflector on properties/fields to ignore,
// And whether to perform a deep or shallow copy of reference types
// for instance properties of types left and right.
public static void OverrideValues(object left, object right)
{
// They are equal by reference, we're done.
// This also handles the case that both left and right are null.
if (object.ReferenceEquals(left, right)) return;
// One or the other is null; we can't do this.
// Alternatively, throw an ArgumentException here?
if (left == null || right == null) return;
// The types mismatch; we can't do this.
// Alternatively, throw an ArgumentException here?
// Note: We could modify this to support the case where
// Left or Right inherits from the other, but that becomes
// more complex, and is beyond the scope of what
// you're asking for.
if (left.GetType() != right.GetType()) return;
Type leftType = left.GetType();
if (!cachedLookup.ContainsKey(leftType))
{
// Add type to cache
cachedLookup.Add(leftType, new Tuple<PropertyInfo[], FieldInfo[]>(leftType.GetProperties(), leftType.GetFields()));
}
// Iterate around each property, and copy-by-value from right into left.
// Do the same for each field, for the type we cached in the dictionary.
// You can add support to exclude properties/fields which are decorated
// with custom attributes. If you do support guiding by custom attributes,
// I'd exclude these types in the lookup/cache step in the dictionary before this point.
// You could even add support to differentiate between structs and classes,
// and do deep / shallow copies accordingly...
}
}
Метод 3 :
Если вы хотите переопределить экземпляр A
из MyObject
значениями для экземпляра B
из MyObject
, вы можете просто использовать оператор присваивания, чтобы буквально сделать их равными по ссылке. Имейте в виду: это означает, что это один и тот же экземпляр, что означает, что внесение изменений в A
отражается в B
, поскольку A
и B
являются одним и тем же объектом в памяти.
расквитаться
- быстрый
- Легче всего понять в будущем (при условии, что вы знаете, как работают ссылочные типы)
- Техническое обслуживание
Downsides
Это было бы так просто, как:
// Populate list of objects.
List<MyObj> objects = GetObjectsSomehow();
// Copy by reference object at index 4 over object at index 5.
objects[5] = objects[4];
Как вы хорошо знаете, этот пример для метода 3: , а не , глубокое копирование / перезапись исходных данных, а вместо этого создание двух (в этом случае я сохраняю их в списке) то же самое - по ссылке. Это особенно полезно, если объект является неизменным, поскольку это, как вы знаете, не нарушает весь принцип неизменности ...
Метод 4 :
(добавлен этот метод после комментария)
Этот метод на самом деле просто синтаксический сахар, и его лучше всего оставить в качестве оператора присваивания, но если вам действительно нужен метод по какой-то причине, вы могли бы сделать это ...
расквитаться
- Похоже, это то, что вы ищете?
- То же (в основном), что и при использовании метода 3, установка равных по ссылке, использование оператора присваивания ...
Downsides
- хак и ненужный подход при простом
a = b
; было бы достаточно ...
Некоторый произвольный пользовательский тип только с основными типами данных:
public class CustomType
{
public string Name { get; set; }
public int ID { get; set; }
}
И тогда у вас мог бы быть некоторый статический класс с методами расширения ...
public static class CopyUtilities
{
public static void MakeReferenceEqual<T>(this T left, ref T right) where T : class
{
if (object.ReferenceEquals(left, right)) return; // we're reference-equal, so be done.
right = left;
}
}
, который вы могли бы затем использовать так:
CustomType a = new CustomType();
a.ID = 42;
a.Name = "Myself";
CustomType b = null;
a.MakeReferenceEqual(ref b);
// a.ID == b.ID
// a.Name == b.Name
// a == b, by reference.