使用表达式交换两个对象的属性值的静态方法

时间:2018-11-17 00:09:21

标签: c# lambda reflection .net-4.5 expression-trees

我正在尝试制作一个实用程序函数,该函数可以交换两个lambda表达式给出的两个属性值-假设两个表达式都表示具有getter和setter的属性:

Swap(() => John.Lunch, () => Jimmy.Lunch);

我想该方法需要看起来像这样,但是我很难将其组合在一起。

private static void Swap<TProperty>(
    Expression<Func<TProperty>> first,
    Expression<Func<TProperty>> second)
{
    PropertyInfo firstProp = (PropertyInfo)((MemberExpression)first.Body).Member;
    PropertyInfo secondProp = (PropertyInfo)((MemberExpression)second.Body).Member;
    object firstObj = (((first.Body as MemberExpression).Expression as MemberExpression)
        .Expression as ConstantExpression).Value;
    object secondObj = (((second.Body as MemberExpression).Expression as MemberExpression)
        .Expression as ConstantExpression).Value;
    TProperty temp = (TProperty)firstProp.GetValue(firstObj);
    firstProp.SetValue(firstObj, secondProp.GetValue(secondObj));
    secondProp.SetValue(secondObj, temp);
}

尽管很确定有可能到达表达式的“主题”对象,但事实证明这很困难。

2 个答案:

答案 0 :(得分:1)

您可以使用private static void Swap<TProperty>( Expression<Func<TProperty>> first, Expression<Func<TProperty>> second) { var firstMember = first.Body as MemberExpression; var secondMember = second.Body as MemberExpression; var variable = Expression.Variable(typeof(TProperty)); var firstMemberAccess = Expression.MakeMemberAccess(firstMember.Expression, firstMember.Member); var secondMemberAccess = Expression.MakeMemberAccess(secondMember.Expression, secondMember.Member); var block = Expression.Block(new []{ variable }, Expression.Assign(variable, firstMemberAccess), Expression.Assign(firstMemberAccess, secondMemberAccess), Expression.Assign(secondMemberAccess, variable) ); Expression.Lambda<Action>(block).Compile()(); } 树编写交换本身:

class A { public int P { get; set; } }
class B { public int P2 { get; set; } }

var a = new A { P = 5 };
var b = new B { P2 = 10 };

Swap(() => a.P, () => b.P2);

示例:

 async function(username) {
   const data = await User.findOne({ username: username }).lean();
   return data.inventory;
 }

 socket.on('give inventory', async function() {
    try{
        console.log(connections[socket.id])
        const result = await itemmanage.getInventory(connections[socket.id]);
        console.log(result);
        socket.emit("get inventory", result);
    }catch(err){
       //code for handleing error 
    }
 });

答案 1 :(得分:0)

我试图编写尽可能具有自我解释性的代码,所以我将让评论进行讨论。该代码需要进行错误检查,但我将由您自己决定。这是最低工作量。我为Swapper选择了一个静态类,但是您可以选择用作实例对象,然后再使用DI容器。

public static class Swapper
{
    /// <summary>
    /// Key used for look-ups.
    /// </summary>
    private struct Key
    {
        private readonly Type _tt1;
        private readonly Type _tt2;
        private readonly MemberInfo _m11;
        private readonly MemberInfo _m12;

        public Key(Type t1, Type t2, MemberInfo m1, MemberInfo m2)
        {
            _tt1 = t1;
            _tt2 = t2;
            _m11 = m1;
            _m12 = m2;
        }

        public override bool Equals(object obj)
        {
            switch (obj)
            {
                case Key key:
                    return _tt1 == key._tt1 && _tt2 == key._tt2 && _m11 == key._m11 && _m12 == key._m12;

                default:
                    return false;
            }
        }

        public override int GetHashCode()
        {
            unchecked
            {
                var hashCode = (_tt1 != null ? _tt1.GetHashCode() : 0);
                hashCode = (hashCode * 397) ^ (_tt2 != null ? _tt2.GetHashCode() : 0);
                hashCode = (hashCode * 397) ^ (_m11 != null ? _m11.GetHashCode() : 0);
                hashCode = (hashCode * 397) ^ (_m12 != null ? _m12.GetHashCode() : 0);
                return hashCode;
            }
        }
    }

    /// <summary>
    /// This is the cache used for compiled actions. Because compiling is time consuming, I think it is better to cache.
    /// </summary>
    private static readonly ConcurrentDictionary<Key, object> CompiledActionsCache = new ConcurrentDictionary<Key, object>();

    /// <summary>
    /// Main Function which does the swapping
    /// </summary>
    public static void Swap<TSource, TDestination, TPropertySource>(TSource source, TDestination destination, Expression<Func<TSource, TPropertySource>> first, Expression<Func<TDestination, TPropertySource>> second)
    {
        //Try to get value from the cache or if it is cache miss then use Factory method to create Compiled Action
        var swapper = (Action<TSource, TDestination>)CompiledActionsCache.GetOrAdd(GetKey(first, second), k => Factory(first, second));
        //Do the actual swapping.
        swapper(source, destination);
    }

    /// <summary>
    /// Our factory method which does all the heavy lfiting fo creating swapping actions.
    /// </summary>
    private static Action<TSource, TDestination> Factory<TSource, TDestination, TPropertySource>(Expression<Func<TSource, TPropertySource>> first, Expression<Func<TDestination, TPropertySource>> second)
    {
        //////////////This is our aim/////////////
        //// var temp = obj1.Property;       /////
        //// obj1.Property = obj2.Property;  /////
        //// obj2.Property = temp;           /////
        //////////////////////////////////////////

        // Temp value for required for swapping
        var tempValue = Expression.Variable(typeof(TPropertySource), "temp_value");
        // Expression assignment
        // first.body is already accesing property, just use it as it is :)
        var assignToTemp = Expression.Assign(tempValue, first.Body);
        // Expression assignment
        // second.body is already accesing property, just use it as it is :)
        var secondToFirst = Expression.Assign(first.Body, second.Body);
        // Final switch here
        var tempToSecond = Expression.Assign(second.Body, tempValue);
        // Define an expression block which has all the above assignments as expressions
        var blockExpression = Expression.Block(new[] { tempValue }, assignToTemp, secondToFirst, tempToSecond);
        // An expression itself is not going to swap values unless it is compiled into a delegate
        // (obj1, obj2) => blockExpression from the previous line 
        return Expression.Lambda<Action<TSource, TDestination>>(blockExpression, first.Parameters[0], second.Parameters[0]).Compile();
    }

    /// <summary>
    /// Key creator method.
    /// </summary>
    private static Key GetKey<TSource, TDestination, TPropertySource>(Expression<Func<TSource, TPropertySource>> first, Expression<Func<TDestination, TPropertySource>> second)
    {
        var sourceType = typeof(TSource);
        var destinationType = typeof(TDestination);
        var sourcePropertyInfo = ((MemberExpression)first.Body).Member;
        var destinationPorpertyInfo = ((MemberExpression)second.Body).Member;
        return new Key(sourceType, destinationType, sourcePropertyInfo, destinationPorpertyInfo);
    }
}

让我们看看如何在行动中使用Swapper

public class From
{
    public byte FromProperty { get; set; }
}

public class To
{
    public byte ToProperty { get; set; }
}

var from = new From();
from.FromProperty = 5;
var to = new To();
Swapper.Swap(from, to, f => f.FromProperty, t => t.ToProperty);