2010-09-22 14 views
6

In una domanda precedente oggi questi due diversi approcci sono stati assegnati a una domanda come risposta.L'uso della trasmissione è due volte o utilizza una sola volta e crea una nuova variabile

Abbiamo un oggetto che potrebbe o meno implementare IDisposable. Se lo fa, vogliamo eliminarlo, altrimenti non faremo nulla. I due approcci differenti sono queste:

1)

if(toDispose is IDisposable) 
    (toDispose as IDisposable).Dispose(); 

2)

IDisposable disposable = toDispose as IDisposable; 
if(disposable != null) 
    disposable.Dispose(); 

Principalmente, dai commenti che suona come il consenso era che 2) è stato l'approccio migliore.

Ma guardando le differenze, scendiamo a questo:

1) Eseguire un cast due volte sul toDispose.

2) Esegui il cast una sola volta, ma crea un nuovo oggetto intermedio.

Direi che 2 sarà leggermente più lento perché deve allocare una nuova variabile locale, quindi perché è considerata la soluzione migliore in questo caso? È solo a causa di problemi di leggibilità?

+0

Su una nota a margine, non mi preoccuperei delle variabili intermedie; una volta che il jitter ha avuto la sua strada con il tuo codice, è probabile che tutte le ipotesi che fai su micro-ottimizzazioni vengano invalidate. –

risposta

12

mie regole pratiche in giro per casting:

  • se si tratta di un errore/bug per il valore non essere del tipo giusto, appena gettati
  • Altrimenti utilizzare as, come nel tuo secondo caso
  • Se hai a che fare con un tipo di valore, è possibile utilizzare as con il tipo nullable (per coerenza) o utilizzare is e un cast diretta

Si noti che il secondo modulo non corrisponde a creando un "nuovo oggetto intermedio". Crea una nuova variabile intermedia . Quindi il tuo primo approccio è davvero - è solo che la variabile è nascosta dal compilatore, in modo efficace. È ancora presente nell'IL come posizione di stack, ma non ha alcuna rappresentazione nel codice sorgente.

Detto tutto questo, nel questo caso particolare (dove si vuole solo disporre), l'approccio di Darin è il migliore, credo.

+0

+1 per suggerire di trasmettere direttamente. Ho visto un sacco di codice in cui le persone gestiscono prematuramente/omettono InvalidCastException/NullReferenceException dove in realtà avrebbe dovuto essere lanciato perché in realtà * è * un errore. – bitbonk

+0

Mi sembrano buone regole. Non ero a conoscenza del fatto che la prima versione crea anche una variabile dietro le quinte, quindi è davvero un gioco da ragazzi e 2) è la strada da percorrere. –

+0

Ed era una variabile intermedia a cui pensavo, ma per qualche motivo è stata espressa come oggetto intermeidato. So che non crea un nuovo oggetto sullo heap, ma una nuova variabile sullo stack. Grazie mille, signor Skeet. –

13

Non proprio rispondere alla tua domanda, ma io consiglierei di questo costrutto:

using (toDispose as IDisposable) 
{ 
    ... 
} 

e lasciare che la preoccupazione compilatore sulle chiamate ifs, finally e Dispose.

+0

+1. Basta dire che usare (toDispose) è sufficiente, poiché verrà eliminato solo se è IDisposable. – eglasius

+0

@eglasius, ma potrebbe non essere compilato in base al tipo di 'toDispose'. Ad esempio: 'object to Dispose = FetchSomeDisposableResource();'. –

+0

è vero, in effetti l'ho ricontrollato. La mia memoria è venuta a mancare, ho ricordato di averlo fatto molto tempo fa, ma credo di averne dovuto usare anche come IDisposable ... e si trattava in realtà dell'istruzione using che ignorava null. – eglasius

4

È considerato una soluzione migliore poiché un'assegnazione variabile è considerata più veloce di un cast. Inoltre, il cast può essere probabilmente ottimizzato dal compilatore.

In entrambi i casi, questo è microottimizzazione comunque. Usa il codice che ritieni sia più facile da mantenere.

2

La seconda alternativa è in realtà un po 'più veloce. Ci vuole più tempo per fare un cast extra rispetto all'accesso a un locale varaible.

Non è lento allocare variabili locali. In realtà non ci vuole tempo.

Quando viene immesso un metodo, viene creato uno stack frame spostando in basso il puntatore dello stack per allocare lo spazio per i dati locali nel metodo. Non ci vuole più tempo per sottrarre un numero più grande dal puntatore dello stack, quindi non ci vuole tempo per allocare spazio per le variabili locali. E non è quasi tempo, ma in realtà tempo zero.

L'unico costo delle variabili locali è che si utilizza lo spazio di stack, ma poiché si tratta solo di pochi byte dallo stack da 1 megabyte (IIRC) non è un problema, a meno che non si effettuino chiamate ricorsive profonde.

Inoltre, il compilatore può creare variabili locali da solo se è necessario, quindi è anche possibile che entrambi i metodi finiscano per utilizzare una variabile locale alla fine.

1

L'uso della riflessione potrebbe funzionare anche qui o sarebbe lento rispetto all'utilizzo di as?

Ho creato 2 classi, una denominata ICanDispose che implementa IDisposable e un'altra denominata ICanNotDispose non in esecuzione IDisposable.

Nella mia classe ICanDispose ho semplicemente mostrato una finestra di messaggio per verificare che il reflection abbia effettivamente invocato il metodo Dispose.

Ho un pulsante sul mio modulo che esegue il codice qui sotto. Usando i riflessi controlla se il mio oggetto implementa l'interfaccia IDisposable e in tal caso invoca il metodo Dispose.

private void button1_Click(object sender, EventArgs e) 
{ 
    ICanDispose myObject1 = new ICanDispose(); 
    ICanNotDispose myObject2 = new ICanNotDispose(); 

    // Checking object 1 which does implement IDisposable. 
    if (myObject1.GetType().GetInterface("IDisposable", true) != null) 
    { 
     myObject1.GetType().InvokeMember(
      "Dispose", 
      System.Reflection.BindingFlags.Default | System.Reflection.BindingFlags.InvokeMethod, 
      null, 
      Activator.CreateInstance(myObject1.GetType()), 
      null); 
    } 

    // Checking object 2 which does not implement IDisposable.  
    if (myObject2.GetType().GetInterface("IDisposable", true) != null) 
    { 
     myObject2.GetType().InvokeMember(
      "Dispose", 
      System.Reflection.BindingFlags.Default | System.Reflection.BindingFlags.InvokeMethod, 
      null, 
      Activator.CreateInstance(myObject2.GetType()), 
      null); 
    } 
} 
1

Nessuno ha menzionato qui che FxCop mostra un DoNotCastUnnecessarily avviso in caso di prima approch. Aiuto per questa regola suggerisce il secondo approccio al posto di prima

http://msdn.microsoft.com/en-us/library/ms182271(VS.90).aspx

calchi duplicati degradare le prestazioni, soprattutto quando i calchi sono eseguite in iterazione compatta dichiarazioni. Per le operazioni di trasmissione duplicate esplicite , archiviare il risultato di il cast in una variabile locale e utilizzare la variabile locale anziché le operazioni di trasmissione duplicate .

Se il C# è operatore viene utilizzato per verificare se la il cast avrà successo prima viene eseguito il cast attuale, in considerazione testare il risultato della qualità di operatore invece. Ciò fornisce la stessa funzionalità senza l'operazione di trasmissione implicita eseguita dall'operatore .

Problemi correlati