Ich überarbeite gerade Kapitel 4 von C# in Depth, das sich mit nullable types beschäftigt, und ich füge einen Abschnitt über die Verwendung des "as"-Operators hinzu, mit dem Sie schreiben können:
object o = ...;
int? x = o as int?;
if (x.HasValue)
{
... // Use x.Value in here
}
Ich dachte, dass dies wirklich nett war, und dass es die Leistung über die C# 1 Äquivalent verbessern könnte, mit "is", gefolgt von einem Cast - immerhin, auf diese Weise müssen wir nur für dynamische Typüberprüfung einmal zu fragen, und dann eine einfache Wertprüfung.
Dies scheint jedoch nicht der Fall zu sein. Ich habe unten ein Beispiel für eine Testanwendung beigefügt, die im Grunde alle Ganzzahlen in einem Objekt-Array summiert - aber das Array enthält eine Menge Null-Referenzen und String-Referenzen sowie eingeschlossene Ganzzahlen. Der Benchmark misst den Code, den Sie in C# 1 verwenden müssten, den Code, der den "as"-Operator verwendet, und - nur zum Spaß - eine LINQ-Lösung. Zu meinem Erstaunen ist der C# 1-Code in diesem Fall 20 Mal schneller - und sogar der LINQ-Code (von dem ich angesichts der beteiligten Iteratoren erwartet hätte, dass er langsamer ist) schlägt den "as"-Code.
Ist die .NET-Implementierung von isinst
für löschbare Typen nur sehr langsam? Ist es die zusätzliche unbox.any
die das Problem verursacht? Gibt es eine andere Erklärung für dieses Problem? Im Moment sieht es so aus, als müsste ich eine Warnung vor der Verwendung in leistungssensiblen Situationen einfügen...
Ergebnisse:
Besetzung: 10000000 : 121
Als: 10000000 : 2211
LINQ: 10000000 : 2143
Code:
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);
}
}