c#:致电盒装代表

时间:2015-05-20 15:30:44

标签: c# reflection delegates

在我的项目中,我需要在几个类之间转换数据,因此我创建了一个类DataMapper,用于两个不同类的属性的强类型映射。当需要修改对中的属性时,我会为此目的存储两个代理(转换器)。

然后DataMapper有两个方法Update(T source,S target)和Update(S source,T target),它们使用这些映射来提供转换。

public class DataMapper<TSourceType, TTargetType> : IDataUpdater<TSourceType, TTargetType> {

    private readonly IDictionary<PropertyInfo, PropertyInfo> _sourceToTargetMap = new Dictionary<PropertyInfo, PropertyInfo>();
    private readonly IDictionary<PropertyInfo, object> _converters = new Dictionary<PropertyInfo, object>();

    public DataMapper<TSourceType, TTargetType> Map<TSourceValue, TTargetValue>(
        Expression<Func<TSourceType, TSourceValue>> sourcePropExpr,
        Expression<Func<TTargetType, TTargetValue>> targetPropExpr) 
    {
        _sourceToTargetMap.Add(sourcePropExpr.AsPropertyInfo(), targetPropExpr.AsPropertyInfo());
        return this;
    }

    public DataMapper<TSourceType, TTargetType> Map<TSourceValue, TTargetValue>(
        Expression<Func<TSourceType, TSourceValue>> sourcePropExpr,
        Expression<Func<TTargetType, TTargetValue>> targetPropExpr,
        Func<TSourceValue, TTargetValue> sourceToTargetConverter, 
        Func<TTargetValue, TSourceValue> targetToSourceConverter) 
    {
        _sourceToTargetMap.Add(sourcePropExpr.AsPropertyInfo(), targetPropExpr.AsPropertyInfo());
        _converters.Add(sourcePropExpr.AsPropertyInfo(), sourceToTargetConverter);
        _converters.Add(targetPropExpr.AsPropertyInfo(), targetToSourceConverter);
        return this;
    }

    public void Update(TSourceType source, TTargetType target) {
        foreach (var keyValuePair in _sourceToTargetMap) {
            var sourceProp = keyValuePair.Key;
            var targetProp = keyValuePair.Value;
            Update(source, target, sourceProp, targetProp);
        }
    }

    public void Update(TTargetType source, TSourceType target) {
        foreach (var keyValuePair in _sourceToTargetMap) {
            var sourceProp = keyValuePair.Value;
            var targetProp = keyValuePair.Key;
            Update(source, target, sourceProp, targetProp);
        }
    }

    private void Update(
        object source, 
        object target, 
        PropertyInfo sourceProperty, 
        PropertyInfo targetProperty) 
    {
        var sourceValue = sourceProperty.GetValue(source);
        if (_converters.ContainsKey(sourceProperty)) {
            sourceValue = typeof(InvokeHelper<,>)
                .MakeGenericType(sourceProperty.PropertyType, targetProperty.PropertyType)
                .InvokeMember("Call", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, new[] { _converters[sourceProperty], sourceValue });
        }
        targetProperty.SetValue(target, sourceValue);
    }
}

以下是用法:

public SomeClass {
    private static readonly IDataUpdater<SomeClass, SomeOtherClass> _dataMapper = new DataMapper<SomeClass, SomeOtherClass>()
        .Map(x => x.PropertyA, y => y.PropertyAA)
        .Map(x => x.PropertyB, y => y.PropertyBB, x => Helper.Encrypt(x), y => Helper.Decrypt(y));

    public string PropertyA { get; set; }
    public string PropertyB { get; set; }

    public void LoadFrom(SomeOtherClass source) {
        _dataMapper.Update(source, this);
    }

    public void SaveTo(SomeOtherClass target) {
        _dataMapper.Update(this, target);
    }
}

你可以在类的DataHelper中看到方法Update的最后一次重载,当我想调用存储的转换器函数时,我使用了助手类InvokeHelper,因为我没有找到其他方法来调用盒装的委托Func。 InvokeHelper类的代码很简单 - 只需一个静态方法:

public static class InvokeHelper<TSource, TTarget> {
    public static TTarget Call(Func<TSource, TTarget> converter, TSource source) {
        return converter(source);
    }
}

有没有办法在没有反思的情况下怎么做?我需要优化这些转换以提高速度。

感谢。

2 个答案:

答案 0 :(得分:2)

您可以使用Delegate.DynamicInvoke来调用委托。或者,使用dynamic

((dynamic)(Delegate)_converters[sourceProperty])(sourceValue);

不需要(Delegate)演员表。它用于文档和运行时断言目的。如果你不喜欢它,请把它拿出来。

实际上,您最好在字典中使用delegate代替object

答案 1 :(得分:0)

如果是我,我会使用带表达式的小元编码来创建已编译和强类型委托的列表。当您调用Update方法时,您可以浏览列表中的每个Action并从来源更新目的地。

在Update调用之前,没有反射和所有编译等都完成了一次。

public class DataMapper<TSourceType, TTargetType> : IDataUpdater<TSourceType, TTargetType>
{
    List<Action<TSourceType, TTargetType>> _mappers = new List<Action<TSourceType, TTargetType>>();
    DataMapper<TTargetType, TSourceType> _reverseMapper;

    public DataMapper() : this(false) { }
    public DataMapper(bool isReverse)
    {
        if (!isReverse)
        {
            _reverseMapper = new DataMapper<TTargetType, TSourceType>(isReverse: true);
        }
    }

    public DataMapper<TSourceType, TTargetType> Map<TSourceValue, TTargetValue>(
        Expression<Func<TSourceType, TSourceValue>> sourcePropExpr,
        Expression<Func<TTargetType, TTargetValue>> targetPropExpr)
    {
        var mapExpression = Expression.Assign(targetPropExpr.Body, sourcePropExpr.Body);

        _mappers.Add(
            Expression.Lambda<Action<TSourceType, TTargetType>>(
                mapExpression,
                sourcePropExpr.Parameters[0],
                targetPropExpr.Parameters[0])
            .Compile());

        if (_reverseMapper != null) _reverseMapper.Map(targetPropExpr, sourcePropExpr);

        return this;
    }

    public DataMapper<TSourceType, TTargetType> Map<TSourceValue, TTargetValue>(
        Expression<Func<TSourceType, TSourceValue>> sourcePropExpr,
        Expression<Func<TTargetType, TTargetValue>> targetPropExpr,
        Func<TSourceValue, TTargetValue> sourceToTargetConverter,
        Func<TTargetValue, TSourceValue> targetToSourceConverter)
    {
        var convertedSourceExpression = Expression.Invoke(Expression.Constant(sourceToTargetConverter), sourcePropExpr.Body);
        var mapExpression = Expression.Assign(targetPropExpr.Body, convertedSourceExpression);

        _mappers.Add(
            Expression.Lambda<Action<TSourceType, TTargetType>>(
                mapExpression,
                sourcePropExpr.Parameters[0],
                targetPropExpr.Parameters[0])
            .Compile());

        if (_reverseMapper != null) _reverseMapper.Map(targetPropExpr, sourcePropExpr, targetToSourceConverter, sourceToTargetConverter);
        return this;
    }

    public void Update(TSourceType source, TTargetType target)
    {
        foreach (var mapper in _mappers)
        {
            mapper(source, target);
        }
    }

    public void Update(TTargetType source, TSourceType target)
    {
        if (_reverseMapper != null)
        {
            _reverseMapper.Update(source, target);
        }
        else
        {
            throw new Exception("Reverse mapper is null.  Did you reverse twice?");
        };
    }
}

通过获取传入的表达式并将它们用作新表达式的部分来构建表达式。

说你打电话给.Map(x => x.PropertyA, y => y.PropertyAA)。您现在有2个表达式,每个表达式都带有参数xy,每个表达式都包含正文x.PropertyAy.PropertyAA

现在,您希望将这些表达式部分重新组合为一个赋值表达式,如y.PropertyAA = x.PropertyA。这是在var mapExpression = Expression.Assign(targetPropExpr.Body, sourcePropExpr.Body);行中完成的,它为您提供了预期的表达式。

现在,当您调用Expression.Lambda时,您将参数(xy)合并到一个看起来像(x,y) = > y.PropertyAA = x.PropertyA的新表达式中。

在执行此操作之前,您需要编译它,因此.Compile()。但是,由于您只需要为任何给定的映射编译一次,您可以编译并存储结果。未编译的表达式的类型为Expression<Action<TSourceType,TTargetType>>,在编译后,结果类型为Action<TSourceType,TTargetType>