c#三元运算符在不应该't时求值
本文关键字:不应该 运算符 三元 | 更新日期: 2023-09-27 18:17:57
今天这段代码把我难住了:
clientFile.ReviewMonth == null ? null : MonthNames.AllValues[clientFile.ReviewMonth.Value]
clientFile。评论月是一个字节吗?在失败的情况下,它的值为空。期望的结果类型是字符串。
异常在以下代码
public static implicit operator string(LookupCode<T> code)
{
if (code != null) return code.Description;
throw new InvalidOperationException();
}
正在对求值的右侧求值,然后隐式地转换为字符串。
但我的问题是,为什么只计算左边的值而计算右边呢?(文档声明"两个表达式中只计算一个。")
顺便说一下,解决方案是将null强制转换为string -这是有效的,但Resharper告诉我强制转换是多余的(我同意) 编辑:这与"为什么我需要在编译之前添加强制转换"类型三元运算符问题不同。这里的要点是,不需要强制转换就可以使其编译—只需使其正确工作即可。您忘记了隐式操作符是在编译时确定的。这意味着您拥有的null
实际上是LookupCode<T>
类型(由于类型推断在三元操作符中工作的方式),并且需要使用隐式操作符将其强制转换为字符串;这就是为什么会出现异常。
void Main()
{
byte? reviewMonth = null;
string result = reviewMonth == null
? null // Exception here, though it's not easy to tell
: new LookupCode<object> { Description = "Hi!" };
result.Dump();
}
class LookupCode<T>
{
public string Description { get; set; }
public static implicit operator string(LookupCode<T> code)
{
if (code != null) return code.Description;
throw new InvalidOperationException();
}
}
无效操作不会发生在第三个操作数上,它发生在第二个操作数上- null
(实际上是default(LookupCode<object>)
)不是string
类型,因此调用隐式操作符。隐式操作符抛出无效操作异常。
你可以很容易地看到这是真的,如果你使用一段稍微修改的代码:
string result = reviewMonth == null
? default(LookupCode<object>)
: "Does this get evaluated?".Dump();
仍然会得到一个无效的操作异常,并且第三个操作数不会被求值。这在生成的IL中当然是显而易见的:两个操作数是两个独立的分支;他们不可能同时被处死。第一个分支有另一个很明显的东西:
ldnull
call LookupCode`1.op_Implicit
它甚至没有隐藏在任何地方:)
解决方案很简单:使用显式类型的null
, default(string)
。r#完全是错误的——在这种情况下(string)null
和null
不一样,r#在这种情况下有错误的类型推断。
当然,这些都在c#规范(14.13 -条件运算符)中有描述:
?:操作符的第二个和第三个操作数控制条件表达式的类型。
设X和Y是第二个和第三个操作数的类型。然后,
- 如果X和Y是相同的类型,那么这就是条件表达式的类型。
- 否则,如果存在从X到Y的隐式转换(§13.1),但不存在从Y到X的隐式转换,则Y是的类型
- 否则,如果存在从Y到X的隐式转换(§13.1),但不存在从X到Y的隐式转换,则X是的类型
- 否则,无法确定表达式类型,并发生编译时错误。
在您的示例中,存在从LookupCode<T>
到string
的隐式转换,而不是相反,因此类型LookupCode<T>
优先于string
。有趣的是,由于这些都是在编译时完成的,赋值的LHS实际上是有区别的:
string result = ... // Fails
var result = ... // Works fine, var is of type LookupCode<object>
问题不是关于三进制求值的正确参数,它显然不是(尝试一下,在隐式操作符中抛出不同的异常,代码仍然会抛出InvalidOperationException
,因为((Nullable<byte>)(null)).Value
)
所以问题是隐式强制转换何时发生。似乎:
clientFile.ReviewMonth == null ? null : MonthNames.AllValues[clientFile.ReviewMonth.Value]
等于
(string)(clientFile.ReviewMonth == null ? (Nullable<LookupCode<byte>>)null : (Nullable<LookupCode<byte>>)MonthNames.AllValues[clientFile.ReviewMonth.Value]);
而不是
clientFile.ReviewMonth == null ? (string)null : (string)MonthNames.AllValues[clientFile.ReviewMonth.Value]);
所以resharper在这里是完全错误的