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:
- System.IO.Stream
- System.IO.StringReader
- System.IO.TextReader
- System.IO.TextWriter
- System.Resources.IResourceReader
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 mimousing
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 MyType
typů, 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 aN:
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. |
Související pravidla
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