2017-07-28 62 views
2

我正在使用ILGenerator創建一個函數,並在發出指定實例時設置屬性的值。這是我正在處理的解決方案的性能方面的主要原因。我有一個基本的代碼形式,它應該採用2個對象並通過IL加載它們以調用設置函數。我發現的問題是我通過設置的值似乎被忽略,另一個似乎產生的值被設置在它的位置。無法通過對象上的IL Emit設置某些類型值

這是LinqPad提出的基本用法的例子,我已經創建演示此問題:

void Main() 
{ 
    var instance = new TestClass 
    { 
     Id = new Guid("f0564ce7-f249-4105-8fc4-2c65cfe095f6"), 
     StringValue = "Something", 
     IntValue = 0 
    }; 

    MethodOne(instance); 
} 

private void MethodOne(TestClass instance) 
{ 
    var setStringMethod = GenerateMethodAssignment("StringValue"); 
    setStringMethod(instance, "Something Else"); 

    var setGuidMethod = GenerateMethodAssignment("Id"); 
    setGuidMethod(instance, new Guid("f8b0fae2-40bb-422a-815f-2300cceb4329")); 

    var setIntMethod = GenerateMethodAssignment("IntValue"); 
    setIntMethod(instance, 100); 
    instance.Dump(); 
} 

// Define other methods and classes here 
public class TestClass 
{ 
    public Guid Id { get; set; } 
    public string StringValue { get; set; } 
    public int IntValue { get; set; } 
} 

private Action<object, object> GenerateMethodAssignment(string propName) 
{ 
    var setMethod = typeof(TestClass).GetProperty(propName).GetSetMethod(); 
    var argTypes = new Type[] { typeof(object), typeof(object) }; 
    var method = new DynamicMethod(Guid.NewGuid().ToString(), null, argTypes, GetType(), true); 

    var ilGenerator = method.GetILGenerator(); 
    ilGenerator.Emit(OpCodes.Ldarg_0); 
    ilGenerator.Emit(OpCodes.Ldarg_1); 
    ilGenerator.Emit(OpCodes.Call, setMethod); 
    ilGenerator.Emit(OpCodes.Ret); 

    var action = (Action<object, object>)method.CreateDelegate(typeof(Action<object, object>)); 
    return action; 
} 

輸出: Value set on object

的字符串值被設置好的,但一個GUID或整型值不按我的預期設置。對於IL來說,我是相當新的,並且可能對代碼的期望過高,因爲代碼很簡單。

但即使如此,我注意到多次運行此代碼似乎以看似順序的順序生成IntValueId的值,所以我很好奇這些值是從哪裏來的。

回答

2

您正在創建的方法和委託正在接受類型爲object的參數。對於目標實例(因爲它是一個引用類型),對於value參數是可以的,當它是一個字符串時,但是當它是一個像int或guid這樣的值類型的時候並不好。您必須添加unbox指令才能將裝箱值類型轉換爲堆棧中的實際值。

Type propertyType = ...; 
var ilGenerator = method.GetILGenerator(); 
ilGenerator.Emit(OpCodes.Ldarg_0); 
ilGenerator.Emit(OpCodes.Ldarg_1); 
if (propertyType.IsValueType) 
    ilGenerator.Emit(OpCodes.Unbox, propertyType); 
ilGenerator.Emit(OpCodes.Call, setMethod); 
ilGenerator.Emit(OpCodes.Ret); 
+0

當然!沒有跨過我的想法,認爲這是理所當然的。謝謝! –

+0

由於某些原因,我不得不使用'OpCodes.Unbox_Any'而不是'OpCodes.Unbox'。我無法從文檔中完全弄清楚它,但它看起來像'Unbox_Any'執行一個額外的操作,'Unbox'沒有。 – Timo

相關問題