我想使用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);
变量指定目标的type
,Type
变量是field
,指定要设置的字段。
这适用于引用类型,但如果目标是结构,那么这个东西会将目标作为副本传递给setter委托并在副本上设置值,而不是像原始目标上那样设置值我想要。 (至少这是我的想法。)
另一方面,
FieldInfo
效果很好,即使对于结构也是如此。
为了使用编译的表达式设置目标字段,我能做些什么吗?
答案 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使用指针直接修改该结构。