Sim*_*itt 5 c# conditional-operator
这段代码今天抓住了我:
clientFile.ReviewMonth == null ? null : MonthNames.AllValues[clientFile.ReviewMonth.Value]
Run Code Online (Sandbox Code Playgroud)
clientFile.Review月是一个字节?在失败的情况下,它的值为null.预期的结果类型是字符串.
此代码中的例外情况
public static implicit operator string(LookupCode<T> code)
{
if (code != null) return code.Description;
throw new InvalidOperationException();
}
Run Code Online (Sandbox Code Playgroud)
正在评估评估的右侧,然后隐式转换为字符串.
但我的问题是,为什么右手边被评估,显然只应评估左手边?(文档说明"只评估两个表达式中的一个.")
顺便说一句,解决方案是将null转换为字符串 - 这有效但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();
}
}
Run Code Online (Sandbox Code Playgroud)
无效操作不会发生在第三个操作数上,而是发生在第二个操作数上 - null(实际上 a default(LookupCode<object>))不是 type string,因此会调用隐式运算符。隐式运算符抛出无效操作异常。
如果您使用稍微修改的代码段,您可以很容易地看到这是真的:
string result = reviewMonth == null
? default(LookupCode<object>)
: "Does this get evaluated?".Dump();
Run Code Online (Sandbox Code Playgroud)
您仍然会收到无效操作异常,并且不会评估第三个操作数。这在生成的 IL 中当然很明显:两个操作数是两个独立的分支;他们俩都没有办法被处决。第一个分支还有另一个明显的痛苦:
ldnull
call LookupCode`1.op_Implicit
Run Code Online (Sandbox Code Playgroud)
它甚至没有隐藏在任何地方:)
解决方案很简单:使用显式类型的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>to的隐式转换string,但反之不存在,因此该类型LookupCode<T>优先于string。有趣的是,由于这一切都是在编译时完成的,赋值的 LHS 实际上有所不同:
string result = ... // Fails
var result = ... // Works fine, var is of type LookupCode<object>
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
454 次 |
| 最近记录: |