这条代码今天引起了我的注意:
clientFile.ReviewMonth == null ? null : MonthNames.AllValues[clientFile.ReviewMonth.Value]
clientFile.Review月是一个字节?在失败的情况下,它的值为null。 预期的结果类型是字符串。
此代码中的例外
public static implicit operator string(LookupCode<T> code)
{
if (code != null) return code.Description;
throw new InvalidOperationException();
}
正在评估评估的右侧,然后隐式转换为字符串。
但我的问题是,为什么右手边被评估,显然只应评估左手边? (文档说明“只评估两个表达式中的一个。”)
顺便说一句,解决方案是将null转换为字符串 - 这有效但Resharper告诉我演员阵容是多余的(我同意)
编辑:这与“为什么我需要在编译之前添加演员”类型三元运算符问题不同。这里的要点是不需要强制转换来使其编译 - 只是为了使其正常工作。
答案 0 :(得分:4)
您忘记了隐式运算符是在编译时确定的。这意味着您拥有的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>
答案 1 :(得分:-2)
问题不在于三元评估的正确参数,它清楚地不是(尝试它,在隐式运算符中抛出一个不同的异常,代码仍会抛出j
因为{{1 }})
所以问题在于隐式演员何时发生。看来:
InvalidOperationException
等同于
((Nullable<byte>)(null)).Value
而不是
clientFile.ReviewMonth == null ? null : MonthNames.AllValues[clientFile.ReviewMonth.Value]
所以resharper在这里完全错了。