views:

66

answers:

1

I need to mimic changes (property/list) changes on an object and then apply it to another object to keep the structure/property the same.

In essence it's like cloning etc. the biz rules require certain properties to not be applied to the other object, so I can't just clone the object otherwise this would be easy.

I've already walked the source object to get INotifyPropertyChanged and IListChanged events, so I have the "source" and the args (Property or List) changed event notifications.

Given that I guess I could build a reflection "hierarchy path" starting from the top level of the source object to get to the Property or List changed "source" (which could be several levels deep).

Ignoring for the moment that certain object properties should not propagate to the other object, what's a way to build this "path"? Is a brute force top level down to build the "path" (and discard on the way back up if we don't hit the original changed event "source") the only way to do it?

Any clever ideas on how to mimic changes from one object to another object?

+1  A: 

There are libraries available to make it easier (and faster) to work with reflection. For instance, Fasterflect allows you to write the following:

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

From your description it sounds like you need something recursive though. I wrote a DeepClone method for Fasterflect, which I believe would make a good starting point for what you need. To adapt the code you'd need to modify it to (a) take a target object parameter instead of creating the instance internally, (b) get/set properties instead of fields and (c) accept a list of named properties to include.

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

Note the map parameter on the private method. This allows me to keep track of instances created (or in your case, visited) so that we can do the right thing when encountering cyclic references.

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

Note that the code uses other Fasterflect methods to speed up the actual reflection, but if you don't want to depend on the library then this is easily replaceable.

Disclaimer: I am involved in said project as a contributor.

Morten Mertner
nice thank you!
soundslike