private static AnalyzeTypeIsResult AnalyzeTypeIs(Expression operand, Type testType)
{
Type operandType = operand.Type;
// An expression is either of type void, or it isn't.
if (operandType == typeof(void))
{
return testType == typeof(void) ? AnalyzeTypeIsResult.KnownTrue : AnalyzeTypeIsResult.KnownFalse;
}
if (testType == typeof(void))
{
return AnalyzeTypeIsResult.KnownFalse;
}
//
// Type comparisons treat nullable types as if they were the
// underlying type. The reason is when you box a nullable it
// becomes a boxed value of the underlying type, or null.
//
Type nnOperandType = operandType.GetNonNullableType();
Type nnTestType = testType.GetNonNullableType();
//
// See if we can determine the answer based on the static types
//
// Extensive testing showed that Type.IsAssignableFrom,
// Type.IsInstanceOfType, and the isinst instruction were all
// equivalent when used against a live object
//
if (nnTestType.IsAssignableFrom(nnOperandType))
{
// If the operand is a value type (other than nullable), we
// know the result is always true.
if (operandType.GetTypeInfo().IsValueType && !operandType.IsNullableType())
{
return AnalyzeTypeIsResult.KnownTrue;
}
// For reference/nullable types, we need to compare to null at runtime
return AnalyzeTypeIsResult.KnownAssignable;
}
// We used to have an if IsSealed, return KnownFalse check here.
// but that doesn't handle generic types & co/contravariance correctly.
// So just use IsInst, which we know always gives us the right answer.
// Otherwise we need a full runtime check
return AnalyzeTypeIsResult.Unknown;
}
}