【问题标题】:Is pattern matching preferred over reference or value equality?模式匹配优先于引用还是值相等?
【发布时间】:2017-09-22 02:27:15
【问题描述】:

我看到很多关于如何在 C#7 中使用模式匹配的示例。一切看起来都不错。但是,我有一个问题,我似乎无法找到答案。

假设你有以下表达式:

if (a is null)

我的问题是:在 C#7 中是否更喜欢使用模式匹配而不是引用或值相等?

所以不要写:

if (a == null)

或:

if (a.Equals(null))

或:

if (object.Equals(a, null))

我怀疑a is null 会生成类似于最后一个表达式的内容。但是一般情况下会更喜欢切换到模式匹配吗?

如果我错了,请纠正我,这是一个主要基于意见的问题,但我似乎无法找到支持这一点的明确答案。

【问题讨论】:

  • 绝对不要写a.Equals(null),如果anull就会抛出,不返回true

标签: c# pattern-matching c#-7.0


【解决方案1】:

考虑以下四个代码sn-ps:

// 1
var x = "";
var y = x is null;

// 2
var x = "";
var y = x.Equals(null);

// 3
var x = "";
var y = object.Equals(x, null);

// 4
var x = "";
var y = x == null;

这些的IL分别是:

// 1
IL_0001: ldstr ""
IL_0006: stloc.0
IL_0007: ldnull
IL_0008: ldloc.0
IL_0009: call bool [mscorlib]System.Object::Equals(object, object)
IL_000e: stloc.1

// 2
IL_0001: ldstr ""
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldnull
IL_0009: call bool [mscorlib]System.Object::Equals(object, object)
IL_000e: stloc.1

// 3 
IL_0001: ldstr ""
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldnull
IL_0009: call bool [mscorlib]System.Object::Equals(object, object)
IL_000e: stloc.1

// 4
IL_0001: ldstr ""
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldnull
IL_0009: ceq
IL_000b: stloc.1

如您所见,前三个生成的代码几乎相同。 == 版本使用ceq 而不是.Equals()

我猜ceq 更快,因此x == null 是测试null 的最快方法。除此之外,它成为首选风格的问题。

【讨论】:

  • #2 的 IL 不正确,使用 callvirt System.String.Equals
  • 另外,我认为您应该在检查 IL 时使用发布模式,这会导致代码更短(没有 stloc.0 ldloc.0),并且可能会让您对代码的性能有更好的了解。
  • 在 LINQPad 5 (.NET 4.6) 中,== nullis null 在开启或关闭优化的情况下产生完全相同的 IL,但在左侧时 == null 仍然优化得更好一些可以在编译时进行评估,例如 default(string) == null
【解决方案2】:

无论是否启用优化,IL:

r = a is null;
IL_0004: ceq

r = a.Equals(null);
IL_0009: callvirt instance bool [mscorlib]System.Object::Equals(object)

r = Equals(a, null);
IL_0011: call bool [mscorlib]System.Object::Equals(object, object)

r = a == null;
IL_0019: ceq

r = a == default;
IL_001e: ceq

r = a == default(object);
IL_0023: ceq

r = ReferenceEquals(a, null);
IL_0028: ceq

我去除了每个表达式的重复噪音:

IL_0002: ldloc.0
IL_0003: ldnull
      // ...
IL_0006: stloc.1

如果a = nulla.Equals(null) 将抛出异常,因此远非首选。

很可能使用了a == null(缩写形式,期望的效果)。

需要注意的是操作符==和方法object.Equals可以被覆盖。

如果某些类对a == null 产生不良影响(显着性能下降或更糟 - 引发异常),那么您可以确定使用ReferenceEquals(a, null)

ReferenceEquals 始终只执行两个对象的比较,并且还使用 ceq
ReferenceEquals 缺少 call 对我来说意味着 AggressiveInlining(IL 大小:5 )。

你可以使用is吗?当然可以。

但这对我来说看起来很奇怪。您不检查a 类型,而是询问a 是否指向null。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-13
    • 2012-05-31
    • 2022-01-17
    • 2014-09-07
    相关资源
    最近更新 更多