[英]Using FieldInfo.SetValue vs LINQ expressions to set a field in a struct
我想使用LINQ表達式設置私有字段。 我有這個代碼:
//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
變量是FieldInfo
,它指定要設置的字段。
這適用於引用類型,但如果目標是結構,那么這個東西會將目標作為副本傳遞給setter委托並在副本上設置值,而不是像我想要的那樣在原始目標上設置值。 (至少這是我的想法。)
另一方面,
field.SetValue(someObject, someValue);
工作得很好,即使是結構。
為了使用編譯的表達式設置目標字段,我能做些什么嗎?
對於值類型,請使用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
參數。 它如何更新原始結構?
答:雖然沒有ref
關鍵字,值類型通常按值傳遞並因此被復制,但這里target
參數的類型是object
。 如果參數是盒裝結構,則對該方框的引用 (按值)傳遞給該方法。
現在,使用純C#來改變盒裝結構是不可能的,因為C#取消裝箱轉換總是產生盒裝值的副本。 但是有可能使用IL或Reflection:
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"
}
問:如果LINQ表達式使用Expression.Convert,為什么setter不起作用?
答: Expression.Convert編譯為unbox.any
IL指令,該指令返回target
引用的struct的副本 。 然后,setter更新此副本(隨后將其丟棄)。
問:為什么Expression.Unbox會解決問題?
答: Expression.Unbox(當用作Expression.Assign的目標時)編譯為unbox
IL指令,該指令返回指向 target
引用的結構的指針 。 然后,setter使用指針直接修改該結構。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.