public static Func <T, T, int> BuildComparerFunc(SortCriteria sortCriteria) { string sortField = sortCriteria.SortField; if (!SortComparerFactory <T> .s_ComparerCache.TryGetValue(sortField, out Func <T, T, int> comparerFunc)) { comparerFunc = (x, y) => { object?valueX = SortComparerReflectionHelper.FindPropertyGetMethod(x !.GetType(), sortField)?.Invoke(x, null); object?valueY = SortComparerReflectionHelper.FindPropertyGetMethod(y !.GetType(), sortField)?.Invoke(y, null); if (!TryEnsureValidReferences(valueX, valueY, out int referenceComparisonResult)) { return(referenceComparisonResult); } Type targetType = SortComparerReflectionHelper.ResolveTargetType(valueX.GetType()); if (targetType == SortComparerReflectionHelper.ResolveTargetType(valueY.GetType())) { MethodInfo?compareToMethodInfo = SortComparerReflectionHelper.FindCompareToMethodInfo(targetType); if (compareToMethodInfo != null) { return((int)compareToMethodInfo.Invoke(valueX, new object[] { valueY })); } } return(Comparer <object> .Default.Compare(valueX, valueY)); }; SortComparerFactory <T> .s_ComparerCache.TryAdd(sortField, comparerFunc); } return(comparerFunc); }
internal static Func <T, T, int> BuildPropertyComparer(Type typeX, Type typeY, string sortField) { MethodInfo?propertyX = SortComparerReflectionHelper.FindPropertyGetMethod(typeX, sortField); MethodInfo?propertyY = SortComparerReflectionHelper.FindPropertyGetMethod(typeY, sortField); if (propertyX == null && propertyY == null) { return((x, y) => 0); } if (propertyX == null || propertyY == null) { return(BuildOneSidedComparer((propertyX ?? propertyY) !, propertyX != null, sortField)); } Type targetType = SortComparerReflectionHelper.ResolveTargetType(propertyX.ReturnType); if (targetType != SortComparerReflectionHelper.ResolveTargetType(propertyY.ReturnType)) { return(BuildDefaultComparer(propertyX, propertyY, sortField)); } bool isEnum = targetType.IsEnum; if (isEnum) { targetType = typeof(long); } MethodInfo?compareToMethodInfo = SortComparerReflectionHelper.FindCompareToMethodInfo(targetType); return(compareToMethodInfo == null ? BuildDefaultComparer(propertyX, propertyY, sortField) : !targetType.IsValueType ? BuildReferenceTypePropertyComparer(propertyX, propertyY, sortField, targetType, compareToMethodInfo) : BuildValueTypePropertyComparer(propertyX, propertyY, sortField, typeof(Nullable <>).MakeGenericType(targetType), targetType, compareToMethodInfo, isEnum)); }
/// <remarks> /// This is used when a property is defined on one side of the comparison but not the other. /// Case A: PropertyX == null && PropertyY != null /// Case B: PropertyX != null && PropertyY == null /// The goal here is to return 0 if the found property is null or !HasValue, otherwise return -1 for CaseA, 1 for CaseB. /// Built using MethodsToDecompile.DefaultOneSidedReferenceComparer & MethodsToDecompile.DefaultOneSidedValueComparer. /// </remarks> private static Func <T, T, int> BuildOneSidedComparer( MethodInfo property, bool isPropertyX, string sortField) { DynamicMethod compareMethod = new DynamicMethod($"{property.ReturnType.Name}.{isPropertyX}.{sortField}$OneSideComparer", typeof(int), new[] { s_SourceType, s_SourceType }, true); ILGenerator generator = compareMethod.GetILGenerator(); Label returnNonNullResult = generator.DefineLabel(); Type targetUnderlyingType = SortComparerReflectionHelper.ResolveTargetType(property.ReturnType); if (property.ReturnType.IsValueType) { Type targetNullableType = typeof(Nullable <>).MakeGenericType(targetUnderlyingType); SortComparerReflectionHelper.NullableTypeInfo nullableTypeInfo = SortComparerReflectionHelper.FindNullableTypeInfo(targetNullableType, targetUnderlyingType); /* * .locals init ( * [0] valuetype [netstandard]System.Nullable`1<int32> valueX * ) */ generator.DeclareLocal(targetUnderlyingType); /* * // if (!new int?(x.Id).HasValue) */ ReadValueTypeIntoFirstLocal(property, isPropertyX, targetNullableType, false, nullableTypeInfo.TargetNullableTypeCtor, generator); /* * ldloca.s 0 * call instance bool valuetype [netstandard]System.Nullable`1<int32>::get_HasValue() * brtrue.s returnNonNullResult */ generator.Emit(OpCodes.Ldloca_S, 0); generator.Emit(OpCodes.Call, nullableTypeInfo.TargetNullableTypeHasValue); generator.Emit(OpCodes.Brtrue_S, returnNonNullResult); /* * // return 0; * ldc.i4.0 * ret */ generator.Emit(OpCodes.Ldc_I4_0); generator.Emit(OpCodes.Ret); generator.MarkLabel(returnNonNullResult); /* * // return 1; * ldc.i4.1 <- return -1 if !isPropertyX */ if (isPropertyX) { generator.Emit(OpCodes.Ldc_I4_1); } else { generator.Emit(OpCodes.Ldc_I4_M1); } } else { /* * // if (x.Text == null) * ldarg.0 * callvirt instance string Code.Thing::get_Text() * brtrue.s returnNonNullResult */ if (isPropertyX) { generator.Emit(OpCodes.Ldarg_0); } else { generator.Emit(OpCodes.Ldarg_1); } if (property.DeclaringType != s_SourceType) { generator.Emit(OpCodes.Castclass, property.DeclaringType); } generator.Emit(OpCodes.Callvirt, property); generator.Emit(OpCodes.Brtrue_S, returnNonNullResult); /* * // return 0; * ldc.i4.0 * ret */ generator.Emit(OpCodes.Ldc_I4_0); generator.Emit(OpCodes.Ret); generator.MarkLabel(returnNonNullResult); /* * // return -1; <-- return 1 if isPropertyX * ldc.i4.m1 */ if (isPropertyX) { generator.Emit(OpCodes.Ldc_I4_1); } else { generator.Emit(OpCodes.Ldc_I4_M1); } } generator.Emit(OpCodes.Ret); return((Func <T, T, int>)compareMethod.CreateDelegate(typeof(Func <T, T, int>))); }