all groups > dotnet clr > october 2006 >
You're in the dotnet clr group:
Emit.Call failing on non-value types
dotnet clr:
Im currently updating my dynamic comparer class (which you can read about on codeproject) but I've run into a slight problem which I hope you guys can help me with... My current emit code looks similar to this: il.EmitCall(OpCodes.Call, member.DefaultComparerProperty.GetGetMethod(), null); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, member.FieldInfo); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldfld, member.FieldInfo); il.EmitCall(OpCodes.Callvirt, member.DefaultComparerCompareMethod, null); Now, "member" is just a custom class containing various properties for easy access to defaul comparers, field types etc. for the member that needs to be compared. "DefaultComparerProperty" is the PropertyInfo for the Comparer<x>.Default property. "DefaultComparerCompareMethod" is the MethodInfo for the "Compare" method on the default comparer for the member's type. My problem is that if the member is a value type this setup works, but if it is a reference type it fails with the following exception: System.InvalidOperationException: Failed to compare two elements in the array. ---> System.MethodAccessException: System.Collections.Generic.Comparer`1.Compare(System.__Canon, System.__Canon) at DynamicComparison(Person , Person ) at DynamicComparer.DynamicComparerB`1.CompareMethodInvoker.Invoke(T itemA, T itemB) at DynamicComparer.DynamicComparerB`1.Compare(T itemA, T itemB) in C:\SomePath\DynamicComparer.cs:line 44 at System.Array.FunctorComparer`1.Compare(T x, T y) at System.Collections.Generic.ArraySortHelper`1.QuickSort[TValue](T[] keys, TValue[] values, Int32 left, Int32 right, IComparer`1 comparer) --- End of inner exception stack trace --- at System.Collections.Generic.ArraySortHelper`1.QuickSort[TValue](T[] keys, TValue[] values, Int32 left, Int32 right, IComparer`1 comparer) at System.Collections.Generic.ArraySortHelper`1.Sort[TValue](T[] keys, TValue[] values, Int32 index, Int32 length, IComparer`1 comparer) at System.Collections.Generic.ArraySortHelper`1.Sort(T[] items, Int32 index, Int32 length, IComparer`1 comparer) at System.Array.Sort[T](T[] array, Int32 index, Int32 length, IComparer`1 comparer) at System.Collections.Generic.List`1.Sort(Comparison`1 comparison) at DynamicMethod.Sample.Program.Main(String[] args) in C:\somePath\Program.cs:line 69 Please note the first line which indicates that instead of calling the default generic comparer's compare method using the type "Address" (or whatever ref type is passed in) it is using the type "System.__Canon" (ie. "Compare(System.__Canon, System.__Canon)" instead of "Compare(Address, Address)")?! Where does this "__Canon" class come from. Please help! -- Johannes Hansen
Thanks for the reply Ben but the MethodInfo for the "Compare" method is taken from the correct instance type of the comparer. I've extracted the IL for the working hard-coded method body and the IL for the failing dynamic method body. The dynamic method IL was extracted using Haibo Luo's Dynamic Method Visualizer and the IL for the hard-coded comparer was extracted using Lutz Roeder's Reflector, this might account for some of the syntactical differences but I'm not sure. Dynamic IL: L_0005: call System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] get_Default()/System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] L_000a: ldarg.0 L_000b: ldfld DynamicMethod.Sample.Address address/DynamicMethod.Sample.Person L_0010: ldarg.1 L_0011: ldfld DynamicMethod.Sample.Address address/DynamicMethod.Sample.Person L_0016: callvirt Int32 Compare(DynamicMethod.Sample.Address, DynamicMethod.Sample.Address)/System.Collections.Generic.GenericComparer`1[DynamicMethod.Sample.Address] Hard-coded (target) IL: L_0005: call [mscorlib]System.Collections.Generic.Comparer`1<!0> [mscorlib]System.Collections.Generic.Comparer`1<DynamicMethod.Sample.Address>::get_Default() L_000a: ldarg.0 L_000b: ldfld DynamicMethod.Sample.Address DynamicMethod.Sample.Person::address L_0010: ldarg.1 L_0011: ldfld DynamicMethod.Sample.Address DynamicMethod.Sample.Person::address L_0016: callvirt instance int32 [mscorlib]System.Collections.Generic.Comparer`1<DynamicMethod.Sample.Address>::Compare(!0, !0) Please remember that the failing code only fails when arg0 and arg1 is reference types and not value types. I belive the error is thrown at the final line (L_0016). -- Johannes Hansen System Consultant, frontAvenue A/S [quoted text, click to view] "Ben Voigt" wrote: > > "Johannes Hansen" <JohannesHansen@discussions.microsoft.com> wrote in > message news:55D373A7-C0C5-4F29-ADBE-5BF707098490@microsoft.com... > > Im currently updating my dynamic comparer class (which you can read about > > on > > codeproject) but I've run into a slight problem which I hope you guys can > > help me with... > > > > My current emit code looks similar to this: > > > > il.EmitCall(OpCodes.Call, member.DefaultComparerProperty.GetGetMethod(), > > null); > > il.Emit(OpCodes.Ldarg_0); > > il.Emit(OpCodes.Ldfld, member.FieldInfo); > > il.Emit(OpCodes.Ldarg_1); > > il.Emit(OpCodes.Ldfld, member.FieldInfo); > > il.EmitCall(OpCodes.Callvirt, member.DefaultComparerCompareMethod, null); > > > > Now, "member" is just a custom class containing various properties for > > easy > > access to defaul comparers, field types etc. for the member that needs to > > be > > compared. "DefaultComparerProperty" is the PropertyInfo for the > > Comparer<x>.Default property. "DefaultComparerCompareMethod" is the > > MethodInfo for the "Compare" method on the default comparer for the > > member's > > type. > > > > My problem is that if the member is a value type this setup works, but if > > it > > is a reference type it fails with the following exception: > > > > System.InvalidOperationException: Failed to compare two elements in the > > array. ---> System.MethodAccessException: > > System.Collections.Generic.Comparer`1.Compare(System.__Canon, > > System.__Canon) > > at DynamicComparison(Person , Person ) > > at DynamicComparer.DynamicComparerB`1.CompareMethodInvoker.Invoke(T > > itemA, T itemB) > > at DynamicComparer.DynamicComparerB`1.Compare(T itemA, T itemB) in > > C:\SomePath\DynamicComparer.cs:line 44 > > at System.Array.FunctorComparer`1.Compare(T x, T y) > > at System.Collections.Generic.ArraySortHelper`1.QuickSort[TValue](T[] > > keys, TValue[] values, Int32 left, Int32 right, IComparer`1 comparer) > > --- End of inner exception stack trace --- > > at System.Collections.Generic.ArraySortHelper`1.QuickSort[TValue](T[] > > keys, TValue[] values, Int32 left, Int32 right, IComparer`1 comparer) > > at System.Collections.Generic.ArraySortHelper`1.Sort[TValue](T[] keys, > > TValue[] values, Int32 index, Int32 length, IComparer`1 comparer) > > at System.Collections.Generic.ArraySortHelper`1.Sort(T[] items, Int32 > > index, Int32 length, IComparer`1 comparer) > > at System.Array.Sort[T](T[] array, Int32 index, Int32 length, > > IComparer`1 > > comparer) > > at System.Collections.Generic.List`1.Sort(Comparison`1 comparison) > > at DynamicMethod.Sample.Program.Main(String[] args) in > > C:\somePath\Program.cs:line 69 > > > > Please note the first line which indicates that instead of calling the > > default generic comparer's compare method using the type "Address" (or > > whatever ref type is passed in) it is using the type "System.__Canon" (ie. > > "Compare(System.__Canon, System.__Canon)" instead of "Compare(Address, > > Address)")?! Where does this "__Canon" class come from. Please help! > > > > Can you write the dynamic assembly to disk and look at it with .NET > Reflector? > > Whenever I've had problems with a reference to the wrong method, it's been > because I either used a plain MemberInfo (i.e. result of GetMethod()) gotten > before I called CreateType, or used a MethodBuilder (ConstructorBuilder, > PropertyBuilder) after calling CreateType. > > > -- > > Johannes Hansen > > > > System Consultant, frontAvenue A/S > >
I just saw that I'm apparently calling compare on a class called "GenericComparer<T>"... However, all my declaring types and such tells me that I am using Comparer<T>. So when I'm using a value type the IL emitted looks like this: IL_0005: call System.Collections.Generic.Comparer`1[System.Int32] get_Default()/System.Collections.Generic.Comparer`1[System.Int32] IL_000a: ldarg.0 IL_000b: ldfld Int32 age/DynamicMethod.Sample.Person IL_0010: ldarg.1 IL_0011: ldfld Int32 age/DynamicMethod.Sample.Person IL_0016: callvirt Int32 Compare(Int32, Int32)/System.Collections.Generic.Comparer`1[System.Int32] But when I'm using a reference type the emitted IL looks like this: L_0005: call System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] get_Default()/System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] L_000a: ldarg.0 L_000b: ldfld DynamicMethod.Sample.Address address/DynamicMethod.Sample.Person L_0010: ldarg.1 L_0011: ldfld DynamicMethod.Sample.Address address/DynamicMethod.Sample.Person L_0016: callvirt Int32 Compare(DynamicMethod.Sample.Address, DynamicMethod.Sample.Address)/System.Collections.Generic.GenericComparer`1[DynamicMethod.Sample.Address] Does anyone know what this is about? -- Johannes Hansen System Consultant, frontAvenue A/S [quoted text, click to view] "Johannes Hansen" wrote: > Thanks for the reply Ben but the MethodInfo for the "Compare" method is taken > from the correct instance type of the comparer. I've extracted the IL for the > working hard-coded method body and the IL for the failing dynamic method > body. The dynamic method IL was extracted using Haibo Luo's Dynamic Method > Visualizer and the IL for the hard-coded comparer was extracted using Lutz > Roeder's Reflector, this might account for some of the syntactical > differences but I'm not sure. > > Dynamic IL: > L_0005: call > System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] > get_Default()/System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] > L_000a: ldarg.0 > L_000b: ldfld DynamicMethod.Sample.Address > address/DynamicMethod.Sample.Person > L_0010: ldarg.1 > L_0011: ldfld DynamicMethod.Sample.Address > address/DynamicMethod.Sample.Person > L_0016: callvirt Int32 Compare(DynamicMethod.Sample.Address, > DynamicMethod.Sample.Address)/System.Collections.Generic.GenericComparer`1[DynamicMethod.Sample.Address] > > Hard-coded (target) IL: > L_0005: call [mscorlib]System.Collections.Generic.Comparer`1<!0> > [mscorlib]System.Collections.Generic.Comparer`1<DynamicMethod.Sample.Address>::get_Default() > L_000a: ldarg.0 > L_000b: ldfld DynamicMethod.Sample.Address > DynamicMethod.Sample.Person::address > L_0010: ldarg.1 > L_0011: ldfld DynamicMethod.Sample.Address > DynamicMethod.Sample.Person::address > L_0016: callvirt instance int32 > [mscorlib]System.Collections.Generic.Comparer`1<DynamicMethod.Sample.Address>::Compare(!0, !0) > > Please remember that the failing code only fails when arg0 and arg1 is > reference types and not value types. I belive the error is thrown at the > final line (L_0016). > > -- > Johannes Hansen > > System Consultant, frontAvenue A/S > > > "Ben Voigt" wrote: > > > > > "Johannes Hansen" <JohannesHansen@discussions.microsoft.com> wrote in > > message news:55D373A7-C0C5-4F29-ADBE-5BF707098490@microsoft.com... > > > Im currently updating my dynamic comparer class (which you can read about > > > on > > > codeproject) but I've run into a slight problem which I hope you guys can > > > help me with... > > > > > > My current emit code looks similar to this: > > > > > > il.EmitCall(OpCodes.Call, member.DefaultComparerProperty.GetGetMethod(), > > > null); > > > il.Emit(OpCodes.Ldarg_0); > > > il.Emit(OpCodes.Ldfld, member.FieldInfo); > > > il.Emit(OpCodes.Ldarg_1); > > > il.Emit(OpCodes.Ldfld, member.FieldInfo); > > > il.EmitCall(OpCodes.Callvirt, member.DefaultComparerCompareMethod, null); > > > > > > Now, "member" is just a custom class containing various properties for > > > easy > > > access to defaul comparers, field types etc. for the member that needs to > > > be > > > compared. "DefaultComparerProperty" is the PropertyInfo for the > > > Comparer<x>.Default property. "DefaultComparerCompareMethod" is the > > > MethodInfo for the "Compare" method on the default comparer for the > > > member's > > > type. > > > > > > My problem is that if the member is a value type this setup works, but if > > > it > > > is a reference type it fails with the following exception: > > > > > > System.InvalidOperationException: Failed to compare two elements in the > > > array. ---> System.MethodAccessException: > > > System.Collections.Generic.Comparer`1.Compare(System.__Canon, > > > System.__Canon) > > > at DynamicComparison(Person , Person ) > > > at DynamicComparer.DynamicComparerB`1.CompareMethodInvoker.Invoke(T > > > itemA, T itemB) > > > at DynamicComparer.DynamicComparerB`1.Compare(T itemA, T itemB) in > > > C:\SomePath\DynamicComparer.cs:line 44 > > > at System.Array.FunctorComparer`1.Compare(T x, T y) > > > at System.Collections.Generic.ArraySortHelper`1.QuickSort[TValue](T[] > > > keys, TValue[] values, Int32 left, Int32 right, IComparer`1 comparer) > > > --- End of inner exception stack trace --- > > > at System.Collections.Generic.ArraySortHelper`1.QuickSort[TValue](T[] > > > keys, TValue[] values, Int32 left, Int32 right, IComparer`1 comparer) > > > at System.Collections.Generic.ArraySortHelper`1.Sort[TValue](T[] keys, > > > TValue[] values, Int32 index, Int32 length, IComparer`1 comparer) > > > at System.Collections.Generic.ArraySortHelper`1.Sort(T[] items, Int32 > > > index, Int32 length, IComparer`1 comparer) > > > at System.Array.Sort[T](T[] array, Int32 index, Int32 length, > > > IComparer`1 > > > comparer) > > > at System.Collections.Generic.List`1.Sort(Comparison`1 comparison) > > > at DynamicMethod.Sample.Program.Main(String[] args) in > > > C:\somePath\Program.cs:line 69 > > > > > > Please note the first line which indicates that instead of calling the > > > default generic comparer's compare method using the type "Address" (or > > > whatever ref type is passed in) it is using the type "System.__Canon" (ie. > > > "Compare(System.__Canon, System.__Canon)" instead of "Compare(Address, > > > Address)")?! Where does this "__Canon" class come from. Please help! > > > > > > > Can you write the dynamic assembly to disk and look at it with .NET > > Reflector? > > > > Whenever I've had problems with a reference to the wrong method, it's been > > because I either used a plain MemberInfo (i.e. result of GetMethod()) gotten > > before I called CreateType, or used a MethodBuilder (ConstructorBuilder, > > PropertyBuilder) after calling CreateType. > > > > > --
"Johannes Hansen" <JohannesHansen@discussions.microsoft.com> wrote in message news:55D373A7-C0C5-4F29-ADBE-5BF707098490@microsoft.com... [quoted text, click to view] > Im currently updating my dynamic comparer class (which you can read about > on > codeproject) but I've run into a slight problem which I hope you guys can > help me with... > > My current emit code looks similar to this: > > il.EmitCall(OpCodes.Call, member.DefaultComparerProperty.GetGetMethod(), > null); > il.Emit(OpCodes.Ldarg_0); > il.Emit(OpCodes.Ldfld, member.FieldInfo); > il.Emit(OpCodes.Ldarg_1); > il.Emit(OpCodes.Ldfld, member.FieldInfo); > il.EmitCall(OpCodes.Callvirt, member.DefaultComparerCompareMethod, null); > > Now, "member" is just a custom class containing various properties for > easy > access to defaul comparers, field types etc. for the member that needs to > be > compared. "DefaultComparerProperty" is the PropertyInfo for the > Comparer<x>.Default property. "DefaultComparerCompareMethod" is the > MethodInfo for the "Compare" method on the default comparer for the > member's > type. > > My problem is that if the member is a value type this setup works, but if > it > is a reference type it fails with the following exception: > > System.InvalidOperationException: Failed to compare two elements in the > array. ---> System.MethodAccessException: > System.Collections.Generic.Comparer`1.Compare(System.__Canon, > System.__Canon) > at DynamicComparison(Person , Person ) > at DynamicComparer.DynamicComparerB`1.CompareMethodInvoker.Invoke(T > itemA, T itemB) > at DynamicComparer.DynamicComparerB`1.Compare(T itemA, T itemB) in > C:\SomePath\DynamicComparer.cs:line 44 > at System.Array.FunctorComparer`1.Compare(T x, T y) > at System.Collections.Generic.ArraySortHelper`1.QuickSort[TValue](T[] > keys, TValue[] values, Int32 left, Int32 right, IComparer`1 comparer) > --- End of inner exception stack trace --- > at System.Collections.Generic.ArraySortHelper`1.QuickSort[TValue](T[] > keys, TValue[] values, Int32 left, Int32 right, IComparer`1 comparer) > at System.Collections.Generic.ArraySortHelper`1.Sort[TValue](T[] keys, > TValue[] values, Int32 index, Int32 length, IComparer`1 comparer) > at System.Collections.Generic.ArraySortHelper`1.Sort(T[] items, Int32 > index, Int32 length, IComparer`1 comparer) > at System.Array.Sort[T](T[] array, Int32 index, Int32 length, > IComparer`1 > comparer) > at System.Collections.Generic.List`1.Sort(Comparison`1 comparison) > at DynamicMethod.Sample.Program.Main(String[] args) in > C:\somePath\Program.cs:line 69 > > Please note the first line which indicates that instead of calling the > default generic comparer's compare method using the type "Address" (or > whatever ref type is passed in) it is using the type "System.__Canon" (ie. > "Compare(System.__Canon, System.__Canon)" instead of "Compare(Address, > Address)")?! Where does this "__Canon" class come from. Please help! >
Can you write the dynamic assembly to disk and look at it with .NET Reflector? Whenever I've had problems with a reference to the wrong method, it's been because I either used a plain MemberInfo (i.e. result of GetMethod()) gotten before I called CreateType, or used a MethodBuilder (ConstructorBuilder, PropertyBuilder) after calling CreateType. [quoted text, click to view] > -- > Johannes Hansen > > System Consultant, frontAvenue A/S
"Johannes Hansen" <JohannesHansen@discussions.microsoft.com> wrote in message news:34E3CAE2-6406-4FEC-8F04-CBBD8DE37159@microsoft.com... [quoted text, click to view] >I just saw that I'm apparently calling compare on a class called > "GenericComparer<T>"... However, all my declaring types and such tells me > that I am using Comparer<T>. > > So when I'm using a value type the IL emitted looks like this: > IL_0005: call System.Collections.Generic.Comparer`1[System.Int32] > get_Default()/System.Collections.Generic.Comparer`1[System.Int32] > IL_000a: ldarg.0 > IL_000b: ldfld Int32 age/DynamicMethod.Sample.Person > IL_0010: ldarg.1 > IL_0011: ldfld Int32 age/DynamicMethod.Sample.Person > IL_0016: callvirt Int32 Compare(Int32, > Int32)/System.Collections.Generic.Comparer`1[System.Int32] > > But when I'm using a reference type the emitted IL looks like this: > L_0005: call > System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] > get_Default()/System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] > L_000a: ldarg.0 > L_000b: ldfld DynamicMethod.Sample.Address > address/DynamicMethod.Sample.Person > L_0010: ldarg.1 > L_0011: ldfld DynamicMethod.Sample.Address > address/DynamicMethod.Sample.Person > L_0016: callvirt Int32 Compare(DynamicMethod.Sample.Address, > DynamicMethod.Sample.Address)/System.Collections.Generic.GenericComparer`1[DynamicMethod.Sample.Address] > > Does anyone know what this is about?
Again, can you use the Save method (on either AssemblyBuilder or ModuleBuilder) to write the dynamic class to disk? Then you can open it with .NET Reflector same as the one written by hand, and comparison will be easier. Also, you can run PEVerify on it. It certainly looks, though, like you're trying to invoke GenericComparer<Address>.Compare on an object of type Comparer<Address>. PEVerify will almost certainly confirm that you've a problem. Don't all of these classes implement IComparer<T>? Try emitting a call to typeof(IComparer<>).GetGenericType(member.FieldInfo.FieldType).GetMethod("Compare"). [quoted text, click to view] > -- > Johannes Hansen > > System Consultant, frontAvenue A/S > > > "Johannes Hansen" wrote: > >> Thanks for the reply Ben but the MethodInfo for the "Compare" method is >> taken >> from the correct instance type of the comparer. I've extracted the IL for >> the >> working hard-coded method body and the IL for the failing dynamic method >> body. The dynamic method IL was extracted using Haibo Luo's Dynamic >> Method >> Visualizer and the IL for the hard-coded comparer was extracted using >> Lutz >> Roeder's Reflector, this might account for some of the syntactical >> differences but I'm not sure. >> >> Dynamic IL: >> L_0005: call >> System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] >> get_Default()/System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] >> L_000a: ldarg.0 >> L_000b: ldfld DynamicMethod.Sample.Address >> address/DynamicMethod.Sample.Person >> L_0010: ldarg.1 >> L_0011: ldfld DynamicMethod.Sample.Address >> address/DynamicMethod.Sample.Person >> L_0016: callvirt Int32 Compare(DynamicMethod.Sample.Address, >> DynamicMethod.Sample.Address)/System.Collections.Generic.GenericComparer`1[DynamicMethod.Sample.Address] >> >> Hard-coded (target) IL: >> L_0005: call [mscorlib]System.Collections.Generic.Comparer`1<!0> >> [mscorlib]System.Collections.Generic.Comparer`1<DynamicMethod.Sample.Address>::get_Default() >> L_000a: ldarg.0 >> L_000b: ldfld DynamicMethod.Sample.Address >> DynamicMethod.Sample.Person::address >> L_0010: ldarg.1 >> L_0011: ldfld DynamicMethod.Sample.Address >> DynamicMethod.Sample.Person::address >> L_0016: callvirt instance int32 >> [mscorlib]System.Collections.Generic.Comparer`1<DynamicMethod.Sample.Address>::Compare(!0, >> !0) >> >> Please remember that the failing code only fails when arg0 and arg1 is >> reference types and not value types. I belive the error is thrown at the >> final line (L_0016). >> >> -- >> Johannes Hansen >> >> System Consultant, frontAvenue A/S >> >> >> "Ben Voigt" wrote: >> >> > >> > "Johannes Hansen" <JohannesHansen@discussions.microsoft.com> wrote in >> > message news:55D373A7-C0C5-4F29-ADBE-5BF707098490@microsoft.com... >> > > Im currently updating my dynamic comparer class (which you can read >> > > about >> > > on >> > > codeproject) but I've run into a slight problem which I hope you guys >> > > can >> > > help me with... >> > > >> > > My current emit code looks similar to this: >> > > >> > > il.EmitCall(OpCodes.Call, >> > > member.DefaultComparerProperty.GetGetMethod(), >> > > null); >> > > il.Emit(OpCodes.Ldarg_0); >> > > il.Emit(OpCodes.Ldfld, member.FieldInfo); >> > > il.Emit(OpCodes.Ldarg_1); >> > > il.Emit(OpCodes.Ldfld, member.FieldInfo); >> > > il.EmitCall(OpCodes.Callvirt, member.DefaultComparerCompareMethod, >> > > null); >> > > >> > > Now, "member" is just a custom class containing various properties >> > > for >> > > easy >> > > access to defaul comparers, field types etc. for the member that >> > > needs to >> > > be >> > > compared. "DefaultComparerProperty" is the PropertyInfo for the >> > > Comparer<x>.Default property. "DefaultComparerCompareMethod" is the >> > > MethodInfo for the "Compare" method on the default comparer for the >> > > member's >> > > type. >> > > >> > > My problem is that if the member is a value type this setup works, >> > > but if >> > > it >> > > is a reference type it fails with the following exception: >> > > >> > > System.InvalidOperationException: Failed to compare two elements in >> > > the >> > > array. ---> System.MethodAccessException: >> > > System.Collections.Generic.Comparer`1.Compare(System.__Canon, >> > > System.__Canon) >> > > at DynamicComparison(Person , Person ) >> > > at DynamicComparer.DynamicComparerB`1.CompareMethodInvoker.Invoke(T >> > > itemA, T itemB) >> > > at DynamicComparer.DynamicComparerB`1.Compare(T itemA, T itemB) in >> > > C:\SomePath\DynamicComparer.cs:line 44 >> > > at System.Array.FunctorComparer`1.Compare(T x, T y) >> > > at >> > > System.Collections.Generic.ArraySortHelper`1.QuickSort[TValue](T[] >> > > keys, TValue[] values, Int32 left, Int32 right, IComparer`1 comparer) >> > > --- End of inner exception stack trace --- >> > > at >> > > System.Collections.Generic.ArraySortHelper`1.QuickSort[TValue](T[] >> > > keys, TValue[] values, Int32 left, Int32 right, IComparer`1 comparer) >> > > at System.Collections.Generic.ArraySortHelper`1.Sort[TValue](T[] >> > > keys, >> > > TValue[] values, Int32 index, Int32 length, IComparer`1 comparer) >> > > at System.Collections.Generic.ArraySortHelper`1.Sort(T[] items, >> > > Int32 >> > > index, Int32 length, IComparer`1 comparer) >> > > at System.Array.Sort[T](T[] array, Int32 index, Int32 length, >> > > IComparer`1
Calling through the IComparer<> interface did the job... Thanks Ben! -- Johannes Hansen System Consultant, frontAvenue A/S [quoted text, click to view] "Ben Voigt" wrote: > "Johannes Hansen" <JohannesHansen@discussions.microsoft.com> wrote in > message news:34E3CAE2-6406-4FEC-8F04-CBBD8DE37159@microsoft.com... > >I just saw that I'm apparently calling compare on a class called > > "GenericComparer<T>"... However, all my declaring types and such tells me > > that I am using Comparer<T>. > > > > So when I'm using a value type the IL emitted looks like this: > > IL_0005: call System.Collections.Generic.Comparer`1[System.Int32] > > get_Default()/System.Collections.Generic.Comparer`1[System.Int32] > > IL_000a: ldarg.0 > > IL_000b: ldfld Int32 age/DynamicMethod.Sample.Person > > IL_0010: ldarg.1 > > IL_0011: ldfld Int32 age/DynamicMethod.Sample.Person > > IL_0016: callvirt Int32 Compare(Int32, > > Int32)/System.Collections.Generic.Comparer`1[System.Int32] > > > > But when I'm using a reference type the emitted IL looks like this: > > L_0005: call > > System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] > > get_Default()/System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] > > L_000a: ldarg.0 > > L_000b: ldfld DynamicMethod.Sample.Address > > address/DynamicMethod.Sample.Person > > L_0010: ldarg.1 > > L_0011: ldfld DynamicMethod.Sample.Address > > address/DynamicMethod.Sample.Person > > L_0016: callvirt Int32 Compare(DynamicMethod.Sample.Address, > > DynamicMethod.Sample.Address)/System.Collections.Generic.GenericComparer`1[DynamicMethod.Sample.Address] > > > > Does anyone know what this is about? > > Again, can you use the Save method (on either AssemblyBuilder or > ModuleBuilder) to write the dynamic class to disk? Then you can open it > with .NET Reflector same as the one written by hand, and comparison will be > easier. Also, you can run PEVerify on it. > > It certainly looks, though, like you're trying to invoke > GenericComparer<Address>.Compare on an object of type Comparer<Address>. > PEVerify will almost certainly confirm that you've a problem. > > Don't all of these classes implement IComparer<T>? Try emitting a call to > typeof(IComparer<>).GetGenericType(member.FieldInfo.FieldType).GetMethod("Compare"). > > > -- > > Johannes Hansen > > > > System Consultant, frontAvenue A/S > > > > > > "Johannes Hansen" wrote: > > > >> Thanks for the reply Ben but the MethodInfo for the "Compare" method is > >> taken > >> from the correct instance type of the comparer. I've extracted the IL for > >> the > >> working hard-coded method body and the IL for the failing dynamic method > >> body. The dynamic method IL was extracted using Haibo Luo's Dynamic > >> Method > >> Visualizer and the IL for the hard-coded comparer was extracted using > >> Lutz > >> Roeder's Reflector, this might account for some of the syntactical > >> differences but I'm not sure. > >> > >> Dynamic IL: > >> L_0005: call > >> System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] > >> get_Default()/System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] > >> L_000a: ldarg.0 > >> L_000b: ldfld DynamicMethod.Sample.Address > >> address/DynamicMethod.Sample.Person > >> L_0010: ldarg.1 > >> L_0011: ldfld DynamicMethod.Sample.Address > >> address/DynamicMethod.Sample.Person > >> L_0016: callvirt Int32 Compare(DynamicMethod.Sample.Address, > >> DynamicMethod.Sample.Address)/System.Collections.Generic.GenericComparer`1[DynamicMethod.Sample.Address] > >> > >> Hard-coded (target) IL: > >> L_0005: call [mscorlib]System.Collections.Generic.Comparer`1<!0> > >> [mscorlib]System.Collections.Generic.Comparer`1<DynamicMethod.Sample.Address>::get_Default() > >> L_000a: ldarg.0 > >> L_000b: ldfld DynamicMethod.Sample.Address > >> DynamicMethod.Sample.Person::address > >> L_0010: ldarg.1 > >> L_0011: ldfld DynamicMethod.Sample.Address > >> DynamicMethod.Sample.Person::address > >> L_0016: callvirt instance int32 > >> [mscorlib]System.Collections.Generic.Comparer`1<DynamicMethod.Sample.Address>::Compare(!0, > >> !0) > >> > >> Please remember that the failing code only fails when arg0 and arg1 is > >> reference types and not value types. I belive the error is thrown at the > >> final line (L_0016). > >> > >> -- > >> Johannes Hansen > >> > >> System Consultant, frontAvenue A/S > >> > >> > >> "Ben Voigt" wrote: > >> > >> > > >> > "Johannes Hansen" <JohannesHansen@discussions.microsoft.com> wrote in > >> > message news:55D373A7-C0C5-4F29-ADBE-5BF707098490@microsoft.com... > >> > > Im currently updating my dynamic comparer class (which you can read > >> > > about > >> > > on > >> > > codeproject) but I've run into a slight problem which I hope you guys > >> > > can > >> > > help me with... > >> > > > >> > > My current emit code looks similar to this: > >> > > > >> > > il.EmitCall(OpCodes.Call, > >> > > member.DefaultComparerProperty.GetGetMethod(), > >> > > null); > >> > > il.Emit(OpCodes.Ldarg_0); > >> > > il.Emit(OpCodes.Ldfld, member.FieldInfo); > >> > > il.Emit(OpCodes.Ldarg_1); > >> > > il.Emit(OpCodes.Ldfld, member.FieldInfo); > >> > > il.EmitCall(OpCodes.Callvirt, member.DefaultComparerCompareMethod, > >> > > null); > >> > > > >> > > Now, "member" is just a custom class containing various properties > >> > > for > >> > > easy > >> > > access to defaul comparers, field types etc. for the member that > >> > > needs to > >> > > be > >> > > compared. "DefaultComparerProperty" is the PropertyInfo for the > >> > > Comparer<x>.Default property. "DefaultComparerCompareMethod" is the > >> > > MethodInfo for the "Compare" method on the default comparer for the > >> > > member's > >> > > type. > >> > > > >> > > My problem is that if the member is a value type this setup works, > >> > > but if > >> > > it > >> > > is a reference type it fails with the following exception: > >> > > > >> > > System.InvalidOperationException: Failed to compare two elements in > >> > > the > >> > > array. ---> System.MethodAccessException: > >> > > System.Collections.Generic.Comparer`1.Compare(System.__Canon, > >> > > System.__Canon) > >> > > at DynamicComparison(Person , Person ) > >> > > at DynamicComparer.DynamicComparerB`1.CompareMethodInvoker.Invoke(T > >> > > itemA, T itemB) > >> > > at DynamicComparer.DynamicComparerB`1.Compare(T itemA, T itemB) in > >> > > C:\SomePath\DynamicComparer.cs:line 44 > >> > > at System.Array.FunctorComparer`1.Compare(T x, T y) > >> > > at > >> > > System.Collections.Generic.ArraySortHelper`1.QuickSort[TValue](T[] > >> > > keys, TValue[] values, Int32 left, Int32 right, IComparer`1 comparer) > >> > > --- End of inner exception stack trace --- > >> > > at > >> > > System.Collections.Generic.ArraySortHelper`1.QuickSort[TValue](T[]
Hi Ben [quoted text, click to view] > You're welcome.... though on second thought, was the whole reason for using > dynamic methods to avoid a v-table dispatch?
No... :) I knew that I probably would get a slight performance penalty due to the pointer lookup but initial perf. testing shows that the penalty is within the acceptable range. The primary goal for using the Comparer<>.Default/IComparer<>.Compare implementation was to get better maintainability and reliability which we got. [quoted text, click to view] > BTW, while emitting, was member.DefaultComparerProperty.PropertyType == > member.DefaultComparerCompareMethod.DeclaringType?
Yes, they are the same in both cases... So I guess I can't use that as a test. -- Johannes Hansen System Consultant, frontAvenue A/S [quoted text, click to view] "Ben Voigt" wrote: > > "Johannes Hansen" <JohannesHansen@discussions.microsoft.com> wrote in > message news:E3767E07-8332-4817-9960-A1A4D14E0B42@microsoft.com... > > Calling through the IComparer<> interface did the job... Thanks Ben! > > You're welcome.... though on second thought, was the whole reason for using > dynamic methods to avoid a v-table dispatch? Using the interface does cause > a pointer lookup at runtime and prevents jit inlining :(. > > BTW, while emitting, was member.DefaultComparerProperty.PropertyType == > member.DefaultComparerCompareMethod.DeclaringType? > > This might be a quick test that allows you to use direct dispatch in most > cases and only fallback to IComparer<> when necessary. > > > -- > > Johannes Hansen > > > > System Consultant, frontAvenue A/S > > > > > > "Ben Voigt" wrote: > > > >> "Johannes Hansen" <JohannesHansen@discussions.microsoft.com> wrote in > >> message news:34E3CAE2-6406-4FEC-8F04-CBBD8DE37159@microsoft.com... > >> >I just saw that I'm apparently calling compare on a class called > >> > "GenericComparer<T>"... However, all my declaring types and such tells > >> > me > >> > that I am using Comparer<T>. > >> > > >> > So when I'm using a value type the IL emitted looks like this: > >> > IL_0005: call > >> > System.Collections.Generic.Comparer`1[System.Int32] > >> > get_Default()/System.Collections.Generic.Comparer`1[System.Int32] > >> > IL_000a: ldarg.0 > >> > IL_000b: ldfld Int32 age/DynamicMethod.Sample.Person > >> > IL_0010: ldarg.1 > >> > IL_0011: ldfld Int32 age/DynamicMethod.Sample.Person > >> > IL_0016: callvirt Int32 Compare(Int32, > >> > Int32)/System.Collections.Generic.Comparer`1[System.Int32] > >> > > >> > But when I'm using a reference type the emitted IL looks like this: > >> > L_0005: call > >> > System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] > >> > get_Default()/System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] > >> > L_000a: ldarg.0 > >> > L_000b: ldfld DynamicMethod.Sample.Address > >> > address/DynamicMethod.Sample.Person > >> > L_0010: ldarg.1 > >> > L_0011: ldfld DynamicMethod.Sample.Address > >> > address/DynamicMethod.Sample.Person > >> > L_0016: callvirt Int32 Compare(DynamicMethod.Sample.Address, > >> > DynamicMethod.Sample.Address)/System.Collections.Generic.GenericComparer`1[DynamicMethod.Sample.Address] > >> > > >> > Does anyone know what this is about? > >> > >> Again, can you use the Save method (on either AssemblyBuilder or > >> ModuleBuilder) to write the dynamic class to disk? Then you can open it > >> with .NET Reflector same as the one written by hand, and comparison will > >> be > >> easier. Also, you can run PEVerify on it. > >> > >> It certainly looks, though, like you're trying to invoke > >> GenericComparer<Address>.Compare on an object of type Comparer<Address>. > >> PEVerify will almost certainly confirm that you've a problem. > >> > >> Don't all of these classes implement IComparer<T>? Try emitting a call > >> to > >> typeof(IComparer<>).GetGenericType(member.FieldInfo.FieldType).GetMethod("Compare"). > >> > >> > -- > >> > Johannes Hansen > >> > > >> > System Consultant, frontAvenue A/S > >> > > >> > > >> > "Johannes Hansen" wrote: > >> > > >> >> Thanks for the reply Ben but the MethodInfo for the "Compare" method > >> >> is > >> >> taken > >> >> from the correct instance type of the comparer. I've extracted the IL > >> >> for > >> >> the > >> >> working hard-coded method body and the IL for the failing dynamic > >> >> method > >> >> body. The dynamic method IL was extracted using Haibo Luo's Dynamic > >> >> Method > >> >> Visualizer and the IL for the hard-coded comparer was extracted using > >> >> Lutz > >> >> Roeder's Reflector, this might account for some of the syntactical > >> >> differences but I'm not sure. > >> >> > >> >> Dynamic IL: > >> >> L_0005: call > >> >> System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] > >> >> get_Default()/System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] > >> >> L_000a: ldarg.0 > >> >> L_000b: ldfld DynamicMethod.Sample.Address > >> >> address/DynamicMethod.Sample.Person > >> >> L_0010: ldarg.1 > >> >> L_0011: ldfld DynamicMethod.Sample.Address > >> >> address/DynamicMethod.Sample.Person > >> >> L_0016: callvirt Int32 Compare(DynamicMethod.Sample.Address, > >> >> DynamicMethod.Sample.Address)/System.Collections.Generic.GenericComparer`1[DynamicMethod.Sample.Address] > >> >> > >> >> Hard-coded (target) IL: > >> >> L_0005: call [mscorlib]System.Collections.Generic.Comparer`1<!0> > >> >> [mscorlib]System.Collections.Generic.Comparer`1<DynamicMethod.Sample.Address>::get_Default() > >> >> L_000a: ldarg.0 > >> >> L_000b: ldfld DynamicMethod.Sample.Address > >> >> DynamicMethod.Sample.Person::address > >> >> L_0010: ldarg.1 > >> >> L_0011: ldfld DynamicMethod.Sample.Address > >> >> DynamicMethod.Sample.Person::address > >> >> L_0016: callvirt instance int32 > >> >> [mscorlib]System.Collections.Generic.Comparer`1<DynamicMethod.Sample.Address>::Compare(!0, > >> >> !0) > >> >> > >> >> Please remember that the failing code only fails when arg0 and arg1 is > >> >> reference types and not value types. I belive the error is thrown at > >> >> the > >> >> final line (L_0016). > >> >> > >> >> -- > >> >> Johannes Hansen > >> >> > >> >> System Consultant, frontAvenue A/S > >> >> > >> >> > >> >> "Ben Voigt" wrote: > >> >> > >> >> > > >> >> > "Johannes Hansen" <JohannesHansen@discussions.microsoft.com> wrote > >> >> > in > >> >> > message news:55D373A7-C0C5-4F29-ADBE-5BF707098490@microsoft.com... > >> >> > > Im currently updating my dynamic comparer class (which you can > >> >> > > read > >> >> > > about > >> >> > > on > >> >> > > codeproject) but I've run into a slight problem which I hope you > >> >> > > guys > >> >> > > can > >> >> > > help me with... > >> >> > > > >> >> > > My current emit code looks similar to this: > >> >> > > > >> >> > > il.EmitCall(OpCodes.Call, > >> >> > > member.DefaultComparerProperty.GetGetMethod(),
"Johannes Hansen" <JohannesHansen@discussions.microsoft.com> wrote in message news:E3767E07-8332-4817-9960-A1A4D14E0B42@microsoft.com... [quoted text, click to view] > Calling through the IComparer<> interface did the job... Thanks Ben!
You're welcome.... though on second thought, was the whole reason for using dynamic methods to avoid a v-table dispatch? Using the interface does cause a pointer lookup at runtime and prevents jit inlining :(. BTW, while emitting, was member.DefaultComparerProperty.PropertyType == member.DefaultComparerCompareMethod.DeclaringType? This might be a quick test that allows you to use direct dispatch in most cases and only fallback to IComparer<> when necessary. [quoted text, click to view] > -- > Johannes Hansen > > System Consultant, frontAvenue A/S > > > "Ben Voigt" wrote: > >> "Johannes Hansen" <JohannesHansen@discussions.microsoft.com> wrote in >> message news:34E3CAE2-6406-4FEC-8F04-CBBD8DE37159@microsoft.com... >> >I just saw that I'm apparently calling compare on a class called >> > "GenericComparer<T>"... However, all my declaring types and such tells >> > me >> > that I am using Comparer<T>. >> > >> > So when I'm using a value type the IL emitted looks like this: >> > IL_0005: call >> > System.Collections.Generic.Comparer`1[System.Int32] >> > get_Default()/System.Collections.Generic.Comparer`1[System.Int32] >> > IL_000a: ldarg.0 >> > IL_000b: ldfld Int32 age/DynamicMethod.Sample.Person >> > IL_0010: ldarg.1 >> > IL_0011: ldfld Int32 age/DynamicMethod.Sample.Person >> > IL_0016: callvirt Int32 Compare(Int32, >> > Int32)/System.Collections.Generic.Comparer`1[System.Int32] >> > >> > But when I'm using a reference type the emitted IL looks like this: >> > L_0005: call >> > System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] >> > get_Default()/System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] >> > L_000a: ldarg.0 >> > L_000b: ldfld DynamicMethod.Sample.Address >> > address/DynamicMethod.Sample.Person >> > L_0010: ldarg.1 >> > L_0011: ldfld DynamicMethod.Sample.Address >> > address/DynamicMethod.Sample.Person >> > L_0016: callvirt Int32 Compare(DynamicMethod.Sample.Address, >> > DynamicMethod.Sample.Address)/System.Collections.Generic.GenericComparer`1[DynamicMethod.Sample.Address] >> > >> > Does anyone know what this is about? >> >> Again, can you use the Save method (on either AssemblyBuilder or >> ModuleBuilder) to write the dynamic class to disk? Then you can open it >> with .NET Reflector same as the one written by hand, and comparison will >> be >> easier. Also, you can run PEVerify on it. >> >> It certainly looks, though, like you're trying to invoke >> GenericComparer<Address>.Compare on an object of type Comparer<Address>. >> PEVerify will almost certainly confirm that you've a problem. >> >> Don't all of these classes implement IComparer<T>? Try emitting a call >> to >> typeof(IComparer<>).GetGenericType(member.FieldInfo.FieldType).GetMethod("Compare"). >> >> > -- >> > Johannes Hansen >> > >> > System Consultant, frontAvenue A/S >> > >> > >> > "Johannes Hansen" wrote: >> > >> >> Thanks for the reply Ben but the MethodInfo for the "Compare" method >> >> is >> >> taken >> >> from the correct instance type of the comparer. I've extracted the IL >> >> for >> >> the >> >> working hard-coded method body and the IL for the failing dynamic >> >> method >> >> body. The dynamic method IL was extracted using Haibo Luo's Dynamic >> >> Method >> >> Visualizer and the IL for the hard-coded comparer was extracted using >> >> Lutz >> >> Roeder's Reflector, this might account for some of the syntactical >> >> differences but I'm not sure. >> >> >> >> Dynamic IL: >> >> L_0005: call >> >> System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] >> >> get_Default()/System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] >> >> L_000a: ldarg.0 >> >> L_000b: ldfld DynamicMethod.Sample.Address >> >> address/DynamicMethod.Sample.Person >> >> L_0010: ldarg.1 >> >> L_0011: ldfld DynamicMethod.Sample.Address >> >> address/DynamicMethod.Sample.Person >> >> L_0016: callvirt Int32 Compare(DynamicMethod.Sample.Address, >> >> DynamicMethod.Sample.Address)/System.Collections.Generic.GenericComparer`1[DynamicMethod.Sample.Address] >> >> >> >> Hard-coded (target) IL: >> >> L_0005: call [mscorlib]System.Collections.Generic.Comparer`1<!0> >> >> [mscorlib]System.Collections.Generic.Comparer`1<DynamicMethod.Sample.Address>::get_Default() >> >> L_000a: ldarg.0 >> >> L_000b: ldfld DynamicMethod.Sample.Address >> >> DynamicMethod.Sample.Person::address >> >> L_0010: ldarg.1 >> >> L_0011: ldfld DynamicMethod.Sample.Address >> >> DynamicMethod.Sample.Person::address >> >> L_0016: callvirt instance int32 >> >> [mscorlib]System.Collections.Generic.Comparer`1<DynamicMethod.Sample.Address>::Compare(!0, >> >> !0) >> >> >> >> Please remember that the failing code only fails when arg0 and arg1 is >> >> reference types and not value types. I belive the error is thrown at >> >> the >> >> final line (L_0016). >> >> >> >> -- >> >> Johannes Hansen >> >> >> >> System Consultant, frontAvenue A/S >> >> >> >> >> >> "Ben Voigt" wrote: >> >> >> >> > >> >> > "Johannes Hansen" <JohannesHansen@discussions.microsoft.com> wrote >> >> > in >> >> > message news:55D373A7-C0C5-4F29-ADBE-5BF707098490@microsoft.com... >> >> > > Im currently updating my dynamic comparer class (which you can >> >> > > read >> >> > > about >> >> > > on >> >> > > codeproject) but I've run into a slight problem which I hope you >> >> > > guys >> >> > > can >> >> > > help me with... >> >> > > >> >> > > My current emit code looks similar to this: >> >> > > >> >> > > il.EmitCall(OpCodes.Call, >> >> > > member.DefaultComparerProperty.GetGetMethod(), >> >> > > null); >> >> > > il.Emit(OpCodes.Ldarg_0); >> >> > > il.Emit(OpCodes.Ldfld, member.FieldInfo); >> >> > > il.Emit(OpCodes.Ldarg_1); >> >> > > il.Emit(OpCodes.Ldfld, member.FieldInfo); >> >> > > il.EmitCall(OpCodes.Callvirt, member.DefaultComparerCompareMethod, >> >> > > null); >> >> > > >> >> > > Now, "member" is just a custom class containing various properties >> >> > > for >> >> > > easy >> >> > > access to defaul comparers, field types etc. for the member that >> >> > > needs to >> >> > > be >> >> > > compared. "DefaultComparerProperty" is the PropertyInfo for the >> >> > > Comparer<x>.Default property. "DefaultComparerCompareMethod" is >> >> > > the >> >> > > MethodInfo for the "Compare" method on the default comparer for >> >> > > the >> >> > > member's >> >> > > type. >> >> > > >> >> > > My problem is that if the member is a value type this setup works, >> >> > > but if >> >> > > it >> >> > > is a reference type it fails with the following exception: >> >> > >
Don't see what you're looking for? Try a search.
|
|
|