Udostępnij za pośrednictwem


CA2000: Usuń obiekty, zanim wyjdą poza zakres

Właściwości Wartość
Identyfikator reguły CA2000
Tytuł Likwiduj obiekty przed utratą zakresu
Kategoria Niezawodność
Poprawka łamiąca lub nienaruszająca Niezgodność
Domyślnie włączone na platformie .NET 10 Nie.
Zastosowane języki C# i Visual Basic

Przyczyna

Lokalny obiekt typu IDisposable jest tworzony, ale nie jest usuwany przed wyjściem wszystkich odwołań do obiektu poza zakres.

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

Opis reguły

Jeśli obiekt do usunięcia nie jest jawnie usuwany, zanim wszystkie odwołania do niego są poza zakresem, obiekt zostanie usunięty w nieokreślonym czasie, gdy moduł odśmiecania 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 na przeniesienie własności zarządzania zasobami 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, nie dochodzi do naruszenia reguły CA2000, nawet jeśli obiekt nie zostanie zlikwidowany, zanim wszystkie odwołania do niego opuszczą zakres.

Jak naprawić naruszenia

Aby naprawić naruszenie tej reguły, wywołaj Dispose na obiekcie, zanim wszystkie odwołania do niego znajdą się 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 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 podlegającego usuwaniu w konstruktorze instrukcji using.

  • Gdy konstruktory, chronione tylko przez jedną procedurę obsługi wyjątków, są zagnieżdżone w części przejmowania instrukcji using, awaria w konstruktorze zewnętrznym może spowodować, że obiekt utworzony przez zagnieżdżony konstruktor nigdy nie zostanie zamknięty. W poniższym przykładzie błąd w konstruktorze StreamReader może powodować, że obiekt FileStream 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łałeś metodę na swoim obiekcie, która wywołuje Dispose metodę, taką jak Close.
  • Metoda, która zgłosiła ostrzeżenie, zwraca IDisposable obiekt, który obejmuje Twój obiekt.
  • Metoda przydzielania nie ma odpowiedzialności za usuwanie; oznacza to, że odpowiedzialność za usunięcie obiektu jest przenoszona na inny obiekt lub opakowanie utworzone w tej metodzie, które jest następnie przekazywane 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ć analizowane przez tę regułę oraz kiedy zarządzanie zasobami ma zostać przeniesione.

Ponadto do tej reguły mają zastosowanie następujące inne opcje związane z analizą przepływu danych:

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

Wyklucz określone symbole

Z analizy można wykluczyć określone symbole, takie jak typy i metody, ustawiając opcję excluded_symbol_names. 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

Notatka

Zastąp część XXXXCAXXXX identyfikatorem odpowiedniej reguły.

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 symbolu wymaga prefiksu określającego rodzaj symbolu, takiego jak M: dla metody, T: dla typów i N: dla przestrzeni nazw.
  • .ctor dla konstruktorów i .cctor dla 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) Dopasowuje określoną metodę MyMethod do określonej, w pełni kwalifikowanej sygnatury.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS1.MyType1.MyMethod1(ParamType)|M:NS2.MyType2.MyMethod2(ParamType) Dopasowuje określone metody MyMethod1 i MyMethod2 z odpowiednimi w pełni kwalifikowanymi sygnaturami.

Wyklucz określone typy i ich pochodne typy

Określone typy i ich typy pochodne można wykluczyć z analizy, ustawiając opcję excluded_type_names_with_derived_types. 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

Notatka

Zastąp część XXXXCAXXXX identyfikatorem odpowiedniej reguły.

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

  • Podaj tylko nazwę typu (obejmuje wszystkie typy o tej nazwie, bez względu na typ zawierający lub przestrzeń nazw).
  • W pełni kwalifikowane nazwy w formacie dokumentacyjnego identyfikatora symbolu, z opcjonalnym prefiksem T:.

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 Dopasowuje określony typ MyType do danej w pełni kwalifikowanej nazwy i do wszystkich jego pochodnych typów.
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.

Konfigurowanie przenoszenia własności zarządzania

Opcje dispose_ownership_transfer_at_constructor i dispose_ownership_transfer_at_method_call konfigurują przeniesienie własności zasobu.

Aby na przykład określić, że reguła przenosi własność argumentów przekazanych do konstruktorów, dodaj następującą parę klucz-wartość do pliku .editorconfig w projekcie:

dotnet_code_quality.CAXXXX.dispose_ownership_transfer_at_constructor = true

Notatka

Zastąp część XXXXCAXXXX identyfikatorem odpowiedniej reguły.

dispose_ownership_transfer_at_constructor

Rozważmy poniższy przykład kodu.

class A : IDisposable
{
    public void Dispose() { }
}

class Test
{
    DisposableOwnerType M1()
    {
        return new DisposableOwnerType(new A());
    }
}
  • Jeśli dotnet_code_quality.dispose_ownership_transfer_at_constructor jest ustawiona na true, własność usuwania dla alokacji new A() zostanie przeniesiona do zwróconego wystąpienia DisposableOwnerType.
  • Jeśli dotnet_code_quality.dispose_ownership_transfer_at_constructor jest ustawiony na false, Test.M1() ma dispose'owanie dla new A() i powoduje naruszenie zasady CA2000 dotyczącej wycieku.

dispose_transfer_własności_przy_wywołaniu_metody

Rozważmy poniższy przykład kodu.

class Test
{
    void M1()
    {
        TransferDisposeOwnership(new A());
    }
}
  • Jeśli dotnet_code_quality.dispose_ownership_transfer_at_method_call jest ustawione na true, przeniesienie własności usuwania dla alokacji new A() następuje do metody TransferDisposeOwnership.
  • Jeśli dotnet_code_quality.dispose_ownership_transfer_at_method_call jest ustawiona na false, Test.M1() ma odpowiedzialność za usuwanie dla new A(), i powoduje naruszenie związane z CA2000 wyciekiem.

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 tworzony i otwierany w try bloku, a wszystkie inne wymagane operacje są wykonywane 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 Eksploratorze Rozwiązań, a następnie wybierz polecenie Właściwości. Wybierz Kompiluj>Opcje zaawansowanej kompilacji, a następnie zaznacz Usuń sprawdzanie przepełnienia 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ż