Имитировать изменения свойств / списка на объекте на другом объекте - PullRequest
0 голосов
/ 16 марта 2010

Мне нужно имитировать изменения (свойство / список) изменений объекта, а затем применить их к другому объекту, чтобы сохранить структуру / свойство неизменными.

По сути это похоже на клонирование и т. Д. Правила biz требуют, чтобы определенные свойства не применялись к другому объекту, поэтому я не могу просто клонировать объект, иначе это будет легко.

Я уже прошел исходный объект, чтобы получить события INotifyPropertyChanged и IListChanged, поэтому я изменил уведомления о событиях "source" и args (Property или List).

Учитывая, что, я думаю, я мог бы построить отражающий "путь иерархии", начиная с верхнего уровня исходного объекта, чтобы добраться до измененного свойства или списка "исходный" (который может иметь несколько уровней глубины).

Не обращая внимания на момент, что определенные свойства объекта не должны распространяться на другой объект, как можно построить этот «путь»? Является ли верхний уровень грубой силы вниз, чтобы построить «путь» (и отбрасывать на обратном пути, если мы не попали в исходное измененное событие «источник»), единственный способ сделать это?

Какие-нибудь умные идеи о том, как имитировать изменения от одного объекта к другому?

1 Ответ

1 голос
/ 22 марта 2010

Доступны библиотеки, облегчающие (и ускоряющие) работу с отражением. Например, Fasterflect позволяет написать следующее:

// copy only the specified named properties
source.MapProperties( target, "FirstName", "LastName" );

Из вашего описания звучит так, будто вам нужно что-то рекурсивное. Я написал метод DeepClone для Fasterflect, который, я считаю, послужит хорошей отправной точкой для того, что вам нужно. Чтобы адаптировать код, вам нужно изменить его так, чтобы (а) принимать параметр целевого объекта вместо внутреннего создания экземпляра, (б) получать / задавать свойства вместо полей и (в) принимать список именованных свойств для включения.

public static T DeepClone<T>( this T source ) where T : class, new()
{
    return source.DeepClone( null );
}

Обратите внимание на параметр карты в приватном методе. Это позволяет мне отслеживать экземпляры, созданные (или, в вашем случае, посещенные), чтобы мы могли поступать правильно, встречая циклические ссылки.

private static T DeepClone<T>( this T source, Dictionary<object, object> map )
    where T : class, new()
{
    Type type = source.GetType();
    IList<FieldInfo> fields = type.Fields( Flags.StaticInstanceAnyVisibility );
    var clone = type.CreateInstance() as T;
    map = map ?? new Dictionary<object, object>();
    map[ source ] = clone;
    object[] values = fields.Select( f => GetValue( f, source, map ) ).ToArray();
    for( int i = 0; i < fields.Count; i++ )
    {
        fields[ i ].Set( clone, values[ i ] );
    }
    return clone;
}

private static object GetValue( FieldInfo field, object source, Dictionary<object, object> map )
{
    object result = field.Get( source );
    object clone;
    if( map.TryGetValue( result, out clone ) )
    {
        return clone;
    }
    bool follow = result != null && result.GetType().IsClass && result.GetType() != typeof(string);
    return follow ? result.DeepClone( map ) : result;
}

Обратите внимание, что в коде используются другие методы Fasterflect для ускорения реального отражения, но если вы не хотите зависеть от библиотеки, это легко заменить.

Отказ от ответственности: я участвую в указанном проекте в качестве участника.

...