为什么运营商';作为';比';是'
本文关键字:作为 运营商 为什么 | 更新日期: 2023-09-27 18:00:51
我有下一个类:
public class A
{
public int MyProperty { get; set; }
}
和以下代码在主:
object obj = new A();
Stopwatch sw = Stopwatch.StartNew();
var res = obj as A;
if (res != null)
{
res.MyProperty = 10;
Console.WriteLine("obj is A (as)" + sw.Elapsed);
}
sw.Stop();
Stopwatch sw2 = Stopwatch.StartNew();
if (obj.GetType() == typeof(A))
{
A a = (A)obj;
a.MyProperty = 10;
Console.WriteLine("obj is A (GetType)" + sw2.Elapsed);
}
sw2.Stop();
Stopwatch sw3 = Stopwatch.StartNew();
var isA = obj is A;
if (isA)
{
A a = (A)obj;
a.MyProperty = 19;
Console.WriteLine("obj is A (is)" + sw3.Elapsed);
}
sw3.Stop();
Console.ReadKey();
结果是:
obj is A (as) 00:00:00.0000589
obj is A (GetType) 00:00:00.0000024
obj is A (is) 00:00:00.0000006
关键是运算符"is"的工作速度总是比"as"快。为什么"as"比"is"慢?甚至GetType((比"as"更快。与"is"和GetType((相比,代表导致此类延迟的"as"运算符的是什么。
我猜这是第一个必须向控制台打开流的Console.Write
,所以这需要更多的时间。
不管怎样,写到控制台比做铸件要花更多的时间,所以你根本无法从测试中得出任何关于铸件的结论。
执行十亿次强制转换,并且不对每个强制转换的控制台写入任何内容,这会给你一个更合理的结果:
object obj = new A();
int iterations = 1000000000;
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) {
var res = obj as A;
if (res != null) {
res.MyProperty = 10;
}
}
sw.Stop();
Console.WriteLine("obj is A (as)" + sw.Elapsed);
Stopwatch sw2 = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) {
if (obj.GetType() == typeof(A)) {
A a = (A)obj;
a.MyProperty = 10;
}
}
sw2.Stop();
Console.WriteLine("obj is A (GetType)" + sw2.Elapsed);
Stopwatch sw3 = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) {
var isA = obj is A;
if (isA) {
A a = (A)obj;
a.MyProperty = 19;
}
}
sw3.Stop();
Console.WriteLine("obj is A (is)" + sw3.Elapsed);
示例输出:
obj is A (as)00:00:00.3937249
obj is A (GetType)00:00:00.3452988
obj is A (is)00:00:01.0193541
如果测量结果显示"as"比"is"慢,则测量结果不正确。
不可能出现这种情况的原因是"as"answers"is"关键字都会生成isinst
IL指令,但"is"指令会生成对返回值的额外检查。
您不需要执行任何计时来确定这一点,因为您可以使用反射器检查生成的IL代码。
例如,这种方法:
static bool isTest(object value)
{
return value is Random;
}
生成此IL:
.method private hidebysig static bool isTest(object 'value') cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: isinst [mscorlib]System.Random
L_0006: ldnull
L_0007: cgt.un
L_0009: ret
}
而这个:
static object asTest(object value)
{
return value as Random;
}
生成:
.method private hidebysig static object asTest(object 'value') cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: isinst [mscorlib]System.Random
L_0006: ret
}
"is"关键字生成与"as"关键字相同的IL以及一些附加指令;因此,对于这种用法,"is">必须比"as"慢。
我认为这可能是因为您也在设置MyProperty
,所以第一次这样做时,属性setter是JIT编译的。
试着运行这个代码,有注释行和没有注释行,并检查差异:
object obj = new A();
// uncomment these lines and see the difference
// A tmp = new A();
// tmp.MyProperty = 100;
Stopwatch sw = Stopwatch.StartNew();
var res = obj as A;
if (res != null) {
res.MyProperty = 10;
}
sw.Stop();
Console.WriteLine("as : " + sw.Elapsed);
Stopwatch sw2 = Stopwatch.StartNew();
if (obj.GetType() == typeof(A)) {
A a = (A)obj;
a.MyProperty = 10;
}
sw2.Stop();
Console.WriteLine("GetType : " + sw2.Elapsed);
Stopwatch sw3 = Stopwatch.StartNew();
var isA = obj is A;
if (isA) {
A a = (A)obj;
a.MyProperty = 19;
}
sw3.Stop();
Console.WriteLine("is : " + sw3.Elapsed);