使用FieldInfo.SetValue与LINQ表达式在结构中设置字段

时间:2015-08-22 16:35:22

标签: c# .net reflection linq-expressions

我想使用LINQ表达式设置私有字段。我有这段代码:

<select ng-model="person.department" 
   ng-options="department.id as department.name for department in departmentList">
</select>

这会编译一个带有两个对象的委托,即目标和值:

//parameter "target", the object on which to set the field `field`
ParameterExpression targetExp = Expression.Parameter(typeof(object), "target");

//parameter "value" the value to be set in the `field` on "target"
ParameterExpression valueExp = Expression.Parameter(typeof(object), "value");

//cast the target from object to its correct type
Expression castTartgetExp = Expression.Convert(targetExp, type);

//cast the value to its correct type
Expression castValueExp = Expression.Convert(valueExp, field.FieldType);

//the field `field` on "target"
MemberExpression fieldExp = Expression.Field(castTartgetExp, field);

//assign the "value" to the `field` 
BinaryExpression assignExp = Expression.Assign(fieldExp, castValueExp);

//compile the whole thing
var setter = Expression.Lambda<Action<object, object>> (assignExp, targetExp, valueExp).Compile();

setter(someObject, someValue); 变量指定目标的typeType变量是field,指定要设置的字段。

这适用于引用类型,但如果目标是结构,那么这个东西会将目标作为副本传递给setter委托并在副本上设置值,而不是像原始目标上那样设置值我想要。 (至少这是我的想法。)

另一方面,

FieldInfo

效果很好,即使对于结构也是如此。

为了使用编译的表达式设置目标字段,我能做些什么吗?

1 个答案:

答案 0 :(得分:5)

对于值类型,请使用Expression.Unbox代替Expression.Convert

//cast the target from object to its correct type
Expression castTartgetExp = type.IsValueType
    ? Expression.Unbox(targetExp, type)
    : Expression.Convert(targetExp, type);

以下是演示:.NET Fiddle

问:setter方法没有ref参数。它如何更新原始结构?

A:虽然没有ref关键字,值类型通常按值传递并因此被复制,但target参数的类型为{ {1}}。如果参数是一个盒装结构,那么该框的引用将通过(按值)传递给该方法。

现在,使用纯C#来改变盒装结构是不可能的,因为C#取消装箱转换总是产生盒装值的副本。但 可能使用IL或Reflection:

object

问:如果LINQ表达式使用Expression.Convert,为什么setter不起作用?

A: Expression.Convert编译为unbox.any IL指令,该指令返回public struct S { public int I; } public void M(object o, int i) { // ((S)o).I = i; // DOESN'T COMPILE typeof(S).GetField("I").SetValue(o, i); } public void N() { S s = new S(); object o = s; // create a boxed copy of s M(o, 1); // mutate o (but not s) Console.WriteLine(((S)o).I); // "1" Console.WriteLine(s.I); // "0" M(s, 2); // mutate a TEMPORARY boxed copy of s (BEWARE!) Console.WriteLine(s.I); // "0" } 引用的结构的副本。然后,setter更新此副本(随后将其丢弃)。

问:为什么Expression.Unbox会解决问题?

A: Expression.Unbox(当用作Expression.Assign的目标时)编译为unbox IL指令,该指令将指针返回给由target引用的结构。然后,setter使用指针直接修改该结构。