System.InvalidOperationException when evaluating "<System.Double> != '<System.String>'"

Sep 15, 2016 at 5:38 PM
Using latest source code (Mar 24, 2014)

The following code
var expression = new CompiledExpression("0.500 != 'Test'");            
var result = expression.Eval();
will result in the following exception

A first chance exception of type 'System.InvalidOperationException' occurred in System.Core.dll

Additional information: The binary operator NotEqual is not defined for the types 'System.Double' and 'System.String'.

I'd just expect a 'false'

Any workaround to this?

Thanks
Joshua
Coordinator
Sep 15, 2016 at 10:32 PM
Hi Joshua,

In short, no.

csharpeval stays as close to the C# specification as possible. C# is strongly typed, and direct comparison of number and string types is not allowed. The expression above will get an error in Visual Studio IDE and a compile-time error if you try to compile it.

If you really need to compare arbitrary types, perhaps you can create a function on a static class, eg. Comparisons.AreEqual(object, object) and reference the Comparisons class as "comp".
   static class Comparisons
    {
        public static bool AreNotEqual(object obj1, object obj2)
        {
            var type1 = obj1.GetType();
            var type2 = obj2.GetType();

            if (type1 == typeof(double) && type2 == typeof(string))
            {
                return obj1.ToString() != (string)obj2;
            }
            if (type2 == typeof(double) && type1 == typeof(string))
            {
                return obj2.ToString() != (string)obj1;
            }
            if (type2 == typeof(double) && type1 == typeof(double))
            {
                return (double)obj2 != (double)obj1;
            }
            // Object reference equality!
            return obj1 != obj2;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var t = new TypeRegistry();
            t.RegisterSymbol("comp", typeof(Comparisons));
            var expression = new CompiledExpression("comp.AreNotEqual(0.500, 'Test')");
            expression.TypeRegistry = t;
            var result = expression.Eval();

            var expression2 = new CompiledExpression("comp.AreNotEqual(0.500, 0.5)");
            expression2.TypeRegistry = t;
            var result2 = expression2.Eval();

            var expression3 = new CompiledExpression("comp.AreNotEqual('Test', 'Test')");
            expression3.TypeRegistry = t;
            var result3 = expression3.Eval();

            Console.WriteLine(result);
            Console.WriteLine(result2);
            Console.WriteLine(result3);

            Console.ReadLine();
        }
    }
But you would expect "true" from your first expression, since you are testing Not Equal.

This is also rather unwieldy, since you have to consider every possible type combination you expect. Note that you have to cast the object to the appropriate type before attempting the comparison.

It would be possible to handle this at the compilation level, to check if one side of the expression for a string type and cast accordingly, but this will assume that the expression types are fixed. If you pass an object type, it will use an object reference comparison. Once compiled, your expression function delegate will expect a certain type to the variables referenced in the expression, the types that were seen during compilation time.

You could recompile the expression but that can be costly if used repeatedly.

The same goes for the AreNotEqual method we used above. It will only work for fixed, non-object variable types or literals.