Exemple #1
0
            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 &amp; 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>)));
            }