2011-08-22 12 views
22

Durante la lettura di codice C# Ho trovato un piuttosto curioso frammento:Che senso ha usare "is" seguito da "as" anziché "as" seguito da un controllo nullo in C#?

if(whatever is IDisposable) { 
    (whatever as IDisposable).Dispose(); 
} 

Preferirei aspetto che viene fatto sia in questo modo:

if(whatever is IDisposable) { //check 
    ((IDisposable)whatever).Dispose(); //cast - won't fail 
} 

o come questo:

IDisposable whateverDisposable = whatever as IDisposable; 
if(whateverDisposable != null) { 
    whateverDisposable.Dispose(); 
} 

Voglio dire as è come un cast, ma restituisce null in caso di fallimento. Nel secondo frammento non può fallire (poiché c'è un controllo is prima).

Qual è lo scopo di scrivere il codice come nel primo frammento anziché come nel secondo o nel terzo?

+0

Immagino che quest'ultimo lasci una variabile nell'ambito, ma lo faccio sempre comunque. Penso di aver letto recentemente un commento di Jon Skeet che preferisce anche l'ultimo caso. Anche se sarei sorpreso se non ottimizzassero tutti allo stesso modo. – Rup

+3

La terza opzione è la mia preferita, dato che hai solo un 'as' /' is' e un controllo nullo - sembra meno lavoro/meno sovraccarico di avere un 'is' prima e un' as' successivo ... –

+0

Ho visto l'ultimo esempio in un libro, mi sembra anche più logico. – atoMerz

risposta

1

Per rispondere alla tua domanda attuale: Esperienza e abitudine.

Prima dell'inserimento della parola chiave as in .Net 2.0, l'unico modo per determinare in modo sicuro se un oggetto poteva essere trasmesso a un tipo/interfaccia specifico era con la parola chiave is.

Quindi, le persone hanno preso l'abitudine di usare is prima di provare un cast esplicito per evitare eccezioni inutili. Ciò ha portato alla struttura che abbiamo secondo nella lista dei campioni:

if(whatever is IDisposable) //check 
{ 
    ((IDisposable)whatever).Dispose(); //cast - won't fail 
} 

Poi, abbiamo ottenuto il as parola chiave sicura pressofuso. Direi che la maggior parte delle persone, quando hanno iniziato a usare as, ha continuato a utilizzare il modello familiare, ma ha sostituito il cast diretto con un cast sicuro e il modello scelto si è trasformato nell'esempio 1. (So che l'ho fatto per . un po ')

if(whatever is IDisposable) 
{ 
    (whatever as IDisposable).Dispose(); 
} 

alla fine, molti o la maggior parte sia realizzati in proprio, o sono stati istruiti da FxCop o CodeAnalysis che il modello 'corretta' è il tuo esempio 3:

IDisposable whateverDisposable = whatever as IDisposable; 
if(whateverDisposable != null) 
{ 
    whateverDisposable.Dispose(); 
} 

Certamente ci sono alcuni fluttuare intorno a chi è ancora in fase 1 e non ha ancora "evoluto" il proprio modello nell'esempio 3 per qualche motivo, o altri ancora sto ancora usando il buon vecchio modello di utilizzo di is seguito da un cast diretto.

1

Hai ragione, il primo codice che hai postato non è necessario. Utilizzare il secondo se si desidera che l'ambito della variabile casted si trovi all'interno dello if o utilizzi il terzo, se non è importante, perché sta facendo la minima quantità di lavoro.

3

non esiste un punto di utilizzare il as dopo la is è vero, ho il sospetto che il codice si legge o il libro che è solo mostrando come dichiarare e utilizzare as e is. Io lo uso come da te suggerito nel secondo frammento di tramite:

IDisposable whateverDisposable = whatever as IDisposable; 
if(whateverDisposable != null) 
{ 
    whateverDisposable.Dispose(); 
} 

Si noti anche che nel tuo primo tentativo "((IDisposable)whatever)", si sta casting l'oggetto che avrà performance "tempo di cast", qui si sta casting due volte la prima volta utilizzando as e il secondo utilizzando il cast esplicito, quindi il modo migliore è implementarlo utilizzando il secondo metodo "as IDisposable quindi verificare se è null".

3

is seguito da as è un costrutto inutile, come sospetti. In definitiva, se non sei sicuro di quale tipo sia o di quali interfacce implementa, esistono due idiomi: uno per i tipi di riferimento e uno per i tipi di valore.

Durante il test per i tipi di riferimento (o un valore Null in box), l'ultimo modello fornito è corretto - as seguito da un test Null. Questo sarà l'approccio più veloce in quanto il runtime dovrà solo eseguire un test di tipo.

Durante il test per i tipi di valore in box, il secondo modello fornito è corretto - is seguito da un cast.

15

Sei corretto. Il primo frammento non ha senso.

Dalle alternative, consiglierei il terzo, poiché è thread-safe nel senso che tra il controllo e la chiamata del metodo, l'oggetto non può essere sostituito da un altro che non implementa l'interfaccia. Considerare il seguente scenario con il secondo frammento:

if (whatever is IDisposable) { //check 
    // <-- here, some other thread changes the value of whatever 
    ((IDisposable)whatever).Dispose(); // could fail 
} 

Questo non può accadere con il terzo frammento.

Nota: ci sono some subtle differences tra un cast e as in relazione alle conversioni definite dall'utente, ma non si applicano in questo caso.

+0

+1 per scenario multithreading. –

+0

È facile rendere gli altri thread sicuri. 'x = qualunque cosa; se (x è IDisposable) {((IDisposable) x) .Dispose(); } ', anche se è un po 'più prolisso. –

+0

Capisco che lo snippet 3 sia "thread safe" in quanto nulla può togliere o modificare il riferimento appena creato. Tuttavia potrebbe esserci uno switch di thread subito dopo il controllo null che esegue qualcosa con quell'oggetto tramite un altro riferimento, quindi l'oggetto potrebbe essere già disposto per l'istanza. Puoi spiegare cosa intendi per thread safe? –

2

D'accordo con il punto, ma se davvero ciò che al look pulito, come su di un metodo di estensione, come ad esempio:

public static void DisposeIfNecessary(this object obj) 
{ 
    if (obj != null && obj is IDisposable) 
     ((IDisposable)obj).Dispose(); 
} 
1

Inoltre, l'ultima opzione, ovviamente, ti dà una variabile in-campo di applicazione che si può riutilizzare in seguito senza dover ri-invocare un cast.

1

is seguito da as è inutile, oltre ad essere più veloce da digitare (a causa dei tasti necessari per le staffe e quanto utile intellisense è con as). È leggermente più lento di entrambi gli altri esempi poiché il runtime deve eseguire due controlli di tipo.

Il CLR ha qualche inganno per as, e come tale dovrebbe essere più veloce rispetto al is seguita dal cast (tuttavia, tenere a mente che non è possibile utilizzare as con tipi di valore).