我有这些扩展方法和枚举类型:
public static bool IsOneOf<T>(this T thing, params T[] things)
{
return things.Contains(thing);
}
public static bool IsOneOf<T>(this T? thing, params T[] things) where T : struct
{
return thing.HasValue && things.Contains(thing.Value);
}
public enum Color { Red, Green, Blue }
下面的第一个if
编译;第二个不是:
if ((x.Y?.Color).IsOneOf(Color.Red, Color.Green))
;
if (x.Y?.Color.IsOneOf(Color.Red, Color.Green))
;
它们仅因额外的一组括号而异。为什么我必须这样做?
起初我怀疑它是在执行从 bool?
到 bool
然后返回到 bool?
的双重隐式转换,但是当我删除第一个扩展方法,它提示没有从 bool
到 bool?
的隐式转换。然后我检查了 IL,没有类型转换。反编译回 C# 会产生如下所示的内容:
if (!(y != null ? new Color?(y.Color) : new Color?()).IsOneOf<Color>(new Color[2]
{
Color.Red,
Color.Green
}));
这对于我正在运行的 CLR 版本以及我所期望的来说很好。我没想到的是 x.Y?.Color.IsOneOf(Color.Red, Color.Green)
无法编译。
这是怎么回事?是否只是语言的实现方式需要 ()
?
更新
这是显示上下文错误的屏幕截图。这让我更加困惑。该错误实际上是有道理的;但(在我看来)没有的是为什么第 18 行不会有同样的问题。
最佳答案
首先,这种行为在我看来是故意的。很少有人向可空类型添加扩展方法,而且人们在一个表达式中混合空条件访问和常规成员访问是很常见的,因此语言倾向于后者。
考虑以下示例:
class B { bool c; }
class A { B b; }
...
A a;
var output = a?.b.c; // infers bool?, throws NPE if (a != null && a.b == null)
// roughly translates to
// var output = (a == null) ? null : a.b.c;
同时
A a;
var output = (a?.b).c; // infers bool, throws NPE if (a == null || a.b == null)
// roughly translates to
// var output = ((a == null) ? null : a.b).c;
然后是
A a;
var output = a?.b?.c; // infers bool?, *cannot* throw NPE
// roughly translates to
// var output = (a == null) ? null : (a.b == null) ? null : a.b.c;
// and this is almost the same as
// var output = (a?.b)?.c; // infers bool?, cannot throw NPE
// Only that the second `?.` is forced to evaluate every time.
这里的设计目标似乎是帮助区分 a?.b.c
和 a?.b?.c
。如果 a
为 null,我们希望在none 的情况下得到 NPE。为什么?因为在 a
之后直接有一个 null 条件。因此,.c
部分必须仅在 a
不为 null 时才进行评估,从而使成员访问依赖于先前的 null 条件结果。通过添加显式括号,(a?.b).c
我们强制编译器尝试访问 (a?.b)
的 .c
> 无论 a
是否为 null,都防止它“短路”整个表达式为 null。 (使用@JamesBuck -s 的话)
在您的情况下,x.Y?.Color.IsOneOf(Color.Red, Color.Green)
类似于 a?.b.c
。仅当 x.Y
为not null,因此将表达式的类型包装在 Nullable 中以处理 x.Y
为 null 的情况。因为 while 的计算结果为 bool?
而不是 bool
,所以它不能用作 if 语句中的测试。
关于c# - 为什么我必须在 null 条件表达式周围放置 () 才能使用正确的方法重载?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37422512/