我可以重构此代码(流行的as/null check
模式)
var a = b as MyType;
if(a != null) { ... }
..变成一个不错的"is" type pattern expression:
if(b is MyType a) { ... }
..这很酷...我想...是吗?
但是现在我也在考虑重构
var a = SomeMethod();
if(a != null) { ... }
.. into:
if(SomMethod() is MyType a) { ... }
注意:没有as
,并且 SomeMethod()已经返回MyType 。看起来像(伪代码)if(A is A)
,很容易造成混淆,不是吗?
第一个重构是合法的,但是第二个呢?我不是要检查自己的IL专家,并且C#7.0功能对我来说仍然是新的。也许有我尚未发现的问题?
答案 0 :(得分:3)
很显然,这两个实现非常相似,在内存,分配和周期中,差异可以忽略不计。
编译器基本上对它们进行以下处理(对于引用类型)
第一
MyType myType = SomeMethod();
if (myType != null)
{
Console.WriteLine(myType.ToString());
}
第二
MyType myType2;
if ((object)(myType2 = SomeMethod()) != null)
{
Console.WriteLine(myType2.ToString());
}
使用 IL
可能更好第一
IL_0000: ldarg.0
IL_0001: call instance class C/MyType C::SomeMethod()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0015
IL_000a: ldloc.0
IL_000b: callvirt instance string[mscorlib] System.Object::ToString()
IL_0010: call void[mscorlib] System.Console::WriteLine(string)
第二
IL_0015: ldarg.0
IL_0016: call instance class C/MyType C::SomeMethod()
IL_001b: dup
IL_001c: stloc.1
IL_001d: brfalse.s IL_002a
IL_001f: ldloc.1
IL_0020: callvirt instance string[mscorlib] System.Object::ToString()
IL_0025: call void[mscorlib] System.Console::WriteLine(string)
注意 :您可以查看反汇编,IL和jit-asm here
IL 的区别基本上是2个操作码:
dup
:将当前最高值复制到评估堆栈中,然后将副本推入评估堆栈中。Ldloc
:将特定索引处的局部变量加载到评估堆栈中。在 Jitted 时,很可能将优化转换为相同的指令
摘要
is
版本更整洁,我想它更简洁。答案 1 :(得分:1)
我发现编译器非常智能。
is
表达式有几种翻译形式:
if(SomeMethod() is MyType a) {...}
SomeMethod
返回MyType
MyType
没有覆盖运算符==,并且未使用变量a
if (SomeMethod() != null) {...}
MyType
具有覆盖运算符==,但未使用变量a
if ((object)(SomeMethod()) != null) {...}
MyType
没有覆盖运算符==,并且使用了变量a
MyType a;
if ((a = SomeMethod()) != null) {...}
MyType
具有覆盖运算符==,并且使用了变量a
MyType a;
if ((object)(a = SomeMethod()) != null) {...}
SomeMethod
返回其他类型,例如object
未使用变量a
if (SomeMethod() is MyType) {...}
MyType
没有覆盖运算符==,并且使用了变量a
MyType a;
if ((a = (SomeMethod() as MyType)) != null) {...}
MyType
具有覆盖运算符==,并且使用了变量a
MyType a;
if ((object)(a = (SomeMethod() as MyType)) != null) {...}
顺便说一句,您可以通过ILSpy或类似方式检查所有这些变体。
答案 2 :(得分:0)
我不会使用它对引用类型执行身份转换,因为对于将来的读者来说,空值检查会更加直观。
对于可空类型,情况则完全不同。给定struct S
,然后
void foo(S? p)
{
if (p is S s) {
bar(s);
}
}
等同于
void foo(S? p)
{
if (p.HasValue) {
bar(p.GetValueOrDefault());
}
}
并避免进行GetValueOrDefault()
调用(或更糟糕的是,读取Value
属性,该属性执行另一个空检查)对IMO很有用,并大大提高了可读性。>