CA2000: Kapsamı kaybetmeden önce verileri atın
Özellik | Değer |
---|---|
Kural Kimliği | CA2000 |
Başlık | Kapsamı kaybetmeden önce nesneleri bırakın |
Kategori | Güvenilirlik |
Hataya neden olan veya bozulmayan düzeltme | Hataya neden olmayan |
.NET 9'da varsayılan olarak etkin | Hayır |
Neden
Bir türün IDisposable yerel nesnesi oluşturulur, ancak nesneye yapılan tüm başvurular kapsam dışı bırakılmadan önce nesne atılmaz.
Varsayılan olarak, bu kural tüm kod tabanını analiz eder, ancak bu yapılandırılabilir.
Kural açıklaması
Atılabilir bir nesnenin tüm başvuruları kapsam dışında kalmadan önce açıkça atılmazsa, atık toplayıcı nesnenin sonlandırıcısını çalıştırdığında nesne belirsiz bir zamanda atılır. Nesnenin sonlandırıcısının çalışmasını engelleyecek olağanüstü bir olay meydana gelebileceğinden, nesne açıkça atılmalıdır.
Özel durumlar
Kural CA2000, nesne atılmasa bile aşağıdaki türlerdeki yerel nesneler için tetiklenmez:
- System.IO.Stream
- System.IO.StringReader
- System.IO.TextReader
- System.IO.TextWriter
- System.Resources.IResourceReader
Bu türlerden birinin nesnesini bir oluşturucuya geçirip bir alana atamak, yeni oluşturulan türe bir atma sahiplik aktarımını gösterir. Yani, yeni yapılan tür artık nesnenin atılmasından sorumludur. Kodunuz bu türlerden birinin nesnesini bir oluşturucuya geçirirse, nesneye yapılan tüm başvurular kapsam dışı bırakılmadan önce atılmasa bile kural CA2000 ihlali gerçekleşmez.
İhlalleri düzeltme
Bu kuralın ihlalini düzeltmek için, nesneye yapılan tüm başvurular kapsam dışında olmadan önce nesneyi çağırın Dispose .
deyimini using
(Using
Visual Basic'te) kullanarak uygulayan IDisposablenesneleri sarmalayabilirsiniz. Bu şekilde sarmalanan nesneler bloğun using
sonunda otomatik olarak atılır. Ancak, aşağıdaki durumlar bir using
deyimle işlenmemelidir veya işlenemeyecektir:
Atılabilir bir nesne döndürmek için, nesne bir bloğun
try/finally
dışındaki birusing
blokta oluşturulmalıdır.Bir deyiminin oluşturucusunda atılabilir bir nesnenin üyelerini
using
başlatmayın.Yalnızca bir özel durum işleyicisi tarafından korunan oluşturucular bir
using
deyimin alma bölümünde iç içe geçirildiğinde, dış oluşturucuda bir hata iç içe oluşturucu tarafından oluşturulan nesnenin hiçbir zaman kapatılmamasına neden olabilir. Aşağıdaki örnekte, oluşturucudaki StreamReader bir hata nesnenin FileStream hiçbir zaman kapatılmamasına neden olabilir. CA2000, bu durumda kuralın ihlalini işaret eder.using (StreamReader sr = new StreamReader(new FileStream("C:/myfile.txt", FileMode.Create))) { ... }
Dinamik nesneler, nesnelerin atma desenini IDisposable uygulamak için bir gölge nesne kullanmalıdır.
Uyarıların ne zaman bastırılması gerekiyor?
Aşağıdakiler dışında bu kuraldan bir uyarıyı gizlemeyin:
- Nesnenizde gibi öğesini çağıran
Dispose
Closebir yöntem çağırdınız. - Uyarıyı tetikleyen yöntem, nesnenizi sarmalayan bir IDisposable nesne döndürür.
- Ayırma yönteminin dispose sahipliği yoktur; diğer bir ifadeyle, nesneyi atma sorumluluğu yönteminde oluşturulan ve çağırana döndürülen başka bir nesneye veya sarmalayıcıya aktarılır.
Uyarıyı gizleme
Yalnızca tek bir ihlali engellemek istiyorsanız, kuralı devre dışı bırakmak ve sonra yeniden etkinleştirmek için kaynak dosyanıza ön işlemci yönergeleri ekleyin.
#pragma warning disable CA2000
// The code that's violating the rule is on this line.
#pragma warning restore CA2000
Bir dosya, klasör veya projenin kuralını devre dışı bırakmak için, yapılandırma dosyasındaki önem derecesini none
olarak ayarlayın.
[*.{cs,vb}]
dotnet_diagnostic.CA2000.severity = none
Daha fazla bilgi için bkz . Kod analizi uyarılarını gizleme.
Çözümlemek için kod yapılandırma
Bu kuralın kod tabanınızın hangi bölümlerinde çalıştırılacağı ve atma sahipliğinin ne zaman aktarılacağı yapılandırmak için aşağıdaki seçenekleri kullanın.
- Belirli simgeleri hariç tutma
- Belirli türleri ve türetilmiş türlerini dışlama
- Dispose sahiplik aktarımını yapılandırma
Ayrıca, bu kural için aşağıdaki diğer veri akışı analiziyle ilgili seçenekler geçerlidir:
- dispose_analysis_kind
- interprocedural_analysis_kind
- max_interprocedural_lambda_or_local_function_call_chain
- max_interprocedural_method_call_chain
- points_to_analysis_kind
- copy_analysis
- sufficient_IterationCount_for_weak_KDF_algorithm
Bu seçenekleri yalnızca bu kural, uyguladıkları tüm kurallar veya bu kategorideki tüm kurallar için (Güvenilirlik) yapılandırabilirsiniz. Daha fazla bilgi için bkz . Kod kalitesi kuralı yapılandırma seçenekleri.
Belirli simgeleri hariç tutma
excluded_symbol_names seçeneğini ayarlayarak türler ve yöntemler gibi belirli simgeleri analizden hariç tutabilirsiniz. Örneğin, kuralın adlı MyType
türlerdeki herhangi bir kodda çalışmaması gerektiğini belirtmek için, projenizdeki bir .editorconfig dosyasına aşağıdaki anahtar-değer çiftini ekleyin:
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType
Not
XXXX
CAXXXX
bölümünü geçerli kuralın kimliğiyle değiştirin.
Seçenek değerinde izin verilen simge adı biçimleri (ile |
ayrılmış):
- Yalnızca sembol adı (içeren tür veya ad alanı ne olursa olsun, ada sahip tüm simgeleri içerir).
- Simgenin belge kimliği biçimindeki tam adlar. Her simge adı için yöntemler
M:
, türler veT:
ad alanları gibiN:
bir sembol türü ön eki gerekir. -
.ctor
oluşturucular ve.cctor
statik oluşturucular için.
Örnekler:
Seçenek Değeri | Özet |
---|---|
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType |
adlı MyType tüm simgelerle eşleşir. |
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType1|MyType2 |
veya MyType1 adlı MyType2 tüm simgelerle eşleşir. |
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS.MyType.MyMethod(ParamType) |
Belirtilen tam imza ile belirli bir yöntemi MyMethod eşleştirir. |
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS1.MyType1.MyMethod1(ParamType)|M:NS2.MyType2.MyMethod2(ParamType) |
Belirli yöntemlerle MyMethod1 ve MyMethod2 ilgili tam imzalarla eşleşir. |
Belirli türleri ve türetilmiş türlerini dışlama
excluded_type_names_with_derived_types seçeneğini ayarlayarak belirli türleri ve türetilmiş türlerini analizden dışlayabilirsiniz. Örneğin, kuralın adlı MyType
ve türetilmiş türleri içindeki hiçbir yöntemde çalışmaması gerektiğini belirtmek için, projenizdeki bir .editorconfig dosyasına aşağıdaki anahtar-değer çiftini ekleyin:
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType
Not
XXXX
CAXXXX
bölümünü geçerli kuralın kimliğiyle değiştirin.
Seçenek değerinde izin verilen simge adı biçimleri (ile |
ayrılmış):
- Yalnızca tür adı (içeren tür veya ad alanına bakılmaksızın adı olan tüm türleri içerir).
- Simgenin belge kimliği biçiminde, isteğe bağlı
T:
ön ek içeren tam adlar.
Örnekler:
Seçenek değeri | Özet |
---|---|
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType |
Adlı MyType tüm türleri ve türetilmiş türlerinin tümünü eşleştirir. |
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType1|MyType2 |
veya MyType1 adlı MyType2 tüm türleri ve türetilmiş türlerinin tümünü eşleştirir. |
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS.MyType |
Belirli bir türü MyType verilen tam adla ve türetilmiş tüm türleriyle eşleştirir. |
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS1.MyType1|M:NS2.MyType2 |
Belirli türleri MyType1 ve MyType2 ilgili tam adlarla ve bunların türetilmiş tüm türleriyle eşleşir. |
Dispose sahiplik aktarımını yapılandırma
dispose_ownership_transfer_at_constructor ve dispose_ownership_transfer_at_method_call seçenekleri, dispose sahipliğinin aktarımını yapılandırılır.
Örneğin, kuralın oluşturuculara geçirilen bağımsız değişkenlerin dispose sahipliğini aktardığını belirtmek için projenizdeki bir .editorconfig dosyasına aşağıdaki anahtar-değer çiftini ekleyin:
dotnet_code_quality.CAXXXX.dispose_ownership_transfer_at_constructor = true
Not
XXXX
CAXXXX
bölümünü geçerli kuralın kimliğiyle değiştirin.
dispose_ownership_transfer_at_constructor
Aşağıdaki kod örneğini göz önünde bulundurun.
class A : IDisposable
{
public void Dispose() { }
}
class Test
{
DisposableOwnerType M1()
{
return new DisposableOwnerType(new A());
}
}
-
dotnet_code_quality.dispose_ownership_transfer_at_constructor
true
olarak ayarlanırsa,new A()
ayırmanın sahipliğini atın, döndürülenDisposableOwnerType
örneğine aktarılır. -
dotnet_code_quality.dispose_ownership_transfer_at_constructor
false
olarak ayarlanırsaTest.M1()
new A()
için atma sahipliğini alır ve atma sızıntısı içinCA2000
ihlaline neden olur.
dispose_ownership_transfer_at_method_call
Aşağıdaki kod örneğini göz önünde bulundurun.
class Test
{
void M1()
{
TransferDisposeOwnership(new A());
}
}
-
dotnet_code_quality.dispose_ownership_transfer_at_method_call
true
olarak ayarlanırsa,new A()
ayırmanın sahipliğini atTransferDisposeOwnership
yöntemine aktarılır. -
dotnet_code_quality.dispose_ownership_transfer_at_method_call
false
olarak ayarlanırsaTest.M1()
new A()
için atma sahipliğini alır ve atma sızıntısı içinCA2000
ihlaline neden olur.
İlgili kurallar
Örnek 1
Tek kullanımlık bir nesne döndüren bir yöntem uyguluyorsanız, nesnenin atıldığından emin olmak için catch bloğu olmayan bir try/finally bloğu kullanın. Try/finally bloğu kullanarak hata noktasında özel durumların tetiklenmesine izin verir ve nesnenin atıldığından emin olursunuz.
OpenPort1 yönteminde, ISerializable nesnesi SerialPort'u açma çağrısı veya SomeMethod çağrısı başarısız olabilir. Bu uygulama üzerinde bir CA2000 uyarısı oluşturulur.
OpenPort2 yönteminde iki SerialPort nesnesi bildirilir ve null olarak ayarlanır:
tempPort
, yöntem işlemlerinin başarılı olup olmadığını test etmek için kullanılır.port
, yönteminin dönüş değeri için kullanılır.
tempPort
bir blokta try
oluşturulur ve açılır ve gerekli diğer tüm işler aynı try
blokta gerçekleştirilir. Bloğun try
sonunda, açılan bağlantı noktası döndürülecek nesneye port
atanır ve tempPort
nesnesi olarak null
ayarlanır.
blok değerini finally
denetler tempPort
. Null değilse, yöntemdeki bir işlem başarısız olmuştur ve tempPort
tüm kaynakların serbest bırakıldığından emin olmak için kapatılır. Döndürülen bağlantı noktası nesnesi, yöntemin işlemleri başarılı olursa açık SerialPort nesnesini içerir veya bir işlem başarısız olursa null olur.
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
Örnek 2
Varsayılan olarak, Visual Basic derleyicisi tüm aritmetik işleçlerin taşma denetimine sahiptir. Bu nedenle, herhangi bir Visual Basic aritmetik işlemi bir OverflowExceptionatabilir. Bu durum CA2000 gibi kurallarda beklenmeyen ihlallere yol açabilir. Örneğin, aşağıdaki CreateReader1 işlevi bir CA2000 ihlali oluşturur çünkü Visual Basic derleyicisi, StreamReader'ın atılmamasına neden olabilecek bir özel durum oluşturabilecek ekleme için bir taşma denetimi yönergesi yayar.
Bunu düzeltmek için, projenizdeki Visual Basic derleyicisi tarafından taşma denetimlerinin yayma özelliğini devre dışı bırakabilir veya kodunuzu aşağıdaki CreateReader2 işlevinde olduğu gibi değiştirebilirsiniz.
Taşma denetimlerinin yayma özelliğini devre dışı bırakmak için, Çözüm Gezgini proje adına sağ tıklayın ve özellikler'i seçin. Gelişmiş Derleme Seçeneklerini Derle'yi>seçin ve ardından Tamsayı taşma denetimlerini kaldır'ı işaretleyin.
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