Direktes Casting gegen "als" Operator?

709

Betrachten Sie den folgenden Code:

void Handler(object o, EventArgs e)
{
   // I swear o is a string
   string s = (string)o; // 1
   //-OR-
   string s = o as string; // 2
   // -OR-
   string s = o.ToString(); // 3
}

Was ist der Unterschied zwischen den drei Arten von Casting (okay, das dritte ist kein Casting, aber Sie haben die Absicht). Welches sollte bevorzugt werden?

nullDev
quelle
1
Nicht ganz ein Duplikat, aber es gibt auch einige Leistungsdiskussionen in einer vorherigen Frage .
Ungeschnitten
8
4.: string s = Convert.ToString(o); 5. string s = $"{o}"(oder gleichwertig das string.FormatFormular für frühere C #)
Earth Engine

Antworten:

834
string s = (string)o; // 1

Wirft InvalidCastException , wenn onicht ein string. Andernfalls Abtretungsempfänger ozu s, auch wenn oist null.

string s = o as string; // 2

Weist nullzu, swenn onicht ein stringoder wenn oist null. Aus diesem Grund können Sie es nicht mit Werttypen verwenden (der Operator könnte nullin diesem Fall niemals zurückkehren ). Andernfalls ordnet oan s.

string s = o.ToString(); // 3

Verursacht eine Nullreferenceexception , wenn oist null. Weist o.ToString()zu s, was auch immer zurückgegeben wird , unabhängig davon, um welchen Typ es sich ohandelt.


Verwenden Sie 1 für die meisten Conversions - es ist einfach und unkompliziert. Ich benutze fast nie 2, da ich normalerweise eine Ausnahme erwarte, wenn etwas nicht der richtige Typ ist. Ich habe nur einen Bedarf für diese Funktion vom Typ return-null mit schlecht gestalteten Bibliotheken gesehen, die Fehlercodes verwenden (z. B. return null = error, anstatt Ausnahmen zu verwenden).

3 ist keine Besetzung und nur ein Methodenaufruf. Verwenden Sie diese Option, wenn Sie die Zeichenfolgendarstellung eines Nicht-Zeichenfolgenobjekts benötigen.

Sander
quelle
2
Sie können Werttypen 'null' zuweisen, wenn sie explizit definiert sind, z. B.: Int? ich; Zeichenfolge s = "5"; i = s als int; // i ist jetzt 5 s = null; i = s als int; // Ich
bin
3
RE: Anheledir Eigentlich wäre ich nach dem ersten Anruf null. Sie müssen eine explizite Konvertierungsfunktion verwenden, um den Wert einer Zeichenfolge abzurufen.
Guvante
45
RE: Sander Tatsächlich gibt es einen weiteren sehr guten Grund, da dies Ihren Prüfcode vereinfacht (Auf Null prüfen, statt auf Null prüfen und den richtigen Typ angeben). Dies ist hilfreich, da Sie häufig lieber eine benutzerdefinierte Ausnahme auslösen möchten. Aber es ist sehr wahr, dass blind wie Anrufe schlecht sind.
Guvante
5
# 2 ist praktisch für Dinge wie Equals-Methoden, bei denen Sie den Eingabetyp nicht kennen. Im Allgemeinen wäre jedoch 1 vorzuziehen. Obwohl bevorzugt, würde offensichtlich das Typsystem verwendet, um auf einen Typ zu beschränken, wenn Sie nur einen erwarten :)
Calum
6
# 2 ist auch nützlich, wenn Sie Code haben, der möglicherweise etwas Spezifisches für einen speziellen Typ tut, aber sonst nichts tut.
AnthonyWJones
349
  1. string s = (string)o;Verwenden Sie, wenn etwas definitiv das andere sein sollte.
  2. string s = o as string;Verwenden Sie, wenn etwas sein könnte die andere Sache.
  3. string s = o.ToString(); Verwenden Sie diese Option, wenn Sie sich nicht darum kümmern, was es ist, sondern nur die verfügbare Zeichenfolgendarstellung verwenden möchten.
Streit
quelle
1
Ich habe das Gefühl, dass diese Antwort gut klingt, aber möglicherweise nicht korrekt ist.
j riv
1
Ich mag die ersten beiden, aber ich würde der dritten Option "und Sie sind sicher, dass es nicht null ist" hinzufügen.
Uxonith
2
Sie können heutzutage Elvis (?.) verwenden, um sich nicht darum kümmern zu müssen: obj? .ToString ()
Quibblesome
@ Quibblesome - tolle Antwort, aber ich musste aufhören, um über dein Rebuttle nachzudenken! Es ist mir buchstäblich ein Rätsel, dass es die Sprache schon seit mehr als 15 Jahren gibt. Es fühlt sich wie gestern an, als wir alle "nervös" waren und versuchten, ältere Entwickler davon zu überzeugen, auf C # umzusteigen.
Griswald_911
1
@ Quibblesome nette Antwort: Werden Sie sich ärgern, wenn ich 1/2/3 hinzufüge, so dass es nicht notwendig ist, zu OP zu scrollen. Ich mit SO würde alte Antworten nach Stimmen ordnen!
Whytheq
29

Es hängt wirklich davon ab, ob Sie wissen, ob oes sich um eine Zeichenfolge handelt und was Sie damit machen möchten. Wenn Ihr Kommentar bedeutet, dass es sich owirklich wirklich um eine Zeichenfolge handelt, würde ich die gerade (string)oBesetzung bevorzugen - es ist unwahrscheinlich, dass sie fehlschlägt.

Der größte Vorteil der Verwendung der geraden Besetzung besteht darin, dass Sie bei einem Fehlschlag eine InvalidCastException erhalten , die Ihnen ziemlich genau sagt, was schief gelaufen ist.

asWenn der Operator okeine Zeichenfolge ist, swird er auf gesetzt. nullDies ist praktisch, wenn Sie sich nicht sicher sind und testen möchten s:

string s = o as string;
if ( s == null )
{
    // well that's not good!
    gotoPlanB();
}

Wenn Sie diesen Test jedoch nicht durchführen, werden Sie ihn sspäter verwenden und eine NullReferenceException auslösen . Diese sind in der Regel häufiger und viel schwerer zu finden, wenn sie in freier Wildbahn auftreten, da fast jede Zeile eine Variable dereferenziert und möglicherweise eine wirft. Wenn Sie dagegen versuchen, in einen Werttyp (ein Grundelement oder Strukturen wie DateTime ) umzuwandeln , müssen Sie die gerade Umwandlung verwenden - das asfunktioniert nicht.

Im speziellen Fall der Konvertierung in eine Zeichenfolge hat jedes Objekt eine ToString, sodass Ihre dritte Methode möglicherweise in Ordnung ist, wenn sie onicht null ist und Sie der Meinung sind, dass die ToStringMethode möglicherweise das tut, was Sie wollen.

Blair Conrad
quelle
2
Ein Hinweis - Sie können asmit nullbaren Werttypen verwenden. IE o as DateTimewird nicht funktionieren, aber o as DateTime?wird ...
John Gibb
Warum nicht if (s is string)stattdessen verwenden?
BornToCode
1
@BornToCode, für mich weitgehend persönliche Präferenz. Je nachdem, was du tust, musst du oft nach dem isIng sowieso wieder besetzen, also hast du das Ist und dann eine harte Besetzung. Aus irgendeinem Grund asfühlte sich der und null Check für mich besser an.
Blair Conrad
9

Wenn Sie bereits wissen, in welchen Typ es umgewandelt werden kann, verwenden Sie eine Besetzung im C-Stil:

var o = (string) iKnowThisIsAString; 

Beachten Sie, dass Sie nur mit einer Besetzung im C-Stil expliziten Typzwang ausführen können.

Wenn Sie nicht wissen, ob es sich um den gewünschten Typ handelt, und ihn gegebenenfalls verwenden, verwenden Sie ihn als Schlüsselwort:

var s = o as string;
if (s != null) return s.Replace("_","-");

//or for early return:
if (s==null) return;

Beachten Sie, dass as keine Typkonvertierungsoperatoren aufruft. Es ist nur dann nicht null, wenn das Objekt nicht null und nativ vom angegebenen Typ ist.

Verwenden Sie ToString (), um eine für Menschen lesbare Zeichenfolgendarstellung eines Objekts zu erhalten, auch wenn es nicht in eine Zeichenfolge umgewandelt werden kann.

Mark Cidade
quelle
3
Das ist ein interessantes kleines Problem in Bezug auf die Typkonvertierungsoperatoren. Ich habe ein paar Typen, für die ich Conversions erstellt habe, darauf muss ich dann achten.
AnthonyWJones
7

Das Schlüsselwort as ist in asp.net gut, wenn Sie die FindControl-Methode verwenden.

Hyperlink link = this.FindControl("linkid") as Hyperlink;
if (link != null)
{
     ...
}

Dies bedeutet, dass Sie die typisierte Variable bearbeiten können, anstatt sie dann objectwie bei einer direkten Umwandlung umwandeln zu müssen:

object linkObj = this.FindControl("linkid");
if (link != null)
{
     Hyperlink link = (Hyperlink)linkObj;
}

Es ist keine große Sache, aber es spart Codezeilen und Variablenzuweisungen und ist besser lesbar

Glenn Slaven
quelle
6

'as' basiert auf 'is', einem Schlüsselwort, das zur Laufzeit prüft, ob das Objekt polimorphisch kompatibel ist (im Grunde genommen, wenn eine Umwandlung vorgenommen werden kann) und bei fehlgeschlagener Prüfung null zurückgibt.

Diese beiden sind gleichwertig:

Verwenden von 'as':

string s = o as string;

Verwenden von 'ist':

if(o is string) 
    s = o;
else
    s = null;

Im Gegenteil, der Cast im C-Stil wird auch zur Laufzeit erstellt, löst jedoch eine Ausnahme aus, wenn der Cast nicht erstellt werden kann.

Nur um eine wichtige Tatsache hinzuzufügen:

Das Schlüsselwort 'as' funktioniert nur mit Referenztypen. Du kannst nicht tun:

// I swear i is an int
int number = i as int;

In diesen Fällen müssen Sie Casting verwenden.

Sergio Acosta
quelle
Danke, dass du auf meinen Fehler hingewiesen hast, du hast recht. Ich habe die Antwort bearbeitet. ups, tut mir Leid.
Sergio Acosta
5

2 ist nützlich zum Gießen auf einen abgeleiteten Typ.

Angenommen, a ist ein Tier:

b = a as Badger;
c = a as Cow;

if (b != null)
   b.EatSnails();
else if (c != null)
   c.EatGrass();

erhält eine gefüttert mit einem Minimum von Abgüssen.

Joel in Gö
quelle
2
@Chirs Moutray, das ist nicht immer möglich, besonders wenn es sich um eine Bibliothek handelt.
Verlangsamte
5

Laut Experimenten auf dieser Seite: http://www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as

(Auf dieser Seite werden manchmal einige "illegale Verweis" -Fehler angezeigt. Aktualisieren Sie sie daher einfach, wenn dies der Fall ist.)

Die Schlussfolgerung ist, dass der "as" -Operator normalerweise schneller als ein Cast ist. Manchmal um ein Vielfaches schneller, manchmal nur knapp schneller.

Ich persönlich habe "as" auch besser lesbar.

Da es sowohl schneller als auch "sicherer" ist (keine Ausnahme auslöst) und möglicherweise einfacher zu lesen ist, empfehle ich, immer "als" zu verwenden.

Brady Moritz
quelle
4

"(string) o" führt zu einer InvalidCastException, da keine direkte Umwandlung erfolgt.

"o als Zeichenfolge" führt dazu, dass s eine Nullreferenz ist und keine Ausnahme ausgelöst wird.

"o.ToString ()" ist an sich keine Besetzung, sondern eine Methode, die von jedem Objekt und damit auf die eine oder andere Weise von jeder Klasse in .net implementiert wird, die mit der Instanz von "etwas" macht Die Klasse, für die es aufgerufen wird, gibt eine Zeichenfolge zurück.

Vergessen Sie nicht, dass es für die Konvertierung in Zeichenfolgen auch Convert.ToString (someType instanceOfThatType) gibt, bei dem someType zu einer Reihe von Typen gehört, im Wesentlichen zu den Framework-Basistypen.

rauben
quelle
3

Alle gegebenen Antworten sind gut, wenn ich etwas hinzufügen darf: Um die Methoden und Eigenschaften des Strings (z. B. ToLower) direkt zu verwenden, können Sie nicht schreiben:

(string)o.ToLower(); // won't compile

du kannst nur schreiben:

((string)o).ToLower();

aber du könntest stattdessen schreiben:

(o as string).ToLower();

Die asOption ist besser lesbar (zumindest meiner Meinung nach).

BornToCode
quelle
Das Konstrukt (o als Zeichenfolge) .ToLower () macht den Zweck des Operators as zunichte. Dies löst eine Nullreferenzausnahme aus, wenn o nicht in einen String umgewandelt werden kann.
James
@james - Aber wer hat gesagt, dass der einzige Zweck des as-Operators darin besteht, eine Ausnahme auszulösen, wenn die Besetzung fehlschlägt? Wenn Sie wissen, dass o eine Zeichenfolge ist und nur saubereren Code schreiben möchten, können Sie (o as string).ToLower()anstelle der mehreren verwirrenden Klammern verwenden.
BornToCode
Der Zweck des as ist genau das Gegenteil - es sollte keine Ausnahme auslösen, wenn die Umwandlung fehlschlägt, es sollte null zurückgeben. Angenommen, Ihr o ist eine Zeichenfolge mit dem Wert null. Was passiert dann? Hinweis - Ihr ToLower-Aufruf schlägt fehl.
James
@james - Sie haben Recht, aber was ist mit den Fällen, in denen ich mit Sicherheit weiß, dass es nicht null ist und ich nur das Casting für den Compiler durchführen muss, damit ich auf die Methoden dieses Objekts zugreifen kann?
BornToCode
1
Sie können das definitiv tun, aber es ist nicht gerade die beste Vorgehensweise, da Sie sich nicht auf den Anrufer oder externe Systeme verlassen möchten, um sicherzustellen, dass Ihr Wert nicht null ist. Wenn Sie C # 6 verwenden, können Sie dies tun (o als Zeichenfolge). Zu senken().
James
3
string s = o as string; // 2

Wird bevorzugt, da dadurch die Leistungseinbußen beim Doppelcasting vermieden werden.

Chris S.
quelle
Hallo Chris, der Link in dieser Antwort ist jetzt ein 404 ... Ich bin mir nicht sicher, ob Sie einen Ersatz haben, den Sie an seine Stelle setzen möchten?
Matt
3

Es scheint, dass die beiden konzeptionell unterschiedlich sind.

Direktes Casting

Typen müssen nicht eng miteinander verbunden sein. Es kommt in allen Arten von Geschmacksrichtungen.

  • Benutzerdefiniertes implizites / explizites Casting: Normalerweise wird ein neues Objekt erstellt.
  • Werttyp Implizit: Kopieren, ohne Informationen zu verlieren.
  • Werttyp Explizit: Kopie und Informationen gehen möglicherweise verloren.
  • IS-A-Beziehung: Referenztyp ändern, andernfalls wird eine Ausnahme ausgelöst.
  • Gleicher Typ: 'Casting ist redundant'.

Es fühlt sich an, als würde das Objekt in etwas anderes umgewandelt.

AS-Operator

Typen haben eine direkte Beziehung. Wie in:

  • Referenztypen: IS-A-Beziehung Objekte sind immer gleich, nur die Referenz ändert sich.
  • Werttypen: Boxing und nullbare Typen kopieren .

Es fühlt sich so an, als würden Sie das Objekt anders behandeln.

Proben und IL

    class TypeA
    {
        public int value;
    }

    class TypeB
    {
        public int number;

        public static explicit operator TypeB(TypeA v)
        {
            return new TypeB() { number = v.value };
        }
    }

    class TypeC : TypeB { }
    interface IFoo { }
    class TypeD : TypeA, IFoo { }

    void Run()
    {
        TypeA customTypeA = new TypeD() { value = 10 };
        long longValue = long.MaxValue;
        int intValue = int.MaxValue;

        // Casting 
        TypeB typeB = (TypeB)customTypeA; // custom explicit casting -- IL:  call class ConsoleApp1.Program/TypeB ConsoleApp1.Program/TypeB::op_Explicit(class ConsoleApp1.Program/TypeA)
        IFoo foo = (IFoo)customTypeA; // is-a reference -- IL: castclass  ConsoleApp1.Program/IFoo

        int loseValue = (int)longValue; // explicit -- IL: conv.i4
        long dontLose = intValue; // implict -- IL: conv.i8

        // AS 
        int? wraps = intValue as int?; // nullable wrapper -- IL:  call instance void valuetype [System.Runtime]System.Nullable`1<int32>::.ctor(!0)
        object o1 = intValue as object; // box -- IL: box [System.Runtime]System.Int32
        TypeD d1 = customTypeA as TypeD; // reference conversion -- IL: isinst ConsoleApp1.Program/TypeD
        IFoo f1 = customTypeA as IFoo; // reference conversion -- IL: isinst ConsoleApp1.Program/IFoo

        //TypeC d = customTypeA as TypeC; // wouldn't compile
    }
Lucas Teixeira
quelle
2

Ich möchte auf die folgenden Besonderheiten des as- Betreibers aufmerksam machen:

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/as

Beachten Sie, dass der as-Operator nur Referenzkonvertierungen, nullfähige Konvertierungen und Boxkonvertierungen ausführt. Der as-Operator kann keine anderen Konvertierungen ausführen, z. B. benutzerdefinierte Konvertierungen, die stattdessen mithilfe von Cast-Ausdrücken ausgeführt werden sollten.

Vadim S.
quelle
0

Wenn ich versuche, die Zeichenfolgendarstellung von irgendetwas (von irgendeinem Typ) zu erhalten, das möglicherweise null sein könnte, bevorzuge ich die folgende Codezeile. Es ist kompakt, ruft ToString () auf und behandelt Nullen korrekt. Wenn o null ist, enthält s String.Empty.

String s = String.Concat(o);
xtrem
quelle
0

Da es niemand erwähnt hat, ist das, was der Instanz von Java per Schlüsselwort am nächsten kommt, Folgendes:

obj.GetType().IsInstanceOfType(otherObj)
Bennett Yeo
quelle
0

Verwenden Sie Direct Cast, string s = (string) o;wenn im logischen Kontext Ihrer App stringder einzig gültige Typ ist. Mit diesem Ansatz erhalten InvalidCastExceptionund implementieren Sie das Prinzip von Fail-Fast . Ihre Logik ist davor geschützt, den ungültigen Typ weiterzuleiten oder bei Verwendung die NullReferenceException zu erhaltenas Operator verwendet wird.

Wenn die Logik verschiedene Arten erwartet werfen string s = o as string;und überprüfen Sie es auf nulloder Verwendungis Betreiber.

In C # 7.0 wurde eine neue coole Funktion veröffentlicht, um die Besetzung zu vereinfachen und zu überprüfen, ob ein Muster übereinstimmt :

if(o is string s)
{
  // Use string variable s
}

or

switch (o)
{
  case int i:
     // Use int variable i
     break;
  case string s:
     // Use string variable s
     break;
 }
Dmitry
quelle
0

Die folgenden zwei Formen der Typkonvertierung (Casting) werden in C # unterstützt:

|

(Lebenslauf

• Konvertieren Sie den statischen Typ von v in den angegebenen Ausdruck in c

• Nur möglich, wenn der dynamische Typ von v c oder ein Subtyp von c ist

• Wenn nicht, wird eine InvalidCastException ausgelöst

|

v als C.

• Nicht tödliche Variante von (c) v

• Konvertieren Sie daher den statischen Typ von v in den angegebenen Ausdruck in c

• Gibt null zurück, wenn der dynamische Typ von v nicht c oder ein Subtyp von c ist


quelle