我只是在深度修改处理可空类型的 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
是导致问题的附加因素吗?对此还有其他解释吗?目前感觉我将不得不包含一个警告,不要在性能敏感的情况下使用它......
结果:
演员:10000000:121
作为: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);
}
}