Casting diretto vs operatore "as"?


710

Considera il seguente codice:

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
}

Qual è la differenza tra i tre tipi di casting (ok, il terzo non è un casting, ma ottieni l'intento). Quale dovrebbe essere preferito?


1
Non proprio un duplicato, ma ci sono anche alcune discussioni sulle prestazioni in una domanda precedente .
tagliato

8
4 ° string s = Convert.ToString(o):; 5: string s = $"{o}"(o equivalentemente il string.Formatmodulo per C # precedente)
Earth Engine

Risposte:


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

Genera InvalidCastException se onon è un string. Altrimenti, assegna oa s, anche se lo oè null.

string s = o as string; // 2

Assegna nulla sse onon è un stringo se oè null. Per questo motivo, non è possibile utilizzarlo con tipi di valore (l'operatore non potrebbe mai tornare nullin quel caso). Altrimenti, assegna oa s.

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

Provoca un'eccezione NullReferenceException se lo oè null. Assegna qualsiasi cosa o.ToString()ritorni a s, indipendentemente dal tipo o.


Usa 1 per la maggior parte delle conversioni: è semplice e diretto. Tendo a non usare quasi mai 2 poiché se qualcosa non è del tipo giusto, di solito mi aspetto un'eccezione. Ho visto solo la necessità di questo tipo di funzionalità return-null con librerie mal progettate che usano codici di errore (ad esempio return null = error, invece di usare le eccezioni).

3 non è un cast ed è solo una chiamata al metodo. Usalo per quando hai bisogno della rappresentazione in forma di stringa di un oggetto senza stringa.


2
È possibile assegnare "null" a tipi di valore quando definito in modo esplicito, ad esempio: int? io; stringa s = "5"; i = s come int; // i ora è 5 s = null; i = s come int; // ora è nullo
Anheledir il

3
RE: Anheledir In realtà sarei nullo dopo la prima chiamata. Devi usare una funzione di conversione esplicita per ottenere il valore di una stringa.
Guvante,

45
RE: Sander In realtà c'è un altro ottimo motivo per usare come, semplifica il tuo codice di controllo (Controlla per null invece di verificare il tipo null e corretto) Ciò è utile dal momento che preferisci lanciare un'eccezione personalizzata. Ma è vero che i ciechi come le chiamate sono cattivi.
Guvante,

5
# 2 è utile per cose come i metodi Equals in cui non si conosce il tipo di input, ma generalmente si preferirebbe 1. Anche se preferito rispetto a quello, sarebbe ovviamente usare il sistema dei tipi per limitarsi a un tipo quando te lo aspetti solo :)
Calum

6
# 2 è utile anche quando si dispone di codice che potrebbe fare qualcosa di specifico per un tipo specializzato ma che altrimenti non farebbe nulla.
AnthonyWJones,

349
  1. string s = (string)o;Utilizzare quando qualcosa dovrebbe essere sicuramente l'altra cosa.
  2. string s = o as string;Utilizzare quando qualcosa potrebbe essere l'altra cosa.
  3. string s = o.ToString(); Utilizzare quando non ti interessa di cosa si tratta ma vuoi solo usare la rappresentazione di stringa disponibile.

1
Sento che questa risposta suona bene, ma potrebbe non essere precisa.
j riv

1
Mi piacciono i primi due, ma aggiungerei "e sei sicuro che non sia null" alla terza opzione.
Uxonith,

2
puoi usare Elvis (?.) in questi giorni per evitare di preoccupartene: obj? .ToString ()
Quibblesome

@Quibblesome - ottima risposta ma ho dovuto smettere di pensare al tuo rebuttle! mi viene letteralmente in mente che la lingua esiste da oltre 15 anni. Sembra ieri quando eravamo tutti "spigolosi" cercando di convincere gli sviluppatori senior a passare al C #.
Griswald_911,

1
@Quibblesome bella risposta: ti arrabbieresti se aggiungo cosa sono 1/2/3 in modo che non sia necessario scorrere fino a OP. Io con SO classificherei le vecchie risposte in base ai voti!
whytheq,

29

Dipende davvero dal fatto che tu sappia se oè una stringa e cosa vuoi farci. Se il tuo commento significa che oè davvero una stringa, preferirei il (string)ocast diretto - è improbabile che fallisca.

Il più grande vantaggio dell'utilizzo del cast diretto è che quando fallisce, ottieni InvalidCastException , che ti dice praticamente cosa è andato storto.

Con l' asoperatore, se onon è una stringa, sè impostato su null, il che è utile se non si è sicuri e si desidera testare s:

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

Tuttavia, se non si esegue quel test, verrà utilizzato in sseguito e verrà generata un'eccezione NullReferenceException . Questi tendono ad essere più comuni e molto più difficili da rintracciare una volta che si verificano in natura, poiché quasi ogni linea dereferenzia una variabile e può lanciarne una. D'altra parte, se stai cercando di eseguire il cast su un tipo di valore (qualsiasi primitiva o strutture come DateTime ), devi usare il cast diretto: asnon funzionerà.

Nel caso speciale della conversione in una stringa, ogni oggetto ha un ToString, quindi il tuo terzo metodo potrebbe andare bene se onon è nullo e pensi che il ToStringmetodo potrebbe fare quello che vuoi.


2
Una nota: è possibile utilizzare ascon tipi di valore annullabili . IE o as DateTimenon funzionerà, ma o as DateTime?...
John Gibb,

Perché non usare if (s is string)invece?
BornToCode

1
@BornToCode, per me, preferenza in gran parte personale. A seconda di ciò che stai facendo, spesso dopo l' ising, dovrai comunque lanciare di nuovo, quindi hai l'Is e quindi un cast difficile. Per qualche motivo, il assegno di spunta nullo mi è sembrato migliore.
Blair Conrad,

9

Se sai già a quale tipo può eseguire il cast, usa un cast in stile C:

var o = (string) iKnowThisIsAString; 

Nota che solo con un cast in stile C puoi eseguire una coercizione di tipo esplicito.

Se non sai se è il tipo desiderato e lo userai se lo è, usa come parola chiave:

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

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

Si noti che come non verrà chiamato alcun operatore di conversione di tipo. Sarà non nullo solo se l'oggetto non è nullo e nativamente del tipo specificato.

Usa ToString () per ottenere una rappresentazione in formato stringa leggibile dall'uomo di qualsiasi oggetto, anche se non può eseguire il cast su stringa.


3
Questo è un piccolo punto interessante per quanto riguarda gli operatori di conversione del tipo. Ho alcuni tipi per cui ho creato conversioni, quindi devo fare attenzione a quello.
AnthonyWJones,

7

La parola chiave as è buona in asp.net quando si utilizza il metodo FindControl.

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

Ciò significa che puoi operare sulla variabile tipizzata piuttosto che dover quindi lanciarla da objectcome faresti con un cast diretto:

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

Non è una cosa enorme, ma salva righe di codice e assegnazione di variabili, inoltre è più leggibile


6

'as' si basa su 'is', che è una parola chiave che verifica in fase di esecuzione se l'oggetto è polimorphycally compatibile (fondamentalmente se è possibile eseguire un cast) e restituisce null se il controllo fallisce.

Questi due sono equivalenti:

Usando 'come':

string s = o as string;

Usando 'è':

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

Al contrario, il cast in stile c è realizzato anche in fase di esecuzione, ma genera un'eccezione se il cast non può essere creato.

Solo per aggiungere un fatto importante:

La parola chiave "as" funziona solo con tipi di riferimento. Non puoi fare:

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

In quei casi devi usare il casting.


Grazie per aver segnalato il mio errore, hai ragione. Ho modificato la risposta. Oops scusa.
Sergio Acosta,

5

2 è utile per il cast su un tipo derivato.

Supponiamo che a sia un animale:

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

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

andranno un alimentato con un minimo di calchi.


2
@Chirs Moutray, questo non è sempre possibile, soprattutto se si tratta di una libreria.
rallentato fino al

5

Secondo gli esperimenti eseguiti su questa pagina: http://www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as

(questa pagina presenta alcuni errori di "riferimento illegale" a volte, quindi aggiorna se lo fa)

La conclusione è che l'operatore "as" è normalmente più veloce di un cast. A volte molto più veloce, a volte appena più veloce.

Peronsonalmente, la cosa "come" è anche più leggibile.

Quindi, dal momento che è sia più veloce che "più sicuro" (non genererà eccezioni), e forse più facile da leggere, consiglio di usare "as" sempre.


4

"(stringa) o" produrrà un InvalidCastException in quanto non esiste un cast diretto.

"o come stringa" comporterà che s è un riferimento null, anziché generare un'eccezione.

"o.ToString ()" non è un cast di alcun tipo di per sé, è un metodo implementato dall'oggetto, e quindi in un modo o nell'altro, da ogni classe in .net che "fa qualcosa" con l'istanza di la classe viene chiamata e restituisce una stringa.

Non dimenticare che per la conversione in stringa, c'è anche Convert.ToString (someType instanceOfThatType) in cui someType è uno di un insieme di tipi, essenzialmente i tipi di base dei framework.


3

Tutte le risposte fornite sono buone, se potessi aggiungere qualcosa: per usare direttamente i metodi e le proprietà della stringa (es. ToLower) non puoi scrivere:

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

puoi solo scrivere:

((string)o).ToLower();

ma invece potresti scrivere:

(o as string).ToLower();

L' asopzione è più leggibile (almeno secondo la mia opinione).


il costrutto (o come stringa) .ToLower () sconfigge lo scopo dell'operatore as. Ciò genererà un'eccezione di riferimento null quando non è possibile eseguire il cast su stringa.
Giacomo,

@james - Ma chi ha detto che l'unico scopo dell'operatore as è quello di lanciare un'eccezione se il cast fallisce? Se sai che o è una stringa e vuoi solo scrivere un codice più pulito, puoi usare al (o as string).ToLower()posto delle parentesi multiple confuse.
BornToCode

lo scopo di as è esattamente l'opposto: non dovrebbe lanciare l'eccezione quando il cast fallisce, dovrebbe restituire null. Diciamo che la tua o è una stringa con un valore null, cosa succederà allora? Suggerimento: la tua chiamata ToLower fallirà.
james

@james - Hai ragione, ma per quanto riguarda i casi in cui so per certo che non sarà nullo e ho solo bisogno di fare il casting per il compilatore per farmi accedere ai metodi di quell'oggetto?
BornToCode

1
puoi sicuramente farlo, ma non è esattamente la migliore pratica perché non vuoi fare affidamento sul chiamante o sui sistemi esterni per assicurarti che il tuo valore non sia nullo. Se stai usando C # 6, potresti farlo (o come stringa) ?. Ridurre().
james

3
string s = o as string; // 2

È preferito, in quanto evita la penalità prestazionale del doppio casting.


Ciao Chris, il link che era in questa risposta ora è un 404 ... Non sono sicuro che tu abbia un sostituto che vuoi inserire al suo posto?
Matt,

3

Sembra che i due siano concettualmente diversi.

Casting diretto

I tipi non devono essere strettamente correlati. È disponibile in tutti i tipi di sapori.

  • Cast implicito / esplicito personalizzato: di solito viene creato un nuovo oggetto.
  • Tipo di valore implicito: copia senza perdere informazioni.
  • Tipo di valore esplicito: copia e informazioni potrebbero andare perse.
  • Relazione IS-A: modifica il tipo di riferimento, altrimenti genera un'eccezione.
  • Stesso tipo: "Casting ridondante".

Sembra che l'oggetto verrà convertito in qualcos'altro.

Operatore AS

I tipi hanno una relazione diretta. Come in:

  • Tipi di riferimento: relazione IS-A Gli oggetti sono sempre gli stessi, cambiano solo i riferimenti.
  • Tipi di valore: copia i tipi di boxing e nullable.

Sembra che tu gestisca l'oggetto in modo diverso.

Campioni e 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
    }

2

Vorrei attirare l'attenzione sui seguenti aspetti specifici dell'operatore as :

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

Si noti che l'operatore as esegue solo conversioni di riferimento, conversioni nullable e conversioni di inscatolamento. L'operatore as non può eseguire altre conversioni, come le conversioni definite dall'utente, che dovrebbero invece essere eseguite utilizzando espressioni di cast.


0

Quando provo a ottenere la rappresentazione in forma di stringa di qualsiasi cosa (di qualsiasi tipo) potenzialmente potenzialmente nulla, preferisco la riga di codice in basso. È compatto, invoca ToString () e gestisce correttamente i null. Se o è null, s conterrà String.Empty.

String s = String.Concat(o);

0

Dato che nessuno l'ha menzionato, il più vicino a instanceOf a Java per parola chiave è questo:

obj.GetType().IsInstanceOfType(otherObj)

0

Utilizza il cast diretto string s = (string) o;se nel contesto logico della tua app stringè l'unico tipo valido. Con questo approccio, otterrai InvalidCastExceptione implementerai il principio di Fail-fast . La tua logica sarà protetta dal passare ulteriormente il tipo non valido o ottenere NullReferenceException se utilizzato asdall'operatore.

Se la logica prevede diversi tipi di cast, string s = o as string;verificarlo nullo utilizzare l' isoperatore.

In C # 7.0 sono apparse nuove fantastiche funzioni per semplificare il cast e il controllo è una corrispondenza del modello :

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;
 }

0

Le seguenti due forme di conversione del tipo (casting) sono supportate in C #:

|

(CV

• Convertire il tipo statico di v in c nell'espressione data

• Possibile solo se il tipo dinamico di v è c o un sottotipo di c

• In caso contrario, viene generata una InvalidCastException

|

v come C

• Variante non fatale di (c) v

• Pertanto, converte il tipo statico di v in c nell'espressione data

• Restituisce null se il tipo dinamico di v non è c o un sottotipo di c

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.