Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- public static class StructuralHashCodeCalculator
- {
- private static readonly ConcurrentDictionary<Type, Func<object, int>> methodCache;
- static StructuralHashCodeCalculator()
- {
- methodCache = new ConcurrentDictionary<Type, Func<object, int>>();
- }
- public static int CalculateHashCode<T>([CanBeNull] T instance)
- {
- if (instance == null)
- {
- return 0;
- }
- var instanceType = instance.GetType();
- var comparisonMethod = methodCache.GetOrAdd(instanceType, ComparisonMethodGenerator.GenerateHashCodeCalculationMethod);
- return comparisonMethod(instance);
- }
- }
- public static class StructuralComparer
- {
- private static readonly ConcurrentDictionary<Type, Func<object, object, bool>> methodCache;
- static StructuralComparer()
- {
- methodCache = new ConcurrentDictionary<Type, Func<object, object, bool>>();
- }
- public static bool AreEqual<T>([CanBeNull] T first, [CanBeNull] T second)
- {
- if (ReferenceEquals(first, second))
- {
- return true;
- }
- if (first == null || second == null)
- {
- return false;
- }
- var firstType = first.GetType();
- var secondType = second.GetType();
- if (firstType != secondType)
- {
- throw new InvalidOperationException($"Could not check structural equality of different types {firstType.Name} and {secondType.Name}");
- }
- var comparisonMethod = methodCache.GetOrAdd(firstType, ComparisonMethodGenerator.GenerateComparisonMethod);
- return comparisonMethod(first, second);
- }
- }
- public static class ComparisonMethodGenerator
- {
- private const int multiplier = 397;
- private static readonly Func<object, object, bool> nativeEqualsFunc = (first, second) => first.Equals(second);
- private static readonly Func<object, int> nativeGetHashCodeFunc = (instance) => instance.GetHashCode();
- private static readonly Type objectType = typeof(object);
- private static readonly Type boolType = typeof(bool);
- private static readonly Type intType = typeof(int);
- private static readonly Type stringType = typeof(string);
- private static readonly Type enumerableType = typeof(IEnumerable<>);
- private static readonly Type comparisonDelegateType = typeof(Func<object, object, bool>);
- private static readonly Type hashCodeCalculationDelegateType = typeof(Func<object, int>);
- private static readonly Type comparisonIgnoreAttributeType = typeof(ComparisonIgnoreAttribute);
- private static readonly Type structuralEqualityExtensionsType = typeof(StructuralEqualityExtensions);
- private static readonly Type comparisonMethodGeneratorType = typeof(ComparisonMethodGenerator);
- public static Func<object, int> GenerateHashCodeCalculationMethod([NotNull] Type instanceType)
- {
- var сomparableProperties = instanceType.GetProperties().Where(ShouldBeConsidered).ToArray();
- var сomparableFields = instanceType.GetFields().Where(ShouldBeConsidered).ToArray();
- if (сomparableProperties.Length == 0 && сomparableFields.Length == 0)
- {
- return nativeGetHashCodeFunc;
- }
- var methodName = $"dynamic_get_hash_code{instanceType.Name}_{Guid.NewGuid():N}";
- var dynamicMethod = new DynamicMethod(methodName, intType, new[] {objectType}, comparisonMethodGeneratorType, true);
- using (var dynamicMethodGenerator = new GroboIL(dynamicMethod))
- {
- if (IsCollection(instanceType, out _) || IsBuildInType(instanceType))
- {
- GenerateArgumentHashCodeCalculationMethodBody(instanceType, dynamicMethodGenerator);
- }
- else
- {
- GenerateStructureHashCodeCalculationMethodBody(instanceType, сomparableProperties, сomparableFields, dynamicMethodGenerator);
- }
- }
- var dynamicMethodDelegate = (Func<object, int>)dynamicMethod.CreateDelegate(hashCodeCalculationDelegateType);
- return dynamicMethodDelegate;
- }
- private static void GenerateArgumentHashCodeCalculationMethodBody(
- [NotNull] Type comparingObjectType,
- [NotNull] GroboIL ilGenerator)
- {
- IL.LoadArgumentToStack(0, comparingObjectType, ilGenerator);
- IL.HashCode.PutCalculatedHashCodeToStack(comparingObjectType, ilGenerator);
- IL.EmitReturnStackHead(ilGenerator);
- }
- private static void GenerateStructureHashCodeCalculationMethodBody(
- [NotNull] Type instanceType,
- [NotNull] PropertyInfo[] comparableProperties,
- [NotNull] FieldInfo[] comparableFields,
- [NotNull] GroboIL ilGenerator)
- {
- var castedToLocal = IL.CreateVariableFromArgument(0, instanceType, ilGenerator);
- var previousHashCodeIsOnStack = false;
- foreach (var instanceProperty in comparableProperties)
- {
- if(previousHashCodeIsOnStack)
- {
- IL.MultiplyTo(multiplier, ilGenerator);
- }
- IL.PutPropertyValueToStack(castedToLocal, instanceType, instanceProperty, ilGenerator);
- var propType = instanceProperty.PropertyType;
- IL.HashCode.PutCalculatedHashCodeToStack(propType, ilGenerator);
- if(previousHashCodeIsOnStack)
- {
- IL.PutXorResultToStack(ilGenerator);
- }
- else
- {
- previousHashCodeIsOnStack = true;
- }
- }
- foreach (var comparableField in comparableFields)
- {
- if(previousHashCodeIsOnStack)
- {
- IL.MultiplyTo(multiplier, ilGenerator);
- }
- IL.PutFieldValueToStack(castedToLocal, instanceType, comparableField, ilGenerator);
- var fieldType = comparableField.FieldType;
- IL.HashCode.PutCalculatedHashCodeToStack(fieldType, ilGenerator);
- if(previousHashCodeIsOnStack)
- {
- IL.PutXorResultToStack(ilGenerator);
- }
- else
- {
- previousHashCodeIsOnStack = true;
- }
- }
- IL.EmitReturnStackHead(ilGenerator);
- }
- public static Func<object, object, bool> GenerateComparisonMethod([NotNull] Type comparingObjectType)
- {
- var сomparableProperties = comparingObjectType.GetProperties().Where(ShouldBeConsidered).ToArray();
- var сomparableFields = comparingObjectType.GetFields().Where(ShouldBeConsidered).ToArray();
- if (сomparableProperties.Length == 0 && сomparableFields.Length == 0)
- {
- return nativeEqualsFunc;
- }
- var methodName = $"dynamic_are_equal_{comparingObjectType.Name}_{Guid.NewGuid():N}";
- var dynamicMethod = new DynamicMethod(methodName, boolType, new[] {objectType, objectType}, comparisonMethodGeneratorType, true);
- using (var dynamicMethodGenerator = new GroboIL(dynamicMethod))
- {
- if (IsCollection(comparingObjectType, out _) || IsBuildInType(comparingObjectType))
- {
- GenerateArgumentsComparisonMethodBody(comparingObjectType, dynamicMethodGenerator);
- }
- else
- {
- GenerateStructureComparisonMethodBody(comparingObjectType, сomparableProperties, сomparableFields, dynamicMethodGenerator);
- }
- }
- var dynamicMethodDelegate = (Func<object, object, bool>)dynamicMethod.CreateDelegate(comparisonDelegateType);
- return dynamicMethodDelegate;
- }
- private static void GenerateArgumentsComparisonMethodBody(
- [NotNull] Type comparingObjectType,
- [NotNull] GroboIL ilGenerator)
- {
- IL.LoadArgumentToStack(0, comparingObjectType, ilGenerator);
- IL.LoadArgumentToStack(1, comparingObjectType, ilGenerator);
- IL.Comparison.PutComparisonResultToStack(comparingObjectType, ilGenerator);
- IL.Comparison.EmitReturnFalseIfNotEquals(ilGenerator);
- IL.EmitReturnBool(true, ilGenerator);
- }
- private static void GenerateStructureComparisonMethodBody(
- [NotNull] Type comparingObjectType,
- [NotNull] PropertyInfo[] comparableProperties,
- [NotNull] FieldInfo[] comparableFields,
- [NotNull] GroboIL ilGenerator)
- {
- var castedToLocal = IL.CreateVariableFromArgument(0, comparingObjectType, ilGenerator);
- var castedFromLocal = IL.CreateVariableFromArgument(1, comparingObjectType, ilGenerator);
- foreach (var comparableProperty in comparableProperties)
- {
- IL.PutPropertyValueToStack(castedToLocal, comparingObjectType, comparableProperty, ilGenerator);
- IL.PutPropertyValueToStack(castedFromLocal, comparingObjectType, comparableProperty, ilGenerator);
- var propType = comparableProperty.PropertyType;
- IL.Comparison.PutComparisonResultToStack(propType, ilGenerator);
- IL.Comparison.EmitReturnFalseIfNotEquals(ilGenerator);
- }
- foreach (var comparableField in comparableFields)
- {
- IL.PutFieldValueToStack(castedToLocal, comparingObjectType, comparableField, ilGenerator);
- IL.PutFieldValueToStack(castedFromLocal, comparingObjectType, comparableField, ilGenerator);
- var fieldType = comparableField.FieldType;
- IL.Comparison.PutComparisonResultToStack(fieldType, ilGenerator);
- IL.Comparison.EmitReturnFalseIfNotEquals(ilGenerator);
- }
- IL.EmitReturnBool(true, ilGenerator);
- }
- private static bool IsCollection([NotNull] Type type, out Type itemType)
- {
- itemType = null;
- if (type == stringType)
- {
- return false;
- }
- //note: http://stackoverflow.com/questions/9434825/determine-if-a-property-is-a-kind-of-array-by-reflection
- var iEnumerableMemberInfo = type.GetInterface(enumerableType.FullName);
- if (iEnumerableMemberInfo != null)
- {
- itemType = iEnumerableMemberInfo.GetGenericArguments()[0];
- return true;
- }
- return false;
- }
- private static bool IsBuildInType([NotNull] Type type)
- {
- //How to determine build-in type in .net
- //https://stackoverflow.com/questions/5932580/how-to-determine-if-a-object-type-is-a-built-in-system-type
- return type.Module.ScopeName == "CommonLanguageRuntimeLibrary";
- }
- private static bool ShouldBeConsidered([NotNull] FieldInfo field)
- {
- return field.IsPublic && !field.IsStatic && !IsIgnored(field);
- }
- private static bool IsIgnored([NotNull] FieldInfo field)
- {
- return field.FindAttribute<ComparisonIgnoreAttribute>() != null;
- }
- private static bool ShouldBeConsidered([NotNull] PropertyInfo property)
- {
- return property.CanRead && property.GetMethod.IsPublic && !IsIgnored(property);
- }
- private static bool IsIgnored([NotNull] PropertyInfo property)
- {
- return property.GetCustomAttributes(comparisonIgnoreAttributeType, true).Length > 0;
- }
- private static class IL
- {
- public static void LoadArgumentToStack(int argumentIndex, [NotNull] Type argumentType, [NotNull] GroboIL ilGenerator)
- {
- ilGenerator.Ldarg(argumentIndex);
- if (argumentType.IsValueType)
- {
- ilGenerator.Unbox_Any(argumentType);
- }
- else
- {
- ilGenerator.Castclass(argumentType);
- }
- }
- public static GroboIL.Local CreateVariableFromArgument(int argumentIndex, [NotNull] Type argumentType, [NotNull] GroboIL ilGenerator)
- {
- var variable = ilGenerator.DeclareLocal(argumentType);
- LoadArgumentToStack(argumentIndex, argumentType, ilGenerator);
- ilGenerator.Stloc(variable);
- return variable;
- }
- public static void MultiplyTo(int value, [NotNull] GroboIL ilGenerator)
- {
- ilGenerator.Ldc_I4(value);
- ilGenerator.Mul();
- }
- public static void PutFieldValueToStack(
- GroboIL.Local variable,
- [NotNull] Type variableType,
- FieldInfo variableField,
- [NotNull] GroboIL ilGenerator)
- {
- if (variableType.IsValueType)
- {
- ilGenerator.Ldloca(variable);
- }
- else
- {
- ilGenerator.Ldloc(variable);
- }
- ilGenerator.Ldfld(variableField);
- }
- public static void PutPropertyValueToStack(
- GroboIL.Local variable,
- [NotNull] Type variableType,
- [NotNull] PropertyInfo variableProperty,
- [NotNull] GroboIL ilGenerator)
- {
- if (variableType.IsValueType)
- {
- ilGenerator.Ldloca(variable);
- }
- else
- {
- ilGenerator.Ldloc(variable);
- }
- var propertyGetMethod = variableProperty.GetMethod;
- ilGenerator.Call(propertyGetMethod);
- }
- public static void EmitReturnBool(bool @bool, [NotNull] GroboIL ilGenerator)
- {
- ilGenerator.Ldc_I4(@bool ? 1 : 0);
- ilGenerator.Ret();
- }
- public static void EmitReturnStackHead([NotNull] GroboIL ilGenerator)
- {
- ilGenerator.Ret();
- }
- public static void PutXorResultToStack([NotNull] GroboIL ilGenerator)
- {
- ilGenerator.Xor();
- }
- public static class Comparison
- {
- private static readonly Type structuralComparerType = typeof(StructuralComparer);
- private static readonly MethodInfo isEqualsForNullableMethod = structuralEqualityExtensionsType.GetMethod(nameof(StructuralEqualityExtensions.IsNullableEquals));
- private static readonly MethodInfo isEqualsForRefMethod = structuralEqualityExtensionsType.GetMethod(nameof(StructuralEqualityExtensions.IsRefEquals));
- private static readonly MethodInfo isEqualsForValueMethod = structuralEqualityExtensionsType.GetMethod(nameof(StructuralEqualityExtensions.IsValueEquals));
- private static readonly MethodInfo isEqualsForCollection = structuralEqualityExtensionsType.GetMethod(nameof(StructuralEqualityExtensions.IsCollectionsEquals));
- private static readonly MethodInfo isEqualsForStructure = structuralComparerType.GetMethod(nameof(StructuralComparer.AreEqual));
- public static void PutComparisonResultToStack([NotNull] Type valueType, [NotNull] GroboIL ilGenerator)
- {
- if (IsCollection(valueType, out Type itemType))
- {
- ilGenerator.Call(isEqualsForCollection.MakeGenericMethod(itemType));
- }
- else if (!IsBuildInType(valueType))
- {
- ilGenerator.Call(isEqualsForStructure.MakeGenericMethod(valueType));
- }
- else if (valueType.IsNullableValueType())
- {
- ilGenerator.Call(isEqualsForNullableMethod.MakeGenericMethod(Nullable.GetUnderlyingType(valueType)));
- }
- else if (valueType.IsValueType)
- {
- ilGenerator.Call(isEqualsForValueMethod.MakeGenericMethod(valueType));
- }
- else
- {
- ilGenerator.Call(isEqualsForRefMethod.MakeGenericMethod(valueType));
- }
- }
- public static void EmitReturnFalseIfNotEquals([NotNull] GroboIL ilGenerator)
- {
- var @continue = ilGenerator.DefineLabel("continue");
- ilGenerator.Brtrue(@continue);
- EmitReturnBool(false, ilGenerator);
- ilGenerator.MarkLabel(@continue);
- }
- }
- public static class HashCode
- {
- private static readonly Type structuralHashCodeCalculatorType = typeof(StructuralHashCodeCalculator);
- private static readonly MethodInfo getHashCodeForNullableMethod = structuralEqualityExtensionsType.GetMethod(nameof(StructuralEqualityExtensions.GetHashCodeForNullable));
- private static readonly MethodInfo getHashCodeForRefMethod = structuralEqualityExtensionsType.GetMethod(nameof(StructuralEqualityExtensions.GetHashCodeForRef));
- private static readonly MethodInfo getHashCodeForCollectionMethod = structuralEqualityExtensionsType.GetMethod(nameof(StructuralEqualityExtensions.GetHashCodeForCollection));
- private static readonly MethodInfo getHashCodeForValueMethod = structuralEqualityExtensionsType.GetMethod(nameof(StructuralEqualityExtensions.GetHashCodeForValue));
- private static readonly MethodInfo getHashCodeForStructureMethod = structuralHashCodeCalculatorType.GetMethod(nameof(StructuralHashCodeCalculator.CalculateHashCode));
- public static void PutCalculatedHashCodeToStack([NotNull] Type propType, [NotNull] GroboIL ilGenerator)
- {
- if (IsCollection(propType, out var itemType))
- {
- ilGenerator.Call(getHashCodeForCollectionMethod.MakeGenericMethod(itemType));
- }
- else if (!IsBuildInType(propType))
- {
- ilGenerator.Call(getHashCodeForStructureMethod.MakeGenericMethod(propType));
- }
- else if (propType.IsNullableValueType())
- {
- ilGenerator.Call(getHashCodeForNullableMethod.MakeGenericMethod(Nullable.GetUnderlyingType(propType)));
- }
- else if (propType.IsValueType)
- {
- ilGenerator.Call(getHashCodeForValueMethod.MakeGenericMethod(propType));
- }
- else
- {
- ilGenerator.Call(getHashCodeForRefMethod.MakeGenericMethod(propType));
- }
- }
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement