模仿另一个对象上对象的属性/列表更改

时间:2010-03-16 03:20:56

标签: c# reflection path hierarchy

我需要模仿对象上的更改(属性/列表)更改,然后将其应用于另一个对象以保持结构/属性不变。

本质上它就像克隆等。商业规则要求某些属性不能应用于其他对象,所以我不能只是克隆对象,否则这很容易。

我已经走过源对象来获取INotifyPropertyChanged和IListChanged事件,所以我有“源”和args(属性或列表)更改了事件通知。

鉴于我想我可以构建一个反射“层次结构路径”,从源对象的顶层开始,以获取属性或列表更改的“源”(可能是几个级别的深度)。

忽略某些对象属性不应传播到另一个对象的时刻,构建此“路径”的方法是什么?如果我们没有击中原来改变的事件“源”,那么蛮力最高级别是建立“路径”(如果我们没有击中原来改变的事件“源”那么就会丢弃)吗?

关于如何模仿从一个对象到另一个对象的变化的任何聪明的想法?

1 个答案:

答案 0 :(得分:1)

有些库可以让它更容易(也更快)地使用反射。例如,Fasterflect允许您编写以下内容:

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

根据你的描述,听起来你需要一些递归的东西。我为Fasterflect写了一个DeepClone方法,我相信这个方法可以为你需要的东西提供一个很好的起点。要调整代码,您需要将其修改为(a)获取目标对象参数而不是在内部创建实例,(b)获取/设置属性而不是字段,以及(c)接受要包括的命名属性列表。

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

请注意私有方法上的map参数。这允许我跟踪创建的实例(或者在您的情况下,访问过),以便在遇到循环引用时我们可以做正确的事情。

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方法来加速实际反射,但如果您不想依赖库,那么这很容易替换。

免责声明:我作为贡献者参与了该项目。