Condividi tramite


CA2000: Eliminare gli oggetti prima di perdere l'ambito

Proprietà valore
ID regola CA2000
Title Eliminare gli oggetti prima che siano esterni all'ambito
Categoria Affidabilità
La correzione causa un'interruzione o meno Non causa un'interruzione
Abilitato per impostazione predefinita in .NET 9 No

Causa

Viene creato un oggetto locale di un IDisposable tipo, ma l'oggetto non viene eliminato prima che tutti i riferimenti all'oggetto non siano nell'ambito.

Per impostazione predefinita, questa regola analizza l'intera codebase, ma è configurabile.

Descrizione regola

Se un oggetto eliminabile non viene eliminato in modo esplicito prima che tutti i riferimenti a esso non siano inclusi nell'ambito, l'oggetto verrà eliminato in un determinato momento indeterminato quando il Garbage Collector esegue il finalizzatore dell'oggetto. Poiché potrebbe verificarsi un evento eccezionale che impedirà l'esecuzione del finalizzatore dell'oggetto, l'oggetto deve essere eliminato in modo esplicito.

Casi speciali

La regola CA2000 non viene attivata per gli oggetti locali dei tipi seguenti anche se l'oggetto non viene eliminato:

Il passaggio di un oggetto di uno di questi tipi a un costruttore e quindi l'assegnazione a un campo indica un trasferimento di proprietà dispose al tipo appena costruito. Ovvero, il tipo appena costruito è ora responsabile dell'eliminazione dell'oggetto. Se il codice passa un oggetto di uno di questi tipi a un costruttore, non si verifica alcuna violazione della regola CA2000 anche se l'oggetto non viene eliminato prima che tutti i riferimenti a esso non siano inclusi nell'ambito.

Come correggere le violazioni

Per correggere una violazione di questa regola, chiamare Dispose sull'oggetto prima che tutti i riferimenti ad esso non siano nell'ambito.

È possibile usare l'istruzione ( in Visual Basic) per eseguire il using wrapping di oggetti che implementano IDisposableUsing . Gli oggetti di cui viene eseguito il wrapping in questo modo vengono eliminati automaticamente alla fine del using blocco. Tuttavia, le situazioni seguenti non devono essere gestite o non possono essere gestite con un'istruzione using :

  • Per restituire un oggetto eliminabile, l'oggetto deve essere costruito in un try/finally blocco all'esterno di un using blocco.

  • Non inizializzare i membri di un oggetto eliminabile nel costruttore di un'istruzione using .

  • Quando i costruttori protetti da un solo gestore eccezioni sono annidati nella parte di acquisizione di un'istruzioneusing, un errore nel costruttore esterno può comportare l'impossibilità di chiudere l'oggetto creato dal costruttore annidato. Nell'esempio seguente un errore nel StreamReader costruttore può comportare la mancata chiusura dell'oggetto FileStream . CA2000 contrassegna una violazione della regola in questo caso.

    using (StreamReader sr = new StreamReader(new FileStream("C:/myfile.txt", FileMode.Create)))
    { ... }
    
  • Gli oggetti dinamici devono usare un oggetto shadow per implementare il modello dispose di IDisposable oggetti.

Quando eliminare gli avvisi

Non eliminare un avviso da questa regola a meno che:

  • È stato chiamato un metodo sull'oggetto che chiama Dispose, ad esempio Close.
  • Il metodo che ha generato l'avviso restituisce un IDisposable oggetto che esegue il wrapping dell'oggetto.
  • Il metodo di allocazione non dispone della proprietà dispose; ovvero la responsabilità di eliminare l'oggetto viene trasferito a un altro oggetto o wrapper creato nel metodo e restituito al chiamante.

Eliminare un avviso

Se si vuole eliminare una singola violazione, aggiungere direttive del preprocessore al file di origine per disabilitare e quindi riabilitare la regola.

#pragma warning disable CA2000
// The code that's violating the rule is on this line.
#pragma warning restore CA2000

Per disabilitare la regola per un file, una cartella o un progetto, impostarne la gravità none su nel file di configurazione.

[*.{cs,vb}]
dotnet_diagnostic.CA2000.severity = none

Per altre informazioni, vedere Come eliminare gli avvisi di analisi del codice.

Configurare il codice da analizzare

Usare le opzioni seguenti per configurare le parti della codebase in cui eseguire questa regola.

È possibile configurare queste opzioni solo per questa regola, per tutte le regole a cui si applica o per tutte le regole in questa categoria (affidabilità) a cui si applica. Per altre informazioni, vedere Opzioni di configurazione delle regole di qualità del codice.

Escludere simboli specifici

È possibile escludere simboli specifici, ad esempio tipi e metodi, dall'analisi. Ad esempio, per specificare che la regola non deve essere eseguita in alcun codice all'interno di tipi denominati MyType, aggiungere la coppia chiave-valore seguente a un file con estensione editorconfig nel progetto:

dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType

Formati di nome simbolo consentiti nel valore dell'opzione (separati da |):

  • Solo nome simbolo (include tutti i simboli con il nome, indipendentemente dal tipo o dallo spazio dei nomi contenitore).
  • Nomi completi nel formato ID della documentazione del simbolo. Ogni nome di simbolo richiede un prefisso di tipo simbolo, ad esempio M: per i metodi, T: per i tipi e N: per gli spazi dei nomi.
  • .ctor per costruttori e .cctor per costruttori statici.

Esempi:

Valore opzione Riepilogo
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType Corrisponde a tutti i simboli denominati MyType.
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType1|MyType2 Corrisponde a tutti i simboli denominati MyType1 o MyType2.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS.MyType.MyMethod(ParamType) Corrisponde a un metodo MyMethod specifico con la firma completa specificata.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS1.MyType1.MyMethod1(ParamType)|M:NS2.MyType2.MyMethod2(ParamType) Trova la corrispondenza con metodi MyMethod1 specifici e MyMethod2 con le rispettive firme complete.

Escludere tipi specifici e i relativi tipi derivati

È possibile escludere tipi specifici e i relativi tipi derivati dall'analisi. Ad esempio, per specificare che la regola non deve essere eseguita in alcun metodo all'interno di tipi denominati MyType e dei relativi tipi derivati, aggiungere la coppia chiave-valore seguente a un file con estensione editorconfig nel progetto:

dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType

Formati di nome simbolo consentiti nel valore dell'opzione (separati da |):

  • Solo nome di tipo (include tutti i tipi con il nome, indipendentemente dal tipo o dallo spazio dei nomi contenitore).
  • Nomi completi nel formato ID della documentazione del simbolo, con un prefisso facoltativoT:.

Esempi:

Valore opzione Riepilogo
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType Corrisponde a tutti i tipi denominati MyType e a tutti i relativi tipi derivati.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType1|MyType2 Corrisponde a tutti i tipi denominati MyType1 o MyType2 e a tutti i relativi tipi derivati.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS.MyType Corrisponde a un tipo MyType specifico con il nome completo specificato e tutti i relativi tipi derivati.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS1.MyType1|M:NS2.MyType2 Corrisponde a tipi MyType1 specifici e MyType2 con i rispettivi nomi completi e tutti i relativi tipi derivati.

Esempio 1

Se si implementa un metodo che restituisce un oggetto eliminabile, usare un blocco try/finally senza un blocco catch per assicurarsi che l'oggetto venga eliminato. Usando un blocco try/finally, è possibile generare eccezioni nel punto di errore e assicurarsi che l'oggetto venga eliminato.

Nel metodo OpenPort1 la chiamata per aprire l'oggetto ISerializable SerialPort o la chiamata a SomeMethod può non riuscire. In questa implementazione viene generato un avviso CA2000.

Nel metodo OpenPort2 vengono dichiarati due oggetti SerialPort e impostati su null:

  • tempPort, usato per verificare che le operazioni del metodo abbiano esito positivo.

  • port, utilizzato per il valore restituito del metodo .

L'oggetto tempPort viene costruito e aperto in un try blocco e qualsiasi altro lavoro necessario viene eseguito nello stesso try blocco. Alla fine del try blocco, la porta aperta viene assegnata all'oggetto port che verrà restituito e l'oggetto tempPort è impostato su null.

Il finally blocco controlla il valore di tempPort. Se non è Null, un'operazione nel metodo non è riuscita e tempPort viene chiusa per assicurarsi che tutte le risorse vengano rilasciate. L'oggetto porta restituito conterrà l'oggetto SerialPort aperto se le operazioni del metodo sono riuscite oppure sarà Null se un'operazione non è riuscita.

public SerialPort OpenPort1(string portName)
{
   SerialPort port = new SerialPort(portName);
   port.Open();  //CA2000 fires because this might throw
   SomeMethod(); //Other method operations can fail
   return port;
}

public SerialPort OpenPort2(string portName)
{
   SerialPort tempPort = null;
   SerialPort port = null;
   try
   {
      tempPort = new SerialPort(portName);
      tempPort.Open();
      SomeMethod();
      //Add any other methods above this line
      port = tempPort;
      tempPort = null;

   }
   finally
   {
      if (tempPort != null)
      {
         tempPort.Close();
      }
   }
   return port;
}
Public Function OpenPort1(ByVal PortName As String) As SerialPort

   Dim port As New SerialPort(PortName)
   port.Open()    'CA2000 fires because this might throw
   SomeMethod()   'Other method operations can fail
   Return port

End Function

Public Function OpenPort2(ByVal PortName As String) As SerialPort

   Dim tempPort As SerialPort = Nothing
   Dim port As SerialPort = Nothing

   Try
      tempPort = New SerialPort(PortName)
      tempPort.Open()
      SomeMethod()
      'Add any other methods above this line
      port = tempPort
      tempPort = Nothing

   Finally
      If Not tempPort Is Nothing Then
         tempPort.Close()
      End If

   End Try

   Return port

End Function

Esempio 2

Per impostazione predefinita, il compilatore Visual Basic dispone di tutti gli operatori aritmetici che controllano l'overflow. Pertanto, qualsiasi operazione aritmetica di Visual Basic potrebbe generare un'eccezione OverflowException. Ciò potrebbe causare violazioni impreviste nelle regole, ad esempio CA2000. Ad esempio, la funzione CreateReader1 seguente genererà una violazione CA2000 perché il compilatore di Visual Basic genera un'istruzione di controllo dell'overflow per l'aggiunta che potrebbe generare un'eccezione che causerebbe l'eliminazione di StreamReader.

Per risolvere questo problema, è possibile disabilitare l'emissione di controlli di overflow dal compilatore Visual Basic nel progetto oppure modificare il codice come nella funzione CreateReader2 seguente.

Per disabilitare l'emissione di controlli di overflow, fare clic con il pulsante destro del mouse sul nome del progetto in Esplora soluzioni e quindi scegliere Proprietà. Selezionare Compila opzioni di compilazione avanzate e quindi selezionare >Rimuovi controlli di overflow integer.

Imports System.IO

Class CA2000
    Public Function CreateReader1(ByVal x As Integer) As StreamReader
        Dim local As New StreamReader("C:\Temp.txt")
        x += 1
        Return local
    End Function


    Public Function CreateReader2(ByVal x As Integer) As StreamReader
        Dim local As StreamReader = Nothing
        Dim localTemp As StreamReader = Nothing
        Try
            localTemp = New StreamReader("C:\Temp.txt")
            x += 1
            local = localTemp
            localTemp = Nothing
        Finally
            If (Not (localTemp Is Nothing)) Then
                localTemp.Dispose()
            End If
        End Try
        Return local
    End Function
End Class

Vedi anche