CA2000: Uvolňujte objekty před ztrátou oboru

Vlastnost Hodnota
ID pravidla CA2000
Název Uvolňujte objekty před ztrátou oboru
Kategorie Spolehlivost
Oprava způsobující chybu nebo chybu způsobující chybu Nenarušující
Povoleno ve výchozím nastavení v .NET 8 No

Příčina

Vytvoří se místní objekt typu IDisposable , ale objekt není uvolněn před tím, než budou všechny odkazy na objekt mimo rozsah.

Ve výchozím nastavení toto pravidlo analyzuje celý základ kódu, ale dá se nakonfigurovat.

Popis pravidla

Pokud není uvolnitelný objekt explicitně odstraněn před tím, než jsou všechny odkazy mimo rozsah, bude objekt odstraněn v době, kdy bude systémem uvolňování paměti spuštěna finalizační metoda objektu. Vzhledem k tomu, že může dojít k mimořádné události, která zabrání spuštění finalizační metody objektu, měl by namísto toho být objekt explicitně odstraněn.

Zvláštní případy

Pravidlo CA2000 se neaktivuje pro místní objekty následujících typů, i když objekt není uvolněn:

Předání objektu jednoho z těchto typů konstruktoru a jeho přiřazení k poli označuje převod vlastnictví na nově vytvořený typ. To znamená, že nově vytvořený typ je nyní zodpovědný za likvidaci objektu. Pokud kód předá objekt jednoho z těchto typů konstruktoru, nedojde k žádnému porušení pravidla CA2000, i když objekt není uvolněn dříve, než všechny odkazy na něj nejsou mimo rozsah.

Jak opravit porušení

Chcete-li vyřešit porušení tohoto pravidla, musíte zavolat na objekt metodu Dispose před tím, než jsou všechny odkazy na něj mimo rozsah.

Příkaz (v jazyce Visual Basic) můžete použít using k zabalení objektů, které implementují IDisposable.Using Objekty zabalené tímto způsobem se automaticky odstraní na konci using bloku. Následující situace by ale neměly být zpracovány příkazem using :

  • Chcete-li vrátit uvolnitelný objekt, musí být objekt vytvořen v try/finally bloku mimo using blok.

  • Neicializovat členy uvolnitelného objektu v konstruktoru using příkazu.

  • Pokud jsou konstruktory chráněné pouze jednou obslužnou rutinou výjimky vnořené do části získání using příkazu, selhání ve vnějším konstruktoru může vést k tomu, že objekt vytvořený vnořeným konstruktorem se nikdy nezavře. V následujícím příkladu může selhání v konstruktoru StreamReader způsobit FileStream , že se objekt nikdy nezavře. CA2000 označuje porušení pravidla v tomto případě.

    using (StreamReader sr = new StreamReader(new FileStream("C:/myfile.txt", FileMode.Create)))
    { ... }
    
  • Dynamické objekty by měly používat stínový objekt k implementaci vzoru IDisposable dispose objektů.

Kdy potlačit upozornění

Nepotlačujte upozornění z tohoto pravidla, pokud:

  • Volali jste metodu u objektu, který volá Dispose, například Close.
  • Metoda, která vyvolala upozornění, vrátí IDisposable objekt, který zabalí objekt.
  • Přidělování metody nemá likvidační vlastnictví; to znamená, že odpovědnost za odstranění objektu je přenesena do jiného objektu nebo obálky vytvořené v metodě a vrácena volajícímu.

Potlačení upozornění

Pokud chcete pouze potlačit jedno porušení, přidejte do zdrojového souboru direktivy preprocesoru, abyste pravidlo zakázali a znovu povolili.

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

Pokud chcete pravidlo pro soubor, složku nebo projekt zakázat, nastavte jeho závažnost v none konfiguračním souboru.

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

Další informace naleznete v tématu Jak potlačit upozornění analýzy kódu.

Konfigurace kódu pro analýzu

Pomocí následujících možností můžete nakonfigurovat, pro které části základu kódu se má toto pravidlo spouštět.

Tyto možnosti můžete nakonfigurovat jenom pro toto pravidlo, pro všechna pravidla, která platí, nebo pro všechna pravidla v této kategorii (spolehlivost), na která platí. Další informace naleznete v tématu Možnosti konfigurace pravidla kvality kódu.

Vyloučení konkrétních symbolů

Z analýzy můžete vyloučit konkrétní symboly, jako jsou typy a metody. Pokud chcete například určit, že pravidlo by se nemělo spouštět u žádného kódu v rámci pojmenovaných MyTypetypů, přidejte do souboru .editorconfig v projektu následující dvojici klíč-hodnota:

dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType

Povolené formáty názvů symbolů v hodnotě možnosti (oddělené ):|

  • Pouze název symbolu (zahrnuje všechny symboly s názvem bez ohledu na typ nebo obor názvů).
  • Plně kvalifikované názvy ve formátu ID dokumentace symbolu. Každý název symbolu vyžaduje předponu typu symbolu, například M: pro metody, T: typy a N: obory názvů.
  • .ctor pro konstruktory a .cctor statické konstruktory.

Příklady:

Hodnota možnosti Shrnutí
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType Odpovídá všem symbolům s názvem MyType.
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType1|MyType2 Porovná všechny symboly pojmenované buď MyType1 nebo MyType2.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS.MyType.MyMethod(ParamType) Odpovídá konkrétní metodě MyMethod se zadaným plně kvalifikovaným podpisem.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS1.MyType1.MyMethod1(ParamType)|M:NS2.MyType2.MyMethod2(ParamType) Odpovídá konkrétním metodám MyMethod1 a MyMethod2 příslušným plně kvalifikovaným podpisům.

Vyloučení konkrétních typů a jejich odvozených typů

Z analýzy můžete vyloučit konkrétní typy a jejich odvozené typy. Pokud chcete například určit, že pravidlo by se nemělo spouštět u žádné metody v rámci pojmenovaných MyType typů a jejich odvozených typů, přidejte do souboru .editorconfig v projektu následující dvojici klíč-hodnota:

dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType

Povolené formáty názvů symbolů v hodnotě možnosti (oddělené ):|

  • Pouze název typu (zahrnuje všechny typy s názvem bez ohledu na typ nebo obor názvů).
  • Plně kvalifikované názvy ve formátu ID dokumentace symbolu s volitelnou T: předponou.

Příklady:

Hodnota možnosti Shrnutí
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType Odpovídá všem pojmenovaným MyType typům a všem jejich odvozeným typům.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType1|MyType2 Odpovídá všem typům pojmenovaným buď MyType1 nebo MyType2 a všem jejich odvozeným typům.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS.MyType Odpovídá určitému typu MyType s daným plně kvalifikovaným názvem a všemi jeho odvozenými typy.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS1.MyType1|M:NS2.MyType2 Odpovídá konkrétním typům MyType1 a MyType2 příslušným plně kvalifikovaným názvům a všem jejich odvozeným typům.

Příklad 1

Pokud implementujete metodu, která vrací uvolnitelný objekt, použijte blok try/finally bez bloku catch a ujistěte se, že je objekt uvolněn. Pomocí bloku try/finally lze povolit vyvolání výjimek v místě selhání a zajistit, aby byl objekt uvolněn.

V metodě OpenPort1 se volání za účelem otevření objektu ISerializable SerialPort nebo volání metody SomeMethod nemusí zdařit. V této implementaci je vyvoláno upozornění CA2000.

V metodě OpenPort2 jsou deklarovány dva objekty SerialPort a jsou nastaveny na hodnotu null:

  • tempPort, který se používá k otestování úspěšné operace metody.

  • port, který se používá pro návratové hodnoty metody.

Objekt tempPort je vytvořen a otevřen v rámci bloku try a jakákoli jiná požadovaná činnost je vykonána v rámci stejného bloku try. Na konci bloku try je otevřený port přiřazen objektu port, který bude vrácen, a objekt tempPort je nastaven na hodnotu null.

Blok finally ověřuje hodnotu tempPort. Pokud hodnota není null, operace se v rámci metody nezdařila a blok tempPort je uzavřen, aby bylo možné zajistit uvolnění jakýchkoli prostředků. Vrácený objekt portu bude obsahovat otevřený objekt SerialPort, pokud byly operace metody úspěšné, nebo bude mít hodnotu null, pokud se operace nezdaří.

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

Příklad 2

Ve výchozím nastavení má kompilátor jazyka Visual Basic všechny aritmetické operátory kontrolu přetečení. Proto může jakákoli aritmetická operace jazyka Visual Basic vyvolat výjimku OverflowException. To může vést k neočekávaným případům porušování pravidel, jako je například CA2000. Například následující funkce CreateReader1 ohlásí porušení pravidla CA2000, protože kompilátor jazyka Visual Basic generuje dodatečnou instrukci kontroly přetečení, jež může vyvolat výjimku, která může způsobit, že StreamReader nebude odstraněn.

Chcete-li tento problém vyřešit, můžete zakázat generování kontrol přetečení kompilátorem jazyka Visual Basic v rámci projektu nebo upravit kód tak, jak je tomu v následující funkci CreateReader2.

Chcete-li zakázat generování kontrol přetečení, klikněte pravým tlačítkem myši na název projektu v Průzkumník řešení a pak vyberte Vlastnosti. Vyberte Zkompilovat>pokročilé možnosti kompilace a zaškrtněte políčko Odebrat kontroly přetečení celého čísla.

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

Viz také