1

Final update: See this new question that narrows the problem down to generic structs.


I have some code that is building an Expression<Func<..>> that compares a value type to the nullable of the same value type. In the line that defines the expression, I'm getting the following InvalidOperationException:

The operands for operator 'Equal' do not match the parameters of method 'op_Equality'.

Any explanation for this?

Note: None of the overriden methods are ever called, the exception is thrown when the expression is being built by .NET

Here's the full code to reproduce the exception:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace ConsoleApplication1
{
    struct MyStruct<T> : IEquatable<MyStruct<T>>
        where T : struct
    {
        private readonly T _value;

        public MyStruct(T val) { this._value = val; }

        public override bool Equals(object obj)
        {
            return false;
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public static bool operator ==(MyStruct<T> a, MyStruct<T> b)
        {
            return false;
        }

        public static bool operator !=(MyStruct<T> a, MyStruct<T> b)
        {
            return false;
        }

        public bool Equals(MyStruct<T> other)
        {
            return false;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Expression<Func<MyStruct<int>, Nullable<MyStruct<int>>, bool>> expr = 
                (value, nullableValue) => value == nullableValue;
            Console.ReadLine();
        }
    }
}

Update: Greatly simplified the code that reproduces the exception


Also: Note that using a Func instead of an expression does not cause this exception:

Func<MyStruct<int>, Nullable<MyStruct<int>>, bool> func =
    (value, nullableValue) => value == nullableValue;

func(new MyStruct<int>(), null);

The above runs with no errors.


Update 3: Seems that removing the IEquatable<> interface doesn't prevent the exception, so the code can be further simplified.

Community
  • 1
  • 1
sinelaw
  • 16,205
  • 3
  • 49
  • 80
  • Shouldn't expression be expr = (value, nullableValue) => value == nullableValue.Value; – cheedep May 24 '13 at 18:41
  • Can you make the parameters to the expression both nullable? If you do that I imagine it would work. – Servy May 24 '13 at 18:41
  • @Servy, it will work. But I'm trying to understand what that exception means. Shouldn't this cause an implicit cast? – sinelaw May 24 '13 at 18:43
  • Well that doesn't match the signature then Expression, Nullable>, bool>> you are saying nullablevalue is nullable type – cheedep May 24 '13 at 18:44
  • @cheedep I'm trying to figure out why == without .Value causes an exception here. If the expression was a Func it doesn't happen (see updated question) – sinelaw May 24 '13 at 18:48
  • See Eric Lippert's comment on http://stackoverflow.com/questions/840150/linq-and-the-equality-operator-expression-of-type-system-int32-cannot-be-used. That applies perfectly to this too. – cheedep May 25 '13 at 03:09
  • @cheedep, thanks - that's an interesting comment by Eric there. I'm not certain it applies here as we're discussing lifting value types into nullables, not inheritance. – sinelaw May 28 '13 at 14:41

2 Answers2

1

EDIT: I am going to leave this answer here even though the question kind of changed, but may help with future folks looking into this:

The below corrects the issue when using Expression<Func<..>>, but the OP found that just using Func<...> solved this issue.


You are getting this because your operator== is looking for MyStruct<T> for both parameters, but you are trying to pass in Nullable<MyStruct<T>>.

You need to make an override to handle the Nullable<MyStruct<T>>

TyCobb
  • 8,909
  • 1
  • 33
  • 53
  • See my updated question - if you use just a Func (not an Expression) the same code works with no exceptions raised. – sinelaw May 24 '13 at 18:46
  • Also, I think anyway it lifts it to a Nullable, furthermore - if you remove the IEquatable interface it works. – sinelaw May 24 '13 at 18:50
  • @sinelaw Good to know. I tested my answer against your original Expression> in a test console app and it worked. Thanks for the information on just using Func<> – TyCobb May 24 '13 at 18:52
  • @sinelaw: `Func<>` works because the compiler is doing the heavy lifting (pun intended). With expressions, you have to do all the work, which usually involves a lot of explicit casts so that all types match perfectly. This answer is indeed correct. – Allon Guralnek May 28 '13 at 16:18
  • @AllonGuralnek, see my answer below - none of this makes sense to me because for non-genric structs everything works just fine. I'm going to post the full details in a new question that narrows it down to the difference between generic and non-generic types. – sinelaw May 28 '13 at 16:54
1

Apparently what's happening here is related to a difference between generic structs (in this case MyStruct<T>) vs. non-generic structs. If I change the struct to the following simpler non-generic class, the exception disappears:

struct MyStruct
{
    private readonly int _value;

    public MyStruct(int val) { this._value = val; }

    public override bool Equals(object obj) { return false; }
    public override int GetHashCode() { return base.GetHashCode(); }

    public static bool operator ==(MyStruct a, MyStruct b) { return false; }
    public static bool operator !=(MyStruct a, MyStruct b) { return false; }
}

For regular (non-generic) structs, the .NET is content with creating expressions that seem to pass nullables to the op_Equality method (which does not accept nullables).

For generic structs, it fails with that strange exception.

I still don't know why there's a difference between generic and non-generic structs, so I'm going to ask this in a separate question.

Community
  • 1
  • 1
sinelaw
  • 16,205
  • 3
  • 49
  • 80