如何设置局部变量memberExpression值

时间:2018-02-28 09:41:19

标签: c# reflection expression

我想获取参数原始名称并更新其值。我怎么能这样做?

public void SetMember<T>(Expression<Func<T>> memberExpression)
{
    var body = (MemberExpression)memberExpression.Body;
    var name = body.Member.Name; //text

    //can I set variable value here 
}

static void Main(string[] args)
{
    var text="test";
    SetMember(() => text);
}

1 个答案:

答案 0 :(得分:5)

是的,可以,在对象中捕获局部变量,该对象将作为常量存储在表达式树中。

您可以编译一个新方法来设置捕获字段的值:

public static void SetMember<T>(Expression<Func<T>> memberExpression, T newVlaue)
{
    var body = (MemberExpression)memberExpression.Body;
    var name = body.Member.Name; //text
    var newValueParam = Expression.Parameter(typeof(T));
    var newBody = Expression.Assign(body, newValueParam);

    var setter = Expression.Lambda<Action<T>>(newBody, newValueParam).Compile();
    setter(newVlaue); // Set with the new value 
}

或者你可以使用常数值的反映

public static void SetMember<T>(Expression<Func<T>> memberExpression, T newVlaue)
{
    var body = (MemberExpression)memberExpression.Body;
    var name = body.Member.Name; //text
    var constant = body.Expression as ConstantExpression;

    (body.Member as FieldInfo).SetValue(constant.Value, newVlaue);
}

注意仅仅因为你可以这样做并不意味着你应该这样做。这取决于变量捕获的内部结构,并且可能随着编译器的新实现而改变。并且不要认为它不会发生,当C#编译器切换到Roslyn时,是否有一些匿名函数被转换为静态函数或实例方法会破坏一些“聪明的代码”。我写了,所以请自担风险。

如果您只想设置局部变量,则更简洁的方法是使用out / ref参数;如果由于某种原因需要变量的名称,则使用nameof,这将更快更清洁,更可靠,但确实需要您在两个地方使用局部变量。

public static void SetMember<T>(ref T local, T newValue, string nameOfLocal)
{
    local = newValue;
    // nameofLocal can be used ..
}

static void Main(string[] args)
{
    var text = "test";
    SetMember(ref text, "new value", nameof(text));
    Console.Write(text);
}