Reputation: 46536
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
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