如何使用DynamicMethod.CreateDelegate返回包含实例对象的字段值

时间:2014-08-07 16:59:46

标签: .net vb.net dynamicmethod

正如问题所述,我有兴趣使用.net的动态功能来缓存对象的字段getter / setter并在运行时调用它。

使用以下信息: Is there a way to create a delegate to get and set values for a FieldInfo?

我已经组建了一个类和函数来设置我需要的功能:

Public Class c1
    Public someField As Integer 'we will Get the value of this dynamically
End Class

Public Function CreateGetter(Of S, T)(ByVal strFieldName As String) As Func(Of S, T)
    'creates a function to return the value
    Dim objFieldInfo As FieldInfo
    Dim strMethodName As String
    Dim objGetterMethod As DynamicMethod
    Dim objGen As ILGenerator
    objFieldInfo = GetType(S).GetField(strFieldName)
    strMethodName = Convert.ToString(objFieldInfo.ReflectedType.FullName) & ".get_" & Convert.ToString(objFieldInfo.Name)
    objGetterMethod = New DynamicMethod(strMethodName, GetType(T), New Type(0) {GetType(S)}, True)
    objGen = objGetterMethod.GetILGenerator()
    If objFieldInfo.IsStatic = False Then
        objGen.Emit(OpCodes.Ldarg_0)
        objGen.Emit(OpCodes.Ldfld, objFieldInfo)
    Else
        objGen.Emit(OpCodes.Ldsfld, objFieldInfo)
    End If
    objGen.Emit(OpCodes.Ret)
    Return DirectCast(objGetterMethod.CreateDelegate(GetType(Func(Of S, T))), Func(Of S, T))
End Function

我用以下代码称之为好的代码:

Dim getValue = CreateGetter(Of c1, Integer)("someField")
Dim someValue As Integer
someValue = getValue(o1)

然而,我所困扰的部分是如何修改函数CreateGetter以便能够以缓存的形式使用它,如:(缓存实例对象)

Dim getValue = CreateGetter(Of c1, Integer)(o1,"someField")
Dim someValue As Integer
someValue = getValue()

我意识到这可能需要对CreateGetter中的IL代码进行一些修改,但这是我遇到的棘手问题。

1 个答案:

答案 0 :(得分:1)

实际上,您无法使用单一方法执行此操作。要永久保存对象,您需要在某个对象中使用一个字段,然后创建一个指向该对象上某个方法的委托。

你可以使用Reflection.Emit做所有这些,但那将是乏味的。相反,您可以利用编译器已经可以执行此操作来创建闭包的事实:您不是直接返回DynamicMethod委托,而是返回调用该委托的lambda:

Dim fieldAccessor = DirectCast(objGetterMethod.CreateDelegate(GetType(Func(Of S, T))), Func(Of S, T))

Return Function() fieldAccessor(obj)

另一种选择是将表达式树用于所有这些。这样做的好处是您不必处理IL,这可能很难做到。类似的东西:

Function CreateGetter(Of S, T)(obj as S, fieldName As String) As Func(Of T)
    Dim expr = Expression.Lambda(Of Func(Of T))(
        Expression.Field(Expression.Constant(obj), fieldName))
    Return expr.Compile()
End Function

此版本仅适用于实例字段,我认为对于静态字段,不使用obj的重载更有意义。