/// <summary> /// Generates a new object equals method using the given typed equals overload. /// </summary> /// <param name="typeBuilder">The type builder to use.</param> /// <param name="equalsInfo">The typed equals function to call.</param> /// <returns>The created equals function.</returns> public static MethodInfo GenerateEquals( this TypeBuilder typeBuilder, MethodInfo equalsInfo) { var equals = typeBuilder.DefineMethod( EqualsInfo.Name, MethodAttributes.Public | MethodAttributes.Virtual, typeof(bool), new Type[] { typeof(object) }); var emitter = new ILEmitter(equals.GetILGenerator()); var falseLabel = emitter.DeclareLabel(); // Check type emitter.Emit(OpCodes.Ldarg_1); emitter.Emit(OpCodes.Isinst, typeBuilder); emitter.Emit(OpCodes.Brfalse_S, falseLabel); // Call typed equals method emitter.Emit(OpCodes.Ldarg_0); emitter.Emit(OpCodes.Ldarg_1); emitter.Emit(OpCodes.Unbox_Any, typeBuilder); emitter.EmitCall(equalsInfo); emitter.Emit(OpCodes.Ret); // Return false emitter.MarkLabel(falseLabel); emitter.EmitConstant(0); emitter.Emit(OpCodes.Ret); emitter.Finish(); typeBuilder.DefineMethodOverride(equals, EqualsInfo); return(equals); }
/// <summary> /// Generates a new typed equals method using the given fields. /// </summary> /// <param name="typeBuilder">The type builder to use.</param> /// <param name="fieldsToUse">All fields to use to compute the hash code.</param> /// <returns>The created equals function.</returns> public static MethodInfo GenerateEquals( this TypeBuilder typeBuilder, FieldInfo[] fieldsToUse) { var equals = typeBuilder.DefineMethod( EqualsInfo.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final, typeof(bool), new Type[] { typeBuilder }); var emitter = new ILEmitter(equals.GetILGenerator()); var falseLabel = emitter.DeclareLabel(); foreach (var field in fieldsToUse) { emitter.Emit(OpCodes.Ldarg_0); emitter.Emit(OpCodes.Ldflda, field); emitter.Emit(ArgumentOperation.Load, 1); emitter.Emit(OpCodes.Ldfld, field); emitter.EmitCall(field.FieldType.GetMethod( EqualsInfo.Name, new Type[] { field.FieldType })); // IMPORTANT: Each field can branch to the false label. However, if we // have a large number of fields, depending on the number of IL bytes we // emit per field, we may not be able to reach the false label using a // 1-byte branch. In that case, we should instead use a 4-byte branch. emitter.Emit( fieldsToUse.Length < 7 ? OpCodes.Brfalse_S : OpCodes.Brfalse, falseLabel); } emitter.EmitConstant(1); emitter.Emit(OpCodes.Ret); emitter.MarkLabel(falseLabel); emitter.EmitConstant(0); emitter.Emit(OpCodes.Ret); emitter.Finish(); var equalityInstance = typeof(IEquatable <>).MakeGenericType(typeBuilder); typeBuilder.AddInterfaceImplementation(equalityInstance); return(equals); }
/// <summary> /// Generates a new typed equals method using the given fields. /// </summary> /// <param name="typeBuilder">The type builder to use.</param> /// <param name="fieldsToUse">All fields to use to compute the hash code.</param> /// <returns>The created equals function.</returns> public static MethodInfo GenerateEquals( this TypeBuilder typeBuilder, FieldInfo[] fieldsToUse) { var equals = typeBuilder.DefineMethod( EqualsInfo.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final, typeof(bool), new Type[] { typeBuilder }); var emitter = new ILEmitter(equals.GetILGenerator()); var falseLabel = emitter.DeclareLabel(); foreach (var field in fieldsToUse) { emitter.Emit(OpCodes.Ldarg_0); emitter.Emit(OpCodes.Ldflda, field); emitter.Emit(ArgumentOperation.Load, 1); emitter.Emit(OpCodes.Ldfld, field); emitter.EmitCall(field.FieldType.GetMethod( EqualsInfo.Name, new Type[] { field.FieldType })); emitter.Emit(OpCodes.Brfalse_S, falseLabel); } emitter.EmitConstant(1); emitter.Emit(OpCodes.Ret); emitter.MarkLabel(falseLabel); emitter.EmitConstant(0); emitter.Emit(OpCodes.Ret); emitter.Finish(); var equalityInstance = typeof(IEquatable <>).MakeGenericType(typeBuilder); typeBuilder.AddInterfaceImplementation(equalityInstance); return(equals); }