我只是在修改c#深度的第4章,它涉及到可空类型,我增加了一个关于使用“as”操作符的章节,它允许你这样写:
object o = ...;
int? x = o as int?;
if (x.HasValue)
{
... // Use x.Value in here
}
我认为这真的很整洁,它可以比c# 1的等效方法提高性能,使用“is”后面加强制转换——毕竟,这样我们只需要要求进行一次动态类型检查,然后进行简单的值检查。
然而,事实似乎并非如此。我在下面包含了一个示例测试应用程序,它基本上是一个对象数组中所有整数的总和——但是数组包含大量空引用和字符串引用以及盒装整数。这个基准测试测量了在c# 1中必须使用的代码,使用“as”操作符的代码,以及仅用于LINQ解决方案的代码。令我惊讶的是,c# 1代码在这种情况下要快20倍——甚至LINQ代码(考虑到涉及的迭代器,我本以为会慢一些)也比“as”代码快。
.NET对可空类型的isinst
的实现真的很慢吗?是额外的unbox.any
导致了这个问题吗?还有其他解释吗?目前,我觉得我必须在对性能敏感的情况下使用此功能时提出警告……
结果:
Cast: 10000000: 121
As: 10000000: 2211
LINQ: 10000000: 2143
代码:
using System;
using System.Diagnostics;
using System.Linq;
class Test
{
const int Size = 30000000;
static void Main()
{
object[] values = new object[Size];
for (int i = 0; i < Size - 2; i += 3)
{
values[i] = null;
values[i+1] = "";
values[i+2] = 1;
}
FindSumWithCast(values);
FindSumWithAs(values);
FindSumWithLinq(values);
}
static void FindSumWithCast(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int sum = 0;
foreach (object o in values)
{
if (o is int)
{
int x = (int) o;
sum += x;
}
}
sw.Stop();
Console.WriteLine("Cast: {0} : {1}", sum,
(long) sw.ElapsedMilliseconds);
}
static void FindSumWithAs(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int sum = 0;
foreach (object o in values)
{
int? x = o as int?;
if (x.HasValue)
{
sum += x.Value;
}
}
sw.Stop();
Console.WriteLine("As: {0} : {1}", sum,
(long) sw.ElapsedMilliseconds);
}
static void FindSumWithLinq(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int sum = values.OfType<int>().Sum();
sw.Stop();
Console.WriteLine("LINQ: {0} : {1}", sum,
(long) sw.ElapsedMilliseconds);
}
}