如何在以后设置属性值

时间:2014-09-04 20:34:16

标签: c# lambda ref

我正在寻找一种机制,只有在满足某些条件时才允许我推迟参数的后备字段设置。我曾经想过设计,直到遇到障碍,因为它需要在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; }
}

1 个答案:

答案 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);