2012-06-28 10 views
14

In genere quando si smaltisce un membro privato, si potrebbe procedere come segue:Dillo FxCop un altro metodo sta chiamando smaltire

public void Dispose() { 
    var localInst = this.privateMember; 
    if (localInst != null) { 
     localInst.Dispose(); 
    } 
} 

Lo scopo della configurazione locale è quello di evitare una condizione di competizione in cui un altro thread potrebbe assegnare il privato membro da null dopo il controllo nullo. In questo caso, non mi interessa se Dispose viene chiamato due volte sull'istanza.

Io uso questo schema tutto il tempo così ho scritto un metodo di estensione per fare questo:

public static void SafeDispose(this IDisposable disposable) 
{ 
    if (disposable != null) 
    { 
     // We also know disposable cannot be null here, 
     // even if the original reference is null. 
     disposable.Dispose(); 
    } 
} 

E ora nella mia classe, posso solo fare questo:

public void Dispose() { 
    this.privateMember.SafeDispose(); 
} 

Il problema è che, FxCop non ha idea che sto facendo questo e mi dà l'avviso CA2000: Dispose objects before losing scope in ogni caso.

Non voglio disattivare questa regola e non voglio sopprimere ogni caso. C'è un modo per suggerire a FxCop che questo metodo è equivalente a Dispose per quanto è interessato?

+3

È campione dice 'this.privateMember.Dispose()'. Il tuo codice in realtà dice 'this.privateMember.SafeDispose()'? –

risposta

8

La risposta breve è: non c'è modo di accennare che l'oggetto è in via di dismissione altrove.

Un po 'di Reflector-ing (o dotPeeking, o qualsiasi altra cosa) spiega perché.

FxCop è in C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop. (Regolare di conseguenza per la combinazione della versione OS/VS.) Le regole sono nella sottodirectory Rules.

Nella principale FxCop cartella, aperto

  • Microsoft.VisualStudio.CodeAnalysis.dll
  • Microsoft.VisualStudio.CodeAnalysis.Phoenix.dll
  • phx.dll

Nella cartella di Rules, aperto DataflowRules.dll.

In DataflowRules.dll trovare Phoenix.CodeAnalysis.DataflowRules.DisposeObjectsBeforeLosingScope. Questa è la classe reale che fa la valutazione.

Guardando il codice lì dentro, puoi vedere due cose di interesse rispetto alla tua domanda.

  1. Esso utilizza un servizio condiviso chiamato SharedNeedsDisposedAnalysis.
  2. E deriva da FunctionBodyRule.

Il primo elemento è interessante perché SharedNeedsDisposedAnalysis è quello che determina quali simboli devono Dispose() chiamato. È piuttosto accurato, facendo una "passeggiata" attraverso il codice per determinare cosa deve essere smaltito e cosa viene effettivamente eliminato. Quindi mantiene un tavolo di quelle cose per un uso successivo.

Il secondo elemento è interessante perché le regole FunctionBodyRule valutano il corpo di una singola funzione. Esistono altri tipi di regole, ad esempio FunctionCallRule, che valutano elementi come i membri di una chiamata di funzione (ad esempio, ProvideCorrectArgumentsToFormattingMethods).

il punto è, tra il potenziale "miss" in quel SharedNeedsDisposedAnalysis servizio in cui esso non può essere recursing attraverso il metodo di vedere che le cose in realtà sono sempre disposti e la limitazione di FunctionBodyRule comunque non oltre il corpo della funzione, è solo non prendere la tua estensione.

Questa è la stessa ragione "Funzioni di guardia" come non arrivano mai viste come convalidare l'argomento prima di usarlo - FxCop sarà ancora dirvi di controllare l'argomento per nulla, anche se questo è ciò che la "funzione di guardia" sta facendo .

Hai fondamentalmente due opzioni.

  1. Escludi problemi o disattiva la regola. Non c'è modo che farà ciò che vuoi.
  2. Creare una regola personalizzata/derivata che comprenda i metodi di estensione. Usa la regola personalizzata al posto della regola predefinita.

Dopo aver personalizzato scritto FxCop mi governa, io farò sapere l'ho trovato ... non banale. Se si scende su quella strada, mentre la raccomandazione nel mondo è quella di utilizzare il nuovo stile di regola del motore Phoenix (è quello che utilizza l'attuale DisposeObjectsBeforeLosingScope), ho trovato più facile capire le regole SDK FxCop precedenti/standard (vedere FxCopSdk.dll in la cartella principale di FxCop). Reflector sarà di grande aiuto nel capire come farlo dato che c'è praticamente zero documenti su di esso. Cerca negli altri assembly nella cartella Rules per vedere esempi di quelli.

+1

In modo acutico, esiste un meccanismo per il riconoscimento delle funzioni di guardia da CA1062: decorazione con un attributo denominato ValidatedNotNullAttribute. Sfortunatamente, non esiste un meccanismo simile per le regole di disposizione. –

+0

Grazie Travis! Conosci una buona guida per scrivere regole personalizzate? Ho fxcop in esecuzione come parte della mia build e voglio assicurarmi che possa caricare le regole dal sorgente (non da Program Files). – Haacked

+0

Ho trovato [questo] (http://www.binarycoder.net/fxcop/pdf/fxcop.pdf) white paper molto utile. – riezebosch

1

Non sono affatto un esperto di FxCop, ma lo è this question sull'utilizzo di SuppressMessage? Non so se decorare il tuo metodo SafeDispose con l'attributo SuppressMessage farebbe in modo che FxCop sopprima quel messaggio sulla sua analisi dei metodi che lo chiamano, ma sembra che valga la pena provare.

Non fidarti la sintassi di seguito, ma qualcosa di simile:

[SuppressMessage("Microsoft.Design", "CA2000:Dispose objects before losing scope", Justification = "We just log the exception and return an HTTP code")] 
public static void SafeDispose(this IDisposable disposable) 
0

Questa regola di analisi del codice è problematica, per tutte le ragioni illustrate da Travis. Sembra sospendere qualsiasi operazione "nuova" e, a meno che la chiamata di eliminazione non sia chiusa, i trigger CA2000.

Invece di utilizzare nuove, chiamare un metodo con questo nel corpo:

MyDisposableClass result; 
MyDisposableClass temp = null; 
try 
{ 
    temp = new MyDisposableClass(); 
    //do any initialization here 
    result = temp; 
    temp = null; 
} 
finally 
{ 
    if (temp != null) temp.Dispose(); 
} 
return result; 

Quello che fa è eliminare qualsiasi possibilità di inizializzazione che causa l'oggetto irraggiungibile per lo smaltimento. Nel tuo caso, quando lo hai "rinnovato" privatemember, lo faresti all'interno di un metodo simile al metodo precedente. Dopo aver utilizzato questo modello, sei naturalmente sempre responsabile del corretto smaltimento e il tuo metodo di estensione è un ottimo modo per generalizzare quel controllo nullo.

Ho scoperto che è possibile evitare CA2000 mentre si passano ancora IDisposables in giro e fare ciò che si desidera con essi, a patto che li si aggiorni correttamente con un metodo come sopra. Fai un tentativo e fammi sapere se funziona per te. Buona fortuna e buona domanda!

Altre correzioni di questa regola (compreso questo) sono descritte qui: CA2000: Dispose objects before losing scope (Microsoft)

Problemi correlati