簡體   English   中英

使用FieldInfo.SetValue與LINQ表達式在結構中設置字段

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM