'&&' and '||' operators incompatible between operand type 'bool' and type with implicit conversation to 'bool'
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').