我正在寻找一种机制,只有在满足某些条件时才允许我推迟参数的后备字段设置。我曾经想过设计,直到遇到障碍,因为它需要在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; }
}
答案 0 :(得分:3)
我能想到的最佳解决方案是Expression
。那里的想法是存储属性保持对象,属性信息和要设置的值,然后在你准备好时设置它。
从我写的另一个answer中抽出一点,你可以有一个函数从表达式中获取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);