c# - 为什么我必须在 null 条件表达式周围放置 () 才能使用正确的方法重载?

标签 c# c#-6.0 null-conditional-operator

我有这些扩展方法和枚举类型:

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? 的双重隐式转换,但是当我删除第一个扩展方法,它提示没有从 boolbool? 的隐式转换。然后我检查了 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 行不会有同样的问题。

enter image description here

最佳答案

首先,这种行为在我看来是故意的。很少有人向可空类型添加扩展方法,而且人们在一个表达式中混合空条件访问和常规成员访问是很常见的,因此语言倾向于后者。

考虑以下示例:

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.ca?.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/

相关文章:

c# - 如何在 net.core 3.1 中连接打开的 excel 应用程序?

c# - "C# Language Specifications"是否针对 C# 6.0 或 7.0 进行了更新?

c# - 方便还是 "lazy programming"IList管理-保护IList.Add方法

c# - 用元素填充 List<> 的方法

C#6 : nameof() current property in getter/setter

c# - 自动实现的属性必须同时定义 get 和 set 访问器

c# - 为什么 IEnumerable?.First() 不起作用?

c# - 空条件运算符不适用于泛型方法中的 Func<T>

c# - 使用空条件 bool ?在 if 语句中

c# - 是否可以使用 c# 类创建此 JSON 格式?