Die Antwort unter der Zeile wurde 2008 geschrieben.
C # 7 führte den Mustervergleich ein, der den as
Operator weitgehend ersetzt hat , wie Sie jetzt schreiben können:
if (randomObject is TargetType tt)
{
// Use tt here
}
Beachten Sie, dass dies danach tt
noch im Geltungsbereich liegt, aber nicht definitiv zugewiesen ist. (Es ist definitiv im if
Körper zugeordnet.) Das ist in einigen Fällen etwas ärgerlich. Wenn Sie also wirklich daran interessiert sind, die kleinstmögliche Anzahl von Variablen in jedem Bereich einzuführen, möchten Sie möglicherweise immer noch is
eine Besetzung verwenden, gefolgt von einer Besetzung.
Ich glaube, keine der bisherigen Antworten (zum Zeitpunkt des Beginns dieser Antwort!) Hat wirklich erklärt, wo es sich lohnt, welche zu verwenden.
Tu das nicht:
// Bad code - checks type twice for no reason
if (randomObject is TargetType)
{
TargetType foo = (TargetType) randomObject;
// Do something with foo
}
Dies wird nicht nur zweimal überprüft, sondern es können auch verschiedene Dinge überprüft werden, wenn randomObject
es sich eher um ein Feld als um eine lokale Variable handelt. Es ist möglich, dass das "Wenn" bestanden wird, aber dann die Umwandlung fehlschlägt, wenn ein anderer Thread den Wert randomObject
zwischen den beiden ändert .
Wenn randomObject
wirklich soll eine Instanz sein TargetType
, das heißt , wenn es nicht ist , das bedeutet ein Fehler da ist, dann ist Gießen die richtige Lösung. Das löst sofort eine Ausnahme aus, was bedeutet, dass unter falschen Annahmen keine Arbeit mehr ausgeführt wird und die Ausnahme die Art des Fehlers 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 es sich randomObject
möglicherweise um eine Instanz von TargetType
und TargetType
um einen Referenztyp handelt, verwenden Sie folgenden Code:
TargetType convertedRandomObject = randomObject as TargetType;
if (convertedRandomObject != null)
{
// Do stuff with convertedRandomObject
}
Wenn es sich randomObject
möglicherweise um eine Instanz von TargetType
und TargetType
um einen Werttyp handelt, können wir ihn nicht as
mit sich TargetType
selbst verwenden, aber wir können einen nullbaren Typ verwenden:
TargetType? convertedRandomObject = randomObject as TargetType?;
if (convertedRandomObject != null)
{
// Do stuff with convertedRandomObject.Value
}
(Hinweis: Derzeit ist dies tatsächlich langsamer als + Besetzung . Ich denke, es ist eleganter und konsistenter, aber los geht's.)
Wenn Sie den konvertierten Wert wirklich nicht benötigen, aber nur wissen müssen, ob es sich um eine Instanz von TargetType handelt, ist der is
Operator Ihr Freund. In diesem Fall spielt es keine Rolle, ob TargetType ein Referenztyp oder ein Werttyp ist.
Es kann andere Fälle mit Generika geben, in denen dies is
nützlich ist (weil Sie möglicherweise nicht wissen, ob T ein Referenztyp ist oder nicht, Sie können es also nicht als verwenden), aber sie sind relativ dunkel.
Ich habe mit ziemlicher Sicherheit schon früher is
für den Werttyp-Fall verwendet, ohne daran gedacht zu haben, einen nullbaren Typ zu verwenden und as
zusammen :)
BEARBEITEN: Beachten Sie, dass keiner der oben genannten Punkte die Leistung betrifft, außer dem Fall des Werttyps, bei dem ich festgestellt habe, dass das Entpacken in einen nullbaren Werttyp tatsächlich langsamer ist - aber konsistent.
Gemäß der Antwort von naasking sind is-and-cast oder is-and-as mit modernen JITs so schnell wie eine Nullprüfung, 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 diese alle in ca. 60 ms ausgeführt. Zwei Dinge zu beachten:
- Es gibt keinen signifikanten Unterschied zwischen ihnen. (In der Tat gibt es Situationen , in denen der als-plus-null-Check auf jeden Fall ist langsamer Der obige Code macht eigentlich die Art Prüfung einfach , weil es für eine versiegelte Klasse ist;. , Wenn Sie überprüfen für eine Schnittstelle, leicht die Balance - Tipps zugunsten von as-plus-null-check.)
- Sie sind alle wahnsinnig schnell. Dies ist einfach nicht der Engpass in Ihrem Code, es sei denn, Sie werden danach wirklich nichts mehr mit den Werten tun .
Machen wir uns also keine Sorgen um die Leistung. Sorgen wir uns um Korrektheit und Konsistenz.
Ich behaupte, dass is-and-cast (oder is-and-as) beim Umgang mit Variablen beide unsicher sind, 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 hätte lieber eine Konvention, die ich konsequent anwenden kann.
Ich behaupte auch, dass der As-Then-Null-Check eine bessere Trennung der Bedenken ermöglicht. Wir haben eine Anweisung, die eine Konvertierung versucht, und dann eine Anweisung, die das Ergebnis verwendet. Das is-and-cast oder is-and-as führt einen Test und dann einen weiteren Versuch durch, den Wert zu konvertieren.
Anders ausgedrückt, würde jemand jemals schreiben:
int value;
if (int.TryParse(text, out value))
{
value = int.Parse(text);
// Use value
}
Das ist genau das, was Is-and-Cast tut - wenn auch offensichtlich billiger.