Jay Bazuzi
Jay Bazuzi

Reputation: 46536

How do I build an expression to call an equality operator from a base class?

If the type in question implements operator ==(), then I can easily build an expression to call it. But if the operator is defined in the base class, it doesn't work - see how three of the assertions pass but one fails.

Is there a correct way to do this?

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {
        Assert.IsTrue(new A(42) == new A(42)); // PASS
        Assert.IsTrue(ExecuteOperatorEqual(new A(42), new A(42))); // PASS
        Assert.IsTrue(new B(42) == new B(42)); // PASS
        Assert.IsTrue(ExecuteOperatorEqual(new B(42), new B(42))); // FAIL
    }

    static bool ExecuteOperatorEqual<T>(T item1, T item2)
    {
        var expression = Expression.Lambda<Func<bool>>(
            Expression.Equal(
                Expression.Constant(item1),
                Expression.Constant(item2)));
        return expression.Compile()();
    }
}

class A
{
    private readonly int _value;

    public A(int value)
    {
        _value = value;
    }

    public static bool operator ==(A left, A right) => left._value == right._value;

    public static bool operator !=(A left, A right) => left._value != right._value;
}

class B : A
{
    public B(int value) : base(value)
    {
    }
}

Upvotes: 2

Views: 224

Answers (1)

Paulo Prestes
Paulo Prestes

Reputation: 494

I'm not sure if it does make sense in your context, but you can fix that problem passing the MethodInfo to you Expression.Equal

    static bool ExecuteOperatorEqual<T>(T item1, T item2)
    {
        BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
        var equalityMethod = typeof(T).GetMethod("op_Equality", bindingAttr, null, new Type[] { typeof(T), typeof(T) }, null);

        var expression = Expression.Lambda<Func<bool>>(
            Expression.Equal(
                Expression.Constant(item1),
                Expression.Constant(item2),
                false,
                equalityMethod
                ));
        return expression.Compile()();
    }

I've had reflected System.Core.dll and the Parameter class does not search for any overloaded operator, as you can see below:

    private static MethodInfo GetUserDefinedBinaryOperator(ExpressionType binaryType, Type leftType, Type rightType, string name)
    {
        Type[] types = new Type[]
        {
            leftType,
            rightType
        };
        Type nonNullableType = leftType.GetNonNullableType();
        Type nonNullableType2 = rightType.GetNonNullableType();
        BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
        MethodInfo methodInfo = nonNullableType.GetMethodValidated(name, bindingAttr, null, types, null);
        if (methodInfo == null && !TypeUtils.AreEquivalent(leftType, rightType))
        {
            methodInfo = nonNullableType2.GetMethodValidated(name, bindingAttr, null, types, null);
        }
        if (Expression.IsLiftingConditionalLogicalOperator(leftType, rightType, methodInfo, binaryType))
        {
            methodInfo = Expression.GetUserDefinedBinaryOperator(binaryType, nonNullableType, nonNullableType2, name);
        }
        return methodInfo;
    }

Adding the BindingFlags.FlattenHierarchy would find the equality operator. They must have a reason to not add that to the .Net.

Upvotes: 1

Related Questions