CA2000: Usuwanie obiektów przed utratą zakresu

Właściwości Wartość
Identyfikator reguły CA2000
Tytuł Likwiduj obiekty przed utratą zakresu
Kategoria Niezawodność
Poprawka powodująca niezgodność lub niezgodność Niezgodność
Domyślnie włączone na platformie .NET 8 Nie.

Przyczyna

Tworzony jest obiekt IDisposable lokalny typu, ale obiekt nie jest usuwany, zanim wszystkie odwołania do obiektu są poza zakresem.

Domyślnie ta reguła analizuje całą bazę kodu, ale można to skonfigurować.

Opis reguły

Jeśli obiekt jednorazowy nie jest jawnie usuwany, zanim wszystkie odwołania do niego są poza zakresem, obiekt zostanie usunięty w określonym czasie, gdy moduł odśmiecanie pamięci uruchamia finalizator obiektu. Ponieważ może wystąpić wyjątkowe zdarzenie, które uniemożliwi uruchomienie finalizatora obiektu, obiekt powinien zostać jawnie usunięty.

Przypadki szczególne

Reguła CA2000 nie jest uruchamiana dla obiektów lokalnych następujących typów, nawet jeśli obiekt nie jest usuwany:

Przekazanie obiektu jednego z tych typów do konstruktora, a następnie przypisanie go do pola wskazuje przeniesienie własności usuwania do nowo skonstruowanego typu. Oznacza to, że nowo skonstruowany typ jest teraz odpowiedzialny za usuwanie obiektu. Jeśli kod przekazuje obiekt jednego z tych typów do konstruktora, żadne naruszenie reguły CA2000 nie występuje, nawet jeśli obiekt nie jest usuwany przed wszystkimi odwołaniami do niego są poza zakresem.

Jak naprawić naruszenia

Aby naprawić naruszenie tej reguły, wywołaj Dispose obiekt przed wszystkimi odwołaniami do niego są poza zakresem.

Możesz użyć instrukcji using (Using w Visual Basic), aby opakowować obiekty implementujące IDisposable. Obiekty, które są opakowane w ten sposób, są automatycznie usuwane na końcu using bloku. Jednak następujące sytuacje nie powinny być obsługiwane lub nie mogą być obsługiwane za pomocą instrukcji using :

  • Aby zwrócić obiekt jednorazowy, obiekt musi być skonstruowany w try/finally bloku poza blokiem using .

  • Nie inicjuj elementów członkowskich obiektu jednorazowego w konstruktorze instrukcji using .

  • Gdy konstruktory chronione tylko przez jedną procedurę obsługi wyjątków są zagnieżdżone w części using pozyskiwania instrukcji, błąd w konstruktorze zewnętrznym może spowodować, że obiekt utworzony przez zagnieżdżony konstruktor nigdy nie jest zamknięty. W poniższym przykładzie błąd konstruktora StreamReader może spowodować FileStream , że obiekt nigdy nie zostanie zamknięty. CA2000 flaguje naruszenie reguły w tym przypadku.

    using (StreamReader sr = new StreamReader(new FileStream("C:/myfile.txt", FileMode.Create)))
    { ... }
    
  • Obiekty dynamiczne powinny używać obiektu w tle do implementowania wzorca IDisposable usuwania obiektów.

Kiedy pomijać ostrzeżenia

Nie pomijaj ostrzeżenia z tej reguły, chyba że:

  • Wywołaliśmy metodę w obiekcie, która wywołuje Disposemetodę , taką jak Close.
  • Metoda, która zgłosiła ostrzeżenie, zwraca IDisposable obiekt, który opakowuje obiekt.
  • Metoda przydzielania nie ma własności usuwania; oznacza to, że odpowiedzialność za usuwanie obiektu jest przenoszona do innego obiektu lub otoki utworzonej w metodzie i zwracana do obiektu wywołującego.

Pomijanie ostrzeżenia

Jeśli chcesz po prostu pominąć pojedyncze naruszenie, dodaj dyrektywy preprocesora do pliku źródłowego, aby wyłączyć, a następnie ponownie włączyć regułę.

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

Aby wyłączyć regułę dla pliku, folderu lub projektu, ustaw jego ważność na none w pliku konfiguracji.

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

Aby uzyskać więcej informacji, zobacz Jak pominąć ostrzeżenia dotyczące analizy kodu.

Konfigurowanie kodu do analizowania

Użyj poniższych opcji, aby skonfigurować, które części bazy kodu mają być uruchamiane w tej regule.

Można skonfigurować te opcje tylko dla tej reguły, dla wszystkich reguł, których dotyczy, lub dla wszystkich reguł w tej kategorii (niezawodność), których dotyczy. Aby uzyskać więcej informacji, zobacz Opcje konfiguracji reguły jakości kodu.

Wykluczanie określonych symboli

Z analizy można wykluczyć określone symbole, takie jak typy i metody. Aby na przykład określić, że reguła nie powinna być uruchamiana w żadnym kodzie w typach o nazwie MyType, dodaj następującą parę klucz-wartość do pliku editorconfig w projekcie:

dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType

Dozwolone formaty nazw symboli w wartości opcji (oddzielone przez |):

  • Tylko nazwa symbolu (zawiera wszystkie symbole o nazwie, niezależnie od typu zawierającego lub przestrzeni nazw).
  • W pełni kwalifikowane nazwy w formacie identyfikatora dokumentacji symbolu. Każda nazwa symboli wymaga prefiksu typu symboli, takiego jak M: metody, T: dla typów i N: przestrzeni nazw.
  • .ctor dla konstruktorów i .cctor konstruktorów statycznych.

Przykłady:

Wartość opcji Podsumowanie
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType Pasuje do wszystkich symboli o nazwie MyType.
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType1|MyType2 Pasuje do wszystkich symboli o nazwie MyType1 lub MyType2.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS.MyType.MyMethod(ParamType) Pasuje do określonej metody MyMethod z określonym w pełni kwalifikowanym podpisem.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS1.MyType1.MyMethod1(ParamType)|M:NS2.MyType2.MyMethod2(ParamType) Pasuje do określonych metod MyMethod1 i MyMethod2 z odpowiednimi w pełni kwalifikowanymi podpisami.

Wykluczanie określonych typów i ich typów pochodnych

Z analizy można wykluczyć określone typy i ich typy pochodne. Aby na przykład określić, że reguła nie powinna być uruchamiana na żadnych metodach w typach nazwanych MyType i ich typach pochodnych, dodaj następującą parę klucz-wartość do pliku .editorconfig w projekcie:

dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType

Dozwolone formaty nazw symboli w wartości opcji (oddzielone przez |):

  • Nazwa typu (zawiera tylko wszystkie typy o nazwie, niezależnie od typu zawierającego lub przestrzeni nazw).
  • W pełni kwalifikowane nazwy w formacie identyfikatora dokumentacji symbolu z opcjonalnym T: prefiksem.

Przykłady:

Wartość opcji Podsumowanie
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType Pasuje do wszystkich typów nazwanych MyType i wszystkich ich typów pochodnych.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType1|MyType2 Dopasuje wszystkie typy o nazwie MyType1 lub MyType2 i wszystkie ich typy pochodne.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS.MyType Pasuje do określonego typu MyType z daną w pełni kwalifikowaną nazwą i wszystkimi jego typami pochodnymi.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS1.MyType1|M:NS2.MyType2 Pasuje do określonych typów MyType1 i MyType2 z odpowiednimi w pełni kwalifikowanymi nazwami i wszystkimi ich typami pochodnymi.

Przykład 1

Jeśli implementujesz metodę zwracającą jednorazowy obiekt, użyj bloku try/finally bez bloku catch, aby upewnić się, że obiekt jest usuwany. Korzystając z bloku try/finally, można zezwolić na wywoływanie wyjątków w punkcie błędu i upewnij się, że obiekt jest usuwany.

W metodzie OpenPort1 wywołanie w celu otwarcia obiektu ISerializable SerialPort lub wywołanie metody SomeMethod może zakończyć się niepowodzeniem. W tej implementacji zostanie zgłoszone ostrzeżenie CA2000.

W metodzie OpenPort2 dwa obiekty SerialPort są zadeklarowane i ustawione na wartość null:

  • tempPort, który służy do testowania, czy operacje metody kończą się powodzeniem.

  • port, który jest używany dla wartości zwracanej metody.

Obiekt tempPort jest skonstruowany i otwarty w try bloku, a każda inna wymagana praca jest wykonywana w tym samym try bloku. Na końcu try bloku otwarty port jest przypisywany do port obiektu, który zostanie zwrócony, a tempPort obiekt ma wartość null.

Blok finally sprawdza wartość tempPort. Jeśli nie ma wartości null, operacja w metodzie nie powiodła się i tempPort jest zamknięta, aby upewnić się, że wszystkie zasoby zostały zwolnione. Zwrócony obiekt portu będzie zawierać otwarty obiekt SerialPort, jeśli operacje metody zakończyły się pomyślnie lub będzie mieć wartość null, jeśli operacja nie powiodła się.

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

Przykład 2

Domyślnie kompilator języka Visual Basic ma wszystkie operatory arytmetyczne sprawdzające przepełnienie. W związku z tym każda operacja arytmetyczna języka Visual Basic może zgłosić OverflowExceptionbłąd . Może to prowadzić do nieoczekiwanych naruszeń reguł, takich jak CA2000. Na przykład następująca funkcja CreateReader1 spowoduje naruszenie CA2000, ponieważ kompilator języka Visual Basic emituje instrukcję sprawdzania przepełnienia dla dodania, która może zgłosić wyjątek, który może spowodować, że element StreamReader nie zostanie usunięty.

Aby rozwiązać ten problem, można wyłączyć emitowanie kontroli przepełnienia przez kompilator języka Visual Basic w projekcie lub zmodyfikować kod, tak jak w poniższej funkcji CreateReader2.

Aby wyłączyć emitowanie kontroli przepełnienia, kliknij prawym przyciskiem myszy nazwę projektu w Eksplorator rozwiązań, a następnie wybierz polecenie Właściwości. Wybierz pozycję Kompiluj zaawansowane opcje kompilowania>, a następnie zaznacz pole Wyboru Usuń przepełnienie liczb całkowitych.

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

Zobacz też