private MethodDefDeclaration InjectEqualsType(TypeDefDeclaration enhancedType,
                                                      StructuralEqualityAttribute config, ICollection <FieldDefDeclaration> ignoredFields)
        {
            IType genericTypeInstance = enhancedType.GetCanonicalGenericInstance();

            var existingMethod = enhancedType.Methods.FirstOrDefault <IMethod>(declaration =>
            {
                return(declaration.Name == "Equals" &&
                       declaration.IsPublic() &&
                       !declaration.IsStatic &&
                       declaration.ParameterCount == 1 &&
                       declaration.GetParameterType(0).Equals(genericTypeInstance));
            });

            if (existingMethod != null)
            {
                return(existingMethod.GetMethodDefinition());
            }

            // public virtual bool Equals( Typed other )
            var equalsDeclaration = new MethodDefDeclaration
            {
                Name              = "Equals",
                Attributes        = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConvention = CallingConvention.HasThis
            };

            enhancedType.Methods.Add(equalsDeclaration);
            equalsDeclaration.Parameters.Add(new ParameterDeclaration(0, "other", genericTypeInstance));
            equalsDeclaration.ReturnParameter = ParameterDeclaration.CreateReturnParameter(this.booleanType);
            CompilerGeneratedAttributeHelper.AddCompilerGeneratedAttribute(equalsDeclaration);

            using (var writer = InstructionWriter.GetInstance())
            {
                var methodBody = MethodBodyCreator.CreateModifiableMethodBody(writer, equalsDeclaration);
                writer.AttachInstructionSequence(methodBody.PrincipalBlock.AddInstructionSequence());

                writer.EmitInstruction(OpCodeNumber.Ldc_I4_0);
                writer.EmitInstructionLocalVariable(OpCodeNumber.Stloc, methodBody.ReturnVariable);

                this.InjectReferenceEquals(writer, methodBody, enhancedType);
                // Writer is either attached to the same sequence (value types) or to a new one which should check the structure.

                // return base.Equals(other) && this.field1 == other.field1 && ...;
                if (!config.IgnoreBaseClass && enhancedType.IsValueTypeSafe() != true)
                {
                    // Find the base method.
                    var baseEqualsMethod = this.instanceEqualsMethod.FindOverride(enhancedType.BaseTypeDef, true)
                                           .GetInstance(enhancedType.Module, enhancedType.BaseType.GetGenericContext());

                    // Do not invoke object.Equals();
                    if (baseEqualsMethod.DeclaringType.GetTypeDefinition() != this.objectTypeDef)
                    {
                        writer.EmitInstruction(OpCodeNumber.Ldarg_0);
                        writer.EmitInstruction(OpCodeNumber.Ldarg_1);
                        writer.EmitInstructionMethod(OpCodeNumber.Call, baseEqualsMethod);

                        // base.Equals(other) returned false, go to return.
                        writer.EmitBranchingInstruction(OpCodeNumber.Brfalse, methodBody.ReturnSequence);
                    }
                }

                foreach (var field in GetFieldsForComparison(enhancedType, ignoredFields))
                {
                    this.EmitEqualsField(writer, methodBody, field);
                }

                InjectCustomMethods(enhancedType, writer, methodBody);

                // return true;
                writer.EmitInstruction(OpCodeNumber.Ldc_I4_1);
                writer.EmitInstructionLocalVariable(OpCodeNumber.Stloc, methodBody.ReturnVariable);
                writer.EmitBranchingInstruction(OpCodeNumber.Br, methodBody.ReturnSequence);
                writer.DetachInstructionSequence();
            }

            return(equalsDeclaration);
        }