Właśnie przeglądam rozdział 4 C # w Depth, który dotyczy typów zerowalnych, i dodaję sekcję o używaniu operatora „as”, który pozwala pisać:
object o = ...;
int? x = o as int?;
if (x.HasValue)
{
... // Use x.Value in here
}
Myślałem, że to naprawdę fajne i że może poprawić wydajność w stosunku do C # 1, używając „jest”, a następnie rzutowania - w końcu w ten sposób musimy poprosić tylko o dynamiczne sprawdzenie typu, a następnie proste sprawdzenie wartości .
Jednak wydaje się, że tak nie jest. Poniżej zamieściłem przykładową aplikację testową, która w zasadzie sumuje wszystkie liczby całkowite w tablicy obiektów - ale tablica zawiera wiele odwołań zerowych i odniesień łańcuchowych, a także liczb całkowitych w ramkach. Benchmark mierzy kod, którego należy użyć w języku C # 1, kod za pomocą operatora „as” i tylko dla rozwiązania LINQ. Ku mojemu zdziwieniu kod C # 1 jest w tym przypadku 20 razy szybszy - a nawet kod LINQ (który, jak się spodziewałam, byłby wolniejszy, biorąc pod uwagę zaangażowane iteratory) bije kod „jak”.
Czy implementacja .NET isinst
dla typów zerowalnych jest po prostu bardzo wolna? Czy to dodatkoweunbox.any
problem powoduje problem? Czy jest na to inne wytłumaczenie? W tej chwili wydaje mi się, że będę musiał dołączyć ostrzeżenie przed użyciem tego w sytuacjach wrażliwych na wydajność ...
Wyniki:
Obsada: 10000000: 121
Jako: 10000000: 2211
LINQ: 10000000: 2143
Kod:
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);
}
}
as
na typach zerowalnych. Interesujące, ponieważ nie można go stosować w przypadku innych typów wartości. Właściwie bardziej zaskakujące.
as
próbuje rzutować na typ, a jeśli się nie powiedzie, to zwraca null. Nie można ustawić typów wartości na null