2010-04-22 13 views
10

Viene visualizzato un avviso quando si esegue un codice tramite l'utilità di analisi del codice di Visual Studio che non sono sicuro su come risolvere. Forse qualcuno qui ha trovato un problema simile, lo ha risolto ed è disposto a condividere le proprie opinioni.CA2000 passando il riferimento oggetto al costruttore base in C#

Sto programmando una cella personalizzata utilizzata in un controllo DataGridView. Il codice è simile:

public class DataGridViewMyCustomColumn : DataGridViewColumn 
{ 
    public DataGridViewMyCustomColumn() : base(new DataGridViewMyCustomCell()) 
    { 
    } 

genera il seguente avviso:

CA2000: Microsoft.Reliability: Nel metodo 'DataGridViewMyCustomColumn.DataGridViewMyCustomColumn()' chiamare System.IDisposable.Dispose sull'oggetto 'nuova DataGridViewMyCustomCell() 'prima che tutti i riferimenti siano fuori portata.

ho capito che mi sta avvisando DataGridViewMyCustomCell (o di una classe che eredita da) implementa l'interfaccia IDisposable e il metodo Dispose() dovrebbe essere chiamato per ripulire tutte le risorse rivendicate da DataGridViewMyCustomCell quando non è più.

Gli esempi che ho visto su Internet suggeriscono un blocco usando per circoscrivere la durata dell'oggetto e il sistema lo dispone automaticamente, ma la base non viene riconosciuta quando viene spostata nel corpo del costruttore così posso scrivere un blocco usando attorno ad esso ... che non sono sicuro di voler fare comunque, dal momento che non dovrebbe istruire il tempo di esecuzione per liberare l'oggetto che potrebbe ancora essere usato in seguito all'interno della classe base?

La mia domanda è, quindi, il codice è ok? Oppure, come potrebbe essere refactored per risolvere l'avvertimento? Non voglio sopprimere l'avviso a meno che non sia veramente appropriato farlo.

risposta

18

Se si utilizza Visual Studio 2010, CA2000 è completamente danneggiato. Può anche essere rotto in altre versioni di FxCop (a.k.a. Code Analysis), ma VS2010 è l'unico che posso garantire. La nostra base di codice sta dando avvertimenti CA2000 per il codice come questo ...

internal static class ConnectionManager 
{ 
    public static SqlConnection CreateConnection() 
    { 
     return new SqlConnection("our connection string"); 
    } 
} 

... che indica che la connessione non è in via di dismissione prima che vada fuori del campo di applicazione del metodo. Beh, sì, è vero, ma non è fuori portata per l'applicazione come viene restituito a un chiamante - questo è l'intero punto del metodo! Allo stesso modo, l'argomento del costruttore non sta andando fuori portata ma viene passato alla classe base, quindi è un falso positivo dalla regola piuttosto che un problema reale.

Questa era una regola utile, ma ora tutto ciò che si può veramente fare è spegnerlo finché non lo risolvono. Il che è sfortunato, perché i (pochi) positivi effettivi sono cose che dovrebbero essere corrette.

+3

+1 per "completamente rotto in VS10". È molto difficile convincere le persone ad unirsi a me in un giro di miglioramento della qualità del codice quando gli strumenti mi fanno sembrare così stupido ... –

0

Grazie, Greg. Questa è la mia preoccupazione: i pochi elementi positivi che devono risolvere. Bene, forse nella prossima iterazione di VS sarà più accurato. Per ora controllerò il codice di avviso.

+0

Ho inserito la seguente soppressione nel mio codice: '[System.Diagnostics.CodeAnalysis.SuppressMessage (" Microsoft .Reliability "," CA2000: elimina gli oggetti prima di perdere l'ambito. "Questa è una regola non valida. Consulta http://stackoverflow.com/q/2687398/228059 per ulteriori")] " – noonand

+0

Oppure puoi creare un file .ruleset, selezionare quali regole si desidera conservare e associarle a Tipi di costruzione nelle proprietà della soluzione. Ecco [un esempio] (https://github.com/madskristensen/WebEssentials2013/blob/master/EditorExtensions/CodeAnalysis.ruleset). Per impostazione predefinita, VS aprirà un editor in modalità di progettazione con caselle di controllo per il file .ruleset. –

1

Non esiste un modo sicuro ed elegante per fare in modo che un costruttore concatenato passi un nuovo oggetto IDisposable al costruttore base, poiché come si nota non è possibile racchiudere la chiamata del costruttore concatenato in qualsiasi tipo di blocco .V'è un approccio che è sicuro, ma non è certo elegante: definire un metodo di utilità qualcosa come:

internal static TV storeAndReturn<TR,TV>(ref TR dest, TV value) where TV:TR 
{ 
    dest = value; return value; 
} 

Avere il costruttore aspetto qualcosa di simile:

protected DataGridViewMyCustomColumn(ref IDisposable cleaner) : 
    base(storeAndReturn(ref cleaner, new DataGridViewMyCustomCell())) 
{ 
} 

codice che ha bisogno di un nuovo oggetto sarebbe poi avere chiamare un metodo factory statico pubblico che chiamerebbe il costruttore appropriato all'interno di un blocco try/finally la cui riga principale sarebbe nullo cleaner poco prima che finisse e il cui blocco finally chiamerebbe Dispose su cleaner se esso non è nullo A condizione che ogni sottoclasse definisca un metodo factory simile, questo approccio garantirà che il nuovo oggetto IDisposable venga eliminato anche se si verifica un'eccezione tra il momento in cui viene creato e il momento in cui l'oggetto incapsulante viene esposto al codice client. Il modello è brutto, ma non sono sicuro che nessun altro schema migliore possa assicurare la correttezza.

Problemi correlati