Expression.Condition: Argument types do not match

c# expression-trees ternary-operator

Question

I am trying to use an expression to create this:

bool.Parse("true") ? default(int?) : 1

BTW, I'm using bool.Parse("true") just to keep VS from complaining about unreachable code paths, so just assume it uses a constant true. When I write my expression like this...

Expression.Condition(Expression.Constant(true), Expression.Default(typeof(int?)),
    Expression.Constant(1))

...I get the error Argument types do not match. I am pretty certain that I'm aware of what's happening, so I changed my expression to do this:

Expression.Condition(Expression.Constant(true), Expression.Default(typeof(int?)),
    Expression.New(typeof(int?).GetConstructor(new[] { typeof(int) }), Expression.Constant(1)));

It works, but I can't say I like writing an expression that's the equivalent of this:

bool.Parse("true") ? default(int?) : new int?(1)

Is there a way to get this ternary expression to work without creating a new instance of int?? Perhaps it's okay to do this because c# is implicitly creating a new instance in my concrete example anyway?

Edit

I should note that I am only using Expression.Constant() to emulate a return value from a MethodCallExpression in order to simplify my code example. Therefore, anything suggesting the use of constant values as a solution will not work in this case.

Accepted Answer

You can create a cast instead of creating an instance with new, i.e.

bool.Parse("true") ? default(int?) : (int?)(1)

like this:

Expression.Condition(
    Expression.Constant(true)
,   Expression.Default(typeof(int?))
,   Expression.Convert(Expression.Constant(1), typeof(int?))
)

Demo.


Popular Answer

Type the constant expression with:

Expression.Condition(Expression.Constant(true), 
  Expression.Default(typeof(int?)), 
  Expression.Constant(1, typeof(int?)))

Or type the conditional expression with:

Expression.Condition(Expression.Constant(true), 
  Expression.Default(typeof(int?)), 
  Expression.Constant(1),
  typeof(int?))

The first changes how the constant works (in both cases the value is stored as a boxed int, just as it would be for an int constant, and the unboxing, if needed, is to int?.

The second makes the condition itself store the type both operands must be cast to.

Note that there may not be any actual casting, especially with the first two, e.g. if you compile you are likely to compile to a form which acts on int? directly, so while the expression object would require a cast to act upon, the compiled code it produces does not.

Note also that some providers can't handle typed conditions, making the last option unavailable. E.g. EnumerableQuery can't deal with them (see https://github.com/dotnet/corefx/issues/3607).



Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why