如何在以后设置属性值

本文关键字:设置 属性 | 更新日期: 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);