null許容型を扱うC#in Depthの第4章を改訂しているところです。また、「as」演算子の使用に関するセクションを追加して、次のように記述できるようにします。
object o = ...;
int? x = o as int?;
if (x.HasValue)
{
... // Use x.Value in here
}
これは本当にすてきで、「is」の後にキャストを使用することで、C#1の同等のものよりもパフォーマンスを向上させることができると思いました。結局のところ、この方法では、動的型チェックを1回だけ要求してから、単純な値チェックを要求するだけで済みます。 。
ただし、これは当てはまらないようです。以下にサンプルのテストアプリを含めました。これは基本的にオブジェクト配列内のすべての整数を合計しますが、配列には多くのnull参照と文字列参照、およびボックス化された整数が含まれています。ベンチマークは、C#1で使用する必要のあるコード、「as」演算子を使用するコード、およびLINQソリューションをキックするためだけに使用するコードを測定します。驚いたことに、この場合、C#1コードは20倍高速です。また、LINQコード(イテレーターが関係していることを考えると、低速になると予想されていました)でさえ「as」コードよりも優れています。
null許容型の.NET実装はisinst
本当に遅いですか?unbox.any
問題を引き起こすのは追加ですか?これについて別の説明はありますか?現時点では、パフォーマンスに敏感な状況でこれを使用することに対する警告を含める必要があるように感じます...
結果:
キャスト: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);
}
}