我正在尝试制作一个实用程序函数,该函数可以交换两个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);
}
尽管很确定有可能到达表达式的“主题”对象,但事实证明这很困难。
答案 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);