public static BinaryExpression Coalesce(Expression left, Expression right, LambdaExpression conversion)
{
RequiresCanRead(left, nameof(left));
RequiresCanRead(right, nameof(right));
if (conversion == null)
{
Type resultType = ValidateCoalesceArgTypes(left.Type, right.Type);
return new SimpleBinaryExpression(ExpressionType.Coalesce, left, right, resultType);
}
if (left.Type.GetTypeInfo().IsValueType && !left.Type.IsNullableType())
{
throw Error.CoalesceUsedOnNonNullType();
}
Type delegateType = conversion.Type;
Debug.Assert(typeof(System.MulticastDelegate).IsAssignableFrom(delegateType) && delegateType != typeof(System.MulticastDelegate));
MethodInfo method = delegateType.GetMethod("Invoke");
if (method.ReturnType == typeof(void))
{
throw Error.UserDefinedOperatorMustNotBeVoid(conversion, nameof(conversion));
}
ParameterInfo[] pms = method.GetParametersCached();
Debug.Assert(pms.Length == conversion.ParameterCount);
if (pms.Length != 1)
{
throw Error.IncorrectNumberOfMethodCallArguments(conversion, nameof(conversion));
}
// The return type must match exactly.
// We could weaken this restriction and
// say that the return type must be assignable to from
// the return type of the lambda.
if (!TypeUtils.AreEquivalent(method.ReturnType, right.Type))
{
throw Error.OperandTypesDoNotMatchParameters(ExpressionType.Coalesce, conversion.ToString());
}
// The parameter of the conversion lambda must either be assignable
// from the erased or unerased type of the left hand side.
if (!ParameterIsAssignable(pms[0], left.Type.GetNonNullableType()) &&
!ParameterIsAssignable(pms[0], left.Type))
{
throw Error.OperandTypesDoNotMatchParameters(ExpressionType.Coalesce, conversion.ToString());
}
return new CoalesceConversionBinaryExpression(left, right, conversion);
}