如何在以后设置属性值
本文关键字:设置 属性 | 更新日期: 2023-09-27 18:20:45
我正在寻找一种机制,该机制允许我只有在满足某些条件时才推迟设置参数的后备字段。我一直在仔细考虑这个设计,直到遇到问题,因为它需要在lambda表达式中使用ref
参数。有没有一种方法可以做到这一点,而不需要将ref参数放在lambda中?
protected bool isRunning = false;
List<Action> argumentSetters = new List<Action>();
// the reason for the delegate and following subroutine
// is to define an action which takes a ref parameter
protected delegate void setArgByRef<T>(ref T arg, T value);
protected void setArgByRefSub<T>(ref T arg, T value)
{
arg = value;
}
protected int _setPoint;
public int SetPoint
{
get { return _setPoint; }
set { setValue(ref _setPoint, value); }
}
public void Run()
{
isRunning = true;
// time consuming code here
// don't want SetPoint to be allowed to change
// while isRunning == true
isRunning = false;
// set SetPoint and other properties after
argumentSetters.ForEach((a) => a.Invoke());
}
protected void setValue<T>(ref T arg, T value)
{
setArgByRef<T> a = new setArgByRef<T>(setArgByRefSub<T>);
if (isRunning)
// cannot use ref parameter inside a lambda
{ argumentSetters.Add(() => a.Invoke(ref arg, value)); }
else
{ arg = value; }
}
我能想到的最好的解决方案是使用Expression
。这里的想法是存储属性持有对象、属性信息和要设置的值,然后在准备好时进行设置。
从我写的另一个答案中提取一点,你可以有一个从表达式中获得PropertyInfo
的函数:
public static PropertyInfo GetPropertyInfo<TIn, TOut>(Expression<Func<TIn, TOut>> PropertyExpression)
{
MemberExpression memberExpr;
switch (PropertyExpression.Body.NodeType)
{
case ExpressionType.MemberAccess:
memberExpr = (MemberExpression)PropertyExpression.Body;
break;
case ExpressionType.Convert:
memberExpr = (MemberExpression)((UnaryExpression)PropertyExpression.Body).Operand;
break;
default:
throw new NotSupportedException();
}
var property = (PropertyInfo)memberExpr.Member;
return property;
}
然后你可以写一组东西来设置:
private static readonly List<Tuple<object, PropertyInfo, object>> _ToSet = new List<Tuple<object, PropertyInfo, object>>();
然后根据需要添加到该列表中。
public static void AddPendingSet<TType, TProperty>(TType obj, Expression<Func<TType, TProperty>> expr, TProperty val)
{
var prop = GetPropertyInfo(expr);
_ToSet.Add(new Tuple<object, PropertyInfo, object>(obj, prop, val);
}
您甚至可以将其分为两种方法,并在需要时直接传递PropertyInfo
。这可能会派上用场,具体取决于您的实现。
而且,当你需要设置它们时:
foreach (var v in _ToSet)
{
v.Item2.SetValue(v.Item1, v.Item3);
}
当然,如果更合适的话,您也可以取出obj
参数,只使用this
。如果这成为现实世界中的代码,我会很想不使用Tuple
,因为它有点乱,但我在这里使用它是为了使这个例子最小化和完整。当然,这两种方式都应该奏效。
这样做的问题是,它不会像您正在使用的字段那样工作,但您可以设置一个属性并使用this
,这应该会起作用。也许还有一种方法可以让表达式与字段一起工作,我只是从来没有需要这样做,所以我不确定。
为了更好地衡量,您可以这样调用AddPendingState
方法。我编了TypeA
作为例子。
AddPendingState<TypeA, int>(instance, c => c.PropertyName, 2);