Die Antwort unter dieser Zeile stammt aus dem Jahr 2008.
Mit C# 7 wurde der Musterabgleich eingeführt, der weitgehend die as
Operator, wie Sie jetzt schreiben können:
if (randomObject is TargetType tt)
{
// Use tt here
}
Beachten Sie, dass tt
ist danach immer noch im Geltungsbereich, aber nicht endgültig zugewiesen. (Es est definitiv zugewiesen innerhalb der if
Körper). Das ist in manchen Fällen etwas lästig, wenn Sie also wirklich Wert darauf legen, die kleinstmögliche Anzahl von Variablen in jedem Bereich einzuführen, sollten Sie vielleicht trotzdem is
gefolgt von einem Gips.
Ich glaube nicht, dass in den bisherigen Antworten (zum Zeitpunkt des Beginns dieser Antwort!) wirklich erklärt wurde, wo es sich lohnt, was zu verwenden.
-
Tun Sie das nicht:
// Bad code - checks type twice for no reason
if (randomObject is TargetType)
{
TargetType foo = (TargetType) randomObject;
// Do something with foo
}
Dabei wird nicht nur zweimal geprüft, sondern es werden möglicherweise auch verschiedene Dinge geprüft, wenn randomObject
ein Feld und nicht eine lokale Variable ist. Es ist möglich, dass die "if"-Funktion erfolgreich ist, aber der Cast fehlschlägt, wenn ein anderer Thread den Wert von randomObject
zwischen den beiden.
-
Wenn randomObject
wirklich sollte ein Fall sein von TargetType
Wenn das nicht der Fall ist, bedeutet das, dass es einen Fehler gibt, dann ist Gießen die richtige Lösung. Dadurch wird sofort eine Ausnahme ausgelöst, was bedeutet, dass keine weitere Arbeit unter falschen Annahmen durchgeführt wird und die Ausnahme den Fehlertyp korrekt anzeigt.
// This will throw an exception if randomObject is non-null and
// refers to an object of an incompatible type. The cast is
// the best code if that's the behaviour you want.
TargetType convertedRandomObject = (TargetType) randomObject;
-
Wenn randomObject
puede ein Fall sein von TargetType
y TargetType
ein Referenztyp ist, dann verwenden Sie einen Code wie diesen:
TargetType convertedRandomObject = randomObject as TargetType;
if (convertedRandomObject != null)
{
// Do stuff with convertedRandomObject
}
-
Wenn randomObject
puede ein Fall sein von TargetType
y TargetType
ein Wertetyp ist, dann können wir nicht as
con TargetType
selbst, aber wir können einen nullbaren Typ verwenden:
TargetType? convertedRandomObject = randomObject as TargetType?;
if (convertedRandomObject != null)
{
// Do stuff with convertedRandomObject.Value
}
(Anmerkung: Derzeit ist dies tatsächlich langsamer als ist + gegossen . Ich denke, es ist eleganter und konsistenter, aber so ist es eben).
-
Wenn Sie den umgewandelten Wert nicht wirklich benötigen, sondern nur wissen wollen, ob er est eine Instanz von TargetType, dann wird die is
Betreiber ist Ihr Freund. In diesem Fall spielt es keine Rolle, ob TargetType ein Referenztyp oder ein Wertetyp ist.
-
Es kann andere Fälle geben, in denen Generika is
ist nützlich (weil man vielleicht nicht weiß, ob T ein Referenztyp ist oder nicht, so dass man as nicht verwenden kann), aber sie sind relativ undurchsichtig.
-
Ich habe fast sicher verwendet is
für den Fall des Werttyps, da ich nicht daran gedacht habe, einen nullbaren Typ zu verwenden und as
zusammen :)
EDIT: Beachten Sie, dass keine der oben genannten spricht über Leistung, andere als der Wert-Typ-Fall, wo ich festgestellt habe, dass unboxing zu einem nullable Wert-Typ tatsächlich langsamer ist - aber konsistent.
Wie in der Antwort von naasking beschrieben, sind is-and-cast oder is-and-as mit modernen JITs genauso schnell wie as-and-null-check, wie der folgende Code zeigt:
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] = "x";
values[i + 2] = new object();
}
FindLengthWithIsAndCast(values);
FindLengthWithIsAndAs(values);
FindLengthWithAsAndNullCheck(values);
}
static void FindLengthWithIsAndCast(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
if (o is string)
{
string a = (string) o;
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("Is and Cast: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}
static void FindLengthWithIsAndAs(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
if (o is string)
{
string a = o as string;
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("Is and As: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}
static void FindLengthWithAsAndNullCheck(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
string a = o as string;
if (a != null)
{
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("As and null check: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}
}
Auf meinem Laptop werden sie alle in etwa 60 ms ausgeführt. Zwei Dinge sind zu beachten:
- Es gibt keinen signifikanten Unterschied zwischen ihnen. (In der Tat gibt es Situationen, in denen die as-plus-null-Prüfung definitiv est langsamer. Der obige Code macht die Typprüfung tatsächlich einfach, weil er für eine versiegelte Klasse ist; wenn Sie für eine Schnittstelle prüfen, kippt das Gleichgewicht leicht zugunsten der as-plus-null-Prüfung).
- Sie sind alle wahnsinnig schnell. Das ist einfach wird nicht der Engpass in Ihrem Code sein, es sei denn, Sie haben wirklich nicht vor, die alles mit den Werten danach.
Machen wir uns also keine Sorgen um die Leistung. Sorgen wir uns lieber um die Korrektheit und Konsistenz.
Ich behaupte, dass is-and-cast (oder is-and-as) beide unsicher sind, wenn es um Variablen geht, da sich der Typ des Wertes, auf den er sich bezieht, aufgrund eines anderen Threads zwischen dem Test und dem Cast ändern kann. Das wäre eine ziemlich seltene Situation - aber ich möchte lieber eine Konvention haben, die ich konsequent verwenden kann.
Ich behaupte auch, dass die as-then-null-Prüfung eine bessere Trennung der Anliegen ermöglicht. Wir haben eine Anweisung, die eine Umwandlung versucht, und dann eine Anweisung, die das Ergebnis verwendet. Der is-and-cast oder is-and-as führt einen Test durch und dann ein weiterer Versuch, den Wert zu konvertieren.
Mit anderen Worten, würde jemand immer schreiben:
int value;
if (int.TryParse(text, out value))
{
value = int.Parse(text);
// Use value
}
Das ist in etwa das, was is-and-cast macht - wenn auch offensichtlich auf eine etwas billigere Weise.