private MethodDefinition OverrideGetHashCode( TypeDefinition caseClassTypeDefinition, IEnumerable <CaseClassProperty> properties, FieldVarietyDetector fieldVarietyDetector) { var method = new MethodDefinition( nameof(GetHashCode), MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual, _typeSystem.Int32Reference); var getHashCodeMethod = _moduleDefinition.ImportReference( _typeSystem.ObjectDefinition.Methods.Single(x => x.Name == nameof(GetHashCode))); var getHashCodeOfValueTypeMethod = _moduleDefinition.ImportReference( _findType(typeof(ValueType).FullName).Methods.Single(m => m.Name == nameof(GetHashCode))); var codeEmitter = method.Body.GetILProcessor(); codeEmitter.Emit(OpCodes.Ldc_I4, 857327845); foreach (var property in properties) { codeEmitter.Emit(OpCodes.Ldc_I4, -1521134295); codeEmitter.Emit(OpCodes.Mul); var doAddition = Instruction.Create(OpCodes.Add); if (fieldVarietyDetector.Detect(property.BackingFieldReference.FieldType).IsReferenceType ?? true) { var callGetHashCode = Instruction.Create(OpCodes.Callvirt, getHashCodeMethod); property.BoxFieldValue(codeEmitter); codeEmitter.Emit(OpCodes.Dup); codeEmitter.Emit(OpCodes.Brtrue_S, callGetHashCode); codeEmitter.Emit(OpCodes.Pop); codeEmitter.Emit(OpCodes.Ldc_I4_0); codeEmitter.Emit(OpCodes.Br_S, doAddition); codeEmitter.Append(callGetHashCode); } else { codeEmitter.Emit(OpCodes.Ldarg_0); codeEmitter.Emit(OpCodes.Ldfld, property.BackingFieldReference); codeEmitter.Emit(OpCodes.Call, getHashCodeOfValueTypeMethod); } codeEmitter.Append(doAddition); } codeEmitter.Emit(OpCodes.Ret); return(method); }
public void EmitEqualityCheck(ILProcessor compareCodeEmitter, FieldVarietyDetector fieldVarietyDetector) { switch (fieldVarietyDetector.Detect(BackingFieldReference.FieldType)) { case ReferenceTypeImplementingIEquatable referenceTypeImplementingIEquatable: EmitFieldEqualityTestingCode( compareCodeEmitter, nameof(DeepEqualityComparer.EquatableReferencesAreEqual), referenceTypeImplementingIEquatable.FieldType); break; case TypedCollection typedCollection: EmitFieldEqualityTestingCode( compareCodeEmitter, nameof(DeepEqualityComparer.TypedCollectionsAreEqual), typedCollection.ElementType); break; case UntypedColection _: EmitFieldEqualityTestingCode( compareCodeEmitter, nameof(DeepEqualityComparer.UntypedCollectionsAreEqual)); break; case PlainReferenceType plainReferenceType: EmitFieldEqualityTestingCode( compareCodeEmitter, nameof(DeepEqualityComparer.ReferenceInstancesAreEqual), plainReferenceType.FieldType); break; case ValueTypeImplementingIEquatable valueTypeImplementingIEquatable: EmitFieldEqualityTestingCode( compareCodeEmitter, nameof(DeepEqualityComparer.EquatableValuesAreEqual), valueTypeImplementingIEquatable.FieldType); break; case PlainValueType plainValueType: EmitFieldEqualityTestingCode( compareCodeEmitter, nameof(DeepEqualityComparer.ValueInstancesAreEqual), plainValueType.FieldType); break; case NullableTypeImplementingIEquatable nullableTypeImplementingIEquatable: EmitFieldEqualityTestingCode( compareCodeEmitter, nameof(DeepEqualityComparer.EquatableNullablesAreEqual), nullableTypeImplementingIEquatable.UnderlyingType); break; case PlainNullableType plainNullableType: EmitFieldEqualityTestingCode( compareCodeEmitter, nameof(DeepEqualityComparer.NullablesAreEqual), plainNullableType.UnderlyingType); break; case GenericTypeImplementingIEquatable genericTypeImplementingIEquatable: EmitFieldEqualityTestingCode( compareCodeEmitter, nameof(DeepEqualityComparer.EquatableGenericsAreEqual), genericTypeImplementingIEquatable.GenericParameter); break; case PlainGenericType plainGenericType: EmitFieldEqualityTestingCode( compareCodeEmitter, nameof(DeepEqualityComparer.GenericsAreEqual), plainGenericType.GenericParameter); break; case FieldVariety fieldVariety: throw new InvalidOperationException($"Program logic exception: no code to handle {fieldVariety.GetType()} field variety."); } }