0

.NET 3.5 を使用して、VB.net アプリのプロパティの Set メソッドを呼び出す動的メソッドを作成するための次のコードがあります (ラムダ式スタイルに切り替えることはできません)。ここに投稿された例を使用して、Int64プロパティで機能しなかったため、関数に追加しました。主に、通常intの unbox 操作で呼び出された場合、無効なキャストエラーが発生するためです。そのため、それを処理するコードを追加しましたが、新しい問題が発生しました。64 ビットで実行している場合はすべて正常に動作しますが、32 ビット プロセスに変更するとすぐに、Int64プロパティのデリゲートを呼び出すと、AccessViolationException保護されたメモリの読み取りまたは書き込みが試行されます。String のような他のタイプは、正常に動作するようです。以下のコードを参照してください。何が間違っていますか?

Public Shared Sub SetFieldData(Instance As Object, PropInfo As PropertyInfo, value As Object)

        Dim Compiled As Action(Of Object, Object) = Nothing
        If Not _PropSetterCache.TryGetValue(PropInfo, Compiled) Then
            SyncLock _PropSetterCache
                If Not _PropSetterCache.TryGetValue(PropInfo, Compiled) Then

                    'http://jachman.wordpress.com/2006/08/22/2000-faster-using-dynamic-method-calls/
                    Dim setMethod = PropInfo.GetSetMethod()

                    Dim arguments As Type() = New Type(1) {}
                    arguments(0) = GetType(Object)
                    arguments(1) = arguments(0)

                    Dim setter As New DynamicMethod([String].Concat("_Set", PropInfo.Name, "_"), Nothing, arguments, PropInfo.DeclaringType)
                    Dim generator As ILGenerator = setter.GetILGenerator()
                    generator.Emit(OpCodes.Ldarg_0)

                    generator.Emit(OpCodes.Castclass, PropInfo.DeclaringType)

                    If PropInfo.PropertyType.IsClass Then
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Castclass, PropInfo.PropertyType)
                    ElseIf PropInfo.PropertyType Is GetType(Boolean) Then
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Unbox_Any, PropInfo.PropertyType)
                    ElseIf PropInfo.PropertyType.IsValueType Then
                        'my stuff, blog example doesn't cover the sent value being different type than the property



                        Dim LByte = generator.DefineLabel
                        Dim LInt = generator.DefineLabel
                        Dim LInt16 = generator.DefineLabel
                        Dim LInt32 = generator.DefineLabel
                        Dim LInt64 = generator.DefineLabel
                        Dim LSByte = generator.DefineLabel
                        Dim LUInt16 = generator.DefineLabel
                        Dim LUInt32 = generator.DefineLabel
                        Dim LUInt64 = generator.DefineLabel
                        Dim LDouble = generator.DefineLabel
                        Dim LSingle = generator.DefineLabel
                        Dim LElse = generator.DefineLabel
                        Dim LEnd = generator.DefineLabel


                        'byte
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Isinst, GetType(Byte))
                        generator.Emit(OpCodes.Brtrue, LByte)
                        'int
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Isinst, GetType(Integer))
                        generator.Emit(OpCodes.Brtrue, LInt)
                        'int16
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Isinst, GetType(Int16))
                        generator.Emit(OpCodes.Brtrue, LInt16)
                        'int32
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Isinst, GetType(Int32))
                        generator.Emit(OpCodes.Brtrue, LInt32)
                        'int64
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Isinst, GetType(Int64))
                        generator.Emit(OpCodes.Brtrue, LInt64)
                        'double 
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Isinst, GetType(Double))
                        generator.Emit(OpCodes.Brtrue, LDouble)
                        'short
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Isinst, GetType(Single))
                        generator.Emit(OpCodes.Brtrue, LSingle)
                        'sbyte
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Isinst, GetType(SByte))
                        generator.Emit(OpCodes.Brtrue, LSByte)
                        'uint16
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Isinst, GetType(UInt16))
                        generator.Emit(OpCodes.Brtrue, LUInt16)
                        'uint32
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Isinst, GetType(UInt32))
                        generator.Emit(OpCodes.Brtrue, LUInt32)
                        'uint64
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Isinst, GetType(UInt64))
                        generator.Emit(OpCodes.Brtrue, LUInt64)
                        'else
                        generator.Emit(OpCodes.Br, LElse)


                        '
                        generator.MarkLabel(LByte)
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Unbox_Any, GetType(Byte))
                        generator.Emit(OpCodes.Br, LEnd)
                        '
                        generator.MarkLabel(LInt)
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Unbox_Any, GetType(Integer))
                        generator.Emit(OpCodes.Br, LEnd)
                        '
                        generator.MarkLabel(LInt16)
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Unbox_Any, GetType(Int16))
                        generator.Emit(OpCodes.Br, LEnd)
                        '
                        generator.MarkLabel(LInt32)
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Unbox_Any, GetType(Int32))
                        generator.Emit(OpCodes.Br, LEnd)
                        '
                        generator.MarkLabel(LInt64)
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Unbox_Any, GetType(Int64))
                        generator.Emit(OpCodes.Br, LEnd)
                        '
                        generator.MarkLabel(LDouble)
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Unbox_Any, GetType(Double))
                        generator.Emit(OpCodes.Br, LEnd)
                        '
                        generator.MarkLabel(LSingle)
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Unbox_Any, GetType(Single))
                        generator.Emit(OpCodes.Br, LEnd)
                        '
                        generator.MarkLabel(LSByte)
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Unbox_Any, GetType(SByte))
                        generator.Emit(OpCodes.Br, LEnd)
                        '
                        generator.MarkLabel(LUInt16)
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Unbox_Any, GetType(UInt16))
                        generator.Emit(OpCodes.Br, LEnd)
                        '
                        generator.MarkLabel(LUInt32)
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Unbox_Any, GetType(UInt32))
                        generator.Emit(OpCodes.Br, LEnd)
                        '
                        generator.MarkLabel(LUInt64)
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Unbox_Any, GetType(UInt64))
                        generator.Emit(OpCodes.Br, LEnd)
                        '
                        generator.MarkLabel(LElse)
                        generator.Emit(OpCodes.Ldarg_1)
                        generator.Emit(OpCodes.Unbox_Any, PropInfo.PropertyType)
                        generator.Emit(OpCodes.Br, LEnd)

                        generator.MarkLabel(LEnd)


                    End If

                    generator.Emit(OpCodes.Callvirt, setMethod)
                    generator.Emit(OpCodes.Ret)

                    Compiled = setter.CreateDelegate(GetType(Action(Of Object, Object)))


                    _PropSetterCache.Add(PropInfo, Compiled)

                End If
            End SyncLock
        End If

        Compiled(Instance, value)

    End Sub
4

1 に答える 1

2

コードを .Net 4.5 用にコンパイルするとInvalidProgramException、32 ビットまたは 64 ビットとしてコンパイルされているかどうかに関係なく、 が得られます。これは、あるタイプの値をまったく異なるタイプのフィールドに単純に割り当てることはできないためです。コードをさらに複雑にする適切なキャストを含める必要があります。場合によってはコードが実際に機能するという事実は、.Net のバグ (新しいバージョンで修正されたもの) だと思います。

ただし、.Net 3.5 でも、式を使用してこれを行うことができます。を使用する代わりに、 set メソッドでAssign使用できます。Callそのメソッドを VB.NET (または C#) のメソッドとして呼び出すことはできませんが、式で呼び出すことはできます。

ただし、これが正しく機能するには、値を 2 回キャストする必要があります。最初にボックス化を解除し、次に目的の型にキャストします。ただし、そのため、生成されるコードは値の型に依存します。これは、キャッシュのキーを のようなものに変更する必要があることを意味しますTuple(Of PropertyInfo, Type)

コードは次のようになります。

Private Shared ReadOnly _PropSetterCache As Dictionary(Of Tuple(Of PropertyInfo, Type), Action(Of Object, Object)) _
    = New Dictionary(Of Tuple(Of PropertyInfo, Type), Action(Of Object, Object))

Public Shared Sub SetFieldData(instance As Object, propInfo As PropertyInfo, value As Object)

    Dim compiled As Action(Of Object, Object) = Nothing
    Dim key = New Tuple(Of PropertyInfo, Type)(propInfo, If(value IsNot Nothing, value.GetType(), GetType(Object)))
    If Not _PropSetterCache.TryGetValue(key, compiled) Then
        SyncLock _PropSetterCache
            If Not _PropSetterCache.TryGetValue(key, compiled) Then
                Dim setMethod = propInfo.GetSetMethod()

                Dim instanceParameter = Expression.Parameter(GetType(Object), "instance")
                Dim castedInstance = Expression.Convert(instanceParameter, propInfo.DeclaringType)

                Dim valueParameter = Expression.Parameter(GetType(Object), "value")
                Dim valueCastedOnce = Expression.Convert(valueParameter, If(value IsNot Nothing, value.GetType(), GetType(Object)))
                Dim valueCastedTwice = Expression.Convert(valueCastedOnce, propInfo.PropertyType)

                Dim callExpression = Expression.Call(castedInstance, setMethod, valueCastedTwice)

                Dim lambda = Expression.Lambda(Of Action(Of Object, Object))(callExpression, instanceParameter, valueParameter)

                compiled = lambda.Compile()

                _PropSetterCache.Add(key, compiled)

            End If
        End SyncLock
    End If

    compiled(instance, value)

End Sub

.Net 3.5 には がありませんがTuple、自分で作成できます。

NotInheritable Class Tuple(Of T1, T2)
    Implements IEquatable(Of Tuple(Of T1, T2))
    Private ReadOnly _value1 As T1

    Public ReadOnly Property Value1 As T1
        Get
            Return _value1
        End Get
    End Property

    Private ReadOnly _value2 As T2

    Public ReadOnly Property Value2 As T2
        Get
            Return _value2
        End Get
    End Property

    Public Sub New(ByVal value1 As T1, ByVal value2 As T2)
        _value1 = value1
        _value2 = value2
    End Sub

    '' following code generated by R#
    Public Overloads Function Equals(ByVal other As Tuple(Of T1, T2)) As Boolean Implements IEquatable(Of Tuple(Of T1, T2)).Equals
        If ReferenceEquals(Nothing, other) Then Return False
        If ReferenceEquals(Me, other) Then Return True
        Return EqualityComparer(Of T1).[Default].Equals(_value1, other._value1) AndAlso EqualityComparer(Of T2).[Default].Equals(_value2, other._value2)
    End Function

    Public Overloads Overrides Function Equals(ByVal obj As Object) As Boolean
        If ReferenceEquals(Nothing, obj) Then Return False
        If ReferenceEquals(Me, obj) Then Return True
        Return TypeOf obj Is Tuple(Of T1, T2) AndAlso Equals(DirectCast(obj, Tuple(Of T1, T2)))
    End Function

    Public Overrides Function GetHashCode() As Integer
        Dim hashCode = EqualityComparer(Of T1).[Default].GetHashCode(_value1)
        hashCode = CInt((hashCode * 397L) Mod Integer.MaxValue) Xor EqualityComparer(Of T2).[Default].GetHashCode(_value2)
        Return hashCode
    End Function
End Class
于 2013-01-15T16:18:14.473 に答える