System.Linq.Dynamic.Core icon indicating copy to clipboard operation
System.Linq.Dynamic.Core copied to clipboard

'&&' and '||' operators incompatible between operand type 'bool' and type with implicit conversation to 'bool'

Open n-shay opened this issue 3 years ago • 0 comments

Here is what to include in your request to make sure we implement a solution as quickly as possible.

1. Description

In order to explain the use case, I will track back to how we got there. I needed a way to evaluate this type of expression SomeBool == 1 or SomeBool != 0. To solve that, we create in runtime a new type that swaps bool properties with BooleanVariable type. That BooleanVariable type has operator overloads to support comparison with int hence the expression above works.

Now, even when the expression is a bit more complex, for example SomeBool1 == 1 && SomeBool2 == 1, it works well. But in doing so it broke the standard usage of logical boolean operators and we run into a parsing exception in an expression like SomeBool1 && SomeBool2.

2. Exception

Exception message:

System.Linq.Dynamic.Core.Exceptions.ParseException
Operator '&&' incompatible with operand types 'BooleanVariable' and 'Boolean'

Stack trace:

   at System.Linq.Dynamic.Core.Parser.ExpressionParser.CheckAndPromoteOperands(Type signatures, TokenId opId, String opName, Expression& left, Expression& right, Int32 errorPos) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 2082
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseAndOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 289
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseOrOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 265
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseLambdaOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 245
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseNullCoalescingOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 232
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.ParseConditionalOperator() in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 216
   at System.Linq.Dynamic.Core.Parser.ExpressionParser.Parse(Type resultType, Boolean createParameterCtor) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\Parser\ExpressionParser.cs:line 154
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(Type delegateType, ParsingConfig parsingConfig, Boolean createParameterCtor, ParameterExpression[] parameters, Type resultType, String expression, Object[] values) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicExpressionParser.cs:line 122
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(ParsingConfig parsingConfig, Boolean createParameterCtor, ParameterExpression[] parameters, Type resultType, String expression, Object[] values) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicExpressionParser.cs:line 97
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(Boolean createParameterCtor, ParameterExpression[] parameters, Type resultType, String expression, Object[] values) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicExpressionParser.cs:line 450
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(Boolean createParameterCtor, Type itType, Type resultType, String expression, Object[] values) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicExpressionParser.cs:line 201
   at System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(Type itType, Type resultType, String expression, Object[] values) in C:\Dev\GitHub\System.Linq.Dynamic.Core\src\System.Linq.Dynamic.Core\DynamicExpressionParser.cs:line 305
   at Program.Main()

3. Fiddle or Project

Fiddle: https://dotnetfiddle.net/1UNldU

Or code for reference:

using System;
using System.Linq.Dynamic.Core;
					
public class Program
{
	public static void Main()
	{
		var model = new Model{
			SomeBool1 = new BooleanVariable(true),
			SomeBool2 = true
		};
		
		Console.WriteLine(model.SomeBool1 && model.SomeBool2);
		
		var expr = DynamicExpressionParser.ParseLambda(typeof(Model), null, "SomeBool1 && SomeBool2");
		var compiled = expr.Compile();
		var result = compiled.DynamicInvoke(model);
		
		Console.WriteLine(result);
	}
}
			
public class Model 
{
	public BooleanVariable SomeBool1 {get; set;}

	public bool SomeBool2 {get; set;}
}

public readonly struct BooleanVariable : IEquatable<bool>, IEquatable<BooleanVariable>, IConvertible
{
        private readonly bool innerValue;

        public BooleanVariable(bool innerValue)
        {
            this.innerValue = innerValue;
        }

        public override bool Equals(object obj)
        {
            return obj is bool val && this.Equals(val);
        }

        public bool Equals(bool other)
        {
            return this.innerValue == other;
        }

        public bool Equals(BooleanVariable other)
        {
            return this.Equals(other.innerValue);
        }

        public override int GetHashCode()
        {
            return this.innerValue.GetHashCode();
        }

        public static implicit operator bool(BooleanVariable v) => v.innerValue;

        public static implicit operator BooleanVariable(bool b) => new BooleanVariable(b);

        public static bool operator true(BooleanVariable v) => v.innerValue;

        public static bool operator false(BooleanVariable v) => !v.innerValue;

        // comparison to bool
        public static bool operator ==(BooleanVariable a, bool b) => a.innerValue == b;

        public static bool operator !=(BooleanVariable a, bool b) => a.innerValue != b;

        // comparison to int
        public static bool operator ==(BooleanVariable a, int b) => a.innerValue == (b != 0);

        public static bool operator !=(BooleanVariable a, int b) => a.innerValue != (b != 0);

        // comparison to decimal
        public static bool operator ==(BooleanVariable a, decimal b) => a.innerValue == (b != 0);

        public static bool operator !=(BooleanVariable a, decimal b) => a.innerValue != (b != 0);

        // To/from self
        public static bool operator ==(BooleanVariable a, BooleanVariable b) => a.innerValue == b.innerValue;

        public static bool operator !=(BooleanVariable a, BooleanVariable b) => a.innerValue != b.innerValue;

        // see remaining code in Fiddle...
}

4. Any further technical details

Of course, our usage is more complex than that with several types replaced in the same pattern, such as enums for comparison with strings (for example, Color == 'Red').

n-shay avatar Sep 01 '22 18:09 n-shay