Özel durumlar için en iyi yöntemler
Uygun özel durum işleme, uygulama güvenilirliği için gereklidir. Uygulamanızın kilitlenmesini önlemek için beklenen özel durumları kasıtlı olarak işleyebilirsiniz. Ancak, kilitlenen bir uygulama tanımsız davranışa sahip bir uygulamadan daha güvenilir ve tanınabilir.
Bu makalede özel durumları işlemeye ve oluşturmaya yönelik en iyi yöntemler açıklanmaktadır.
Özel durum işleme
Aşağıdaki en iyi yöntemler, özel durumları nasıl işlediğinizle ilgilidir:
- Hatalardan veya yayın kaynaklarından kurtarmak için try/catch/finally bloklarını kullanma
- Özel durumları önlemek için yaygın koşulları işleme
- İptal ve zaman uyumsuz özel durumları yakalama
- Özel durumların önlenebilmesi için sınıfları tasarlama
- Özel durumlar nedeniyle yöntemler tamamlanmadığında geri yükleme durumu
- Özel durumları düzgün bir şekilde yakalama ve yeniden düzeltme
Hatalardan veya yayın kaynaklarından kurtarmak için try/catch/finally bloklarını kullanma
Bir özel durum oluşturabilecek kodlar ve uygulamanız bu özel durumdan kurtarabildiğinde kodun çevresindeki blokları kullanın try
/catch
. Bloklarda catch
, her zaman en çok türetilenden en az türetilene kadar özel durumları sıralar. (Tüm özel durumlar sınıfından Exception türetilir. Daha fazla türetilmiş özel durum, temel özel durum sınıfı için bir catch
yan tümceden önce gelen bir catch
yan tümce tarafından işlenmez.) Kodunuz bir özel durumdan kurtarılamıyorsa bu özel durumu yakalamayın. Mümkünse kurtarmak için çağrı yığınında daha fazla yöntem etkinleştirin.
Deyimler veya finally
bloklarla using
ayrılan kaynakları temizleyin. Özel durumlar oluştuğunda kaynakları otomatik olarak temizlemek için deyimleri tercih edin using
. uygulamayan IDisposablekaynakları temizlemek için blokları kullanınfinally
. Bir finally
yan tümcedeki kod, özel durumlar oluştuğunda bile neredeyse her zaman yürütülür.
Özel durumları önlemek için yaygın koşulları işleme
Olası ancak bir özel durum tetikleyebilecek koşullar için, bunları özel durumdan kaçınacak şekilde işlemeyi göz önünde bulundurun. Örneğin, zaten kapalı olan bir bağlantıyı kapatmaya çalışırsanız bir InvalidOperationException
alırsınız. Kapatmaya çalışmadan önce bağlantı durumunu denetlemek için bir if
deyim kullanarak bunu önleyebilirsiniz.
if (conn.State != ConnectionState.Closed)
{
conn.Close();
}
If conn.State <> ConnectionState.Closed Then
conn.Close()
End IF
Kapatmadan önce bağlantı durumunu denetlemezseniz, özel durumu yakalayabilirsiniz InvalidOperationException
.
try
{
conn.Close();
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.GetType().FullName);
Console.WriteLine(ex.Message);
}
Try
conn.Close()
Catch ex As InvalidOperationException
Console.WriteLine(ex.GetType().FullName)
Console.WriteLine(ex.Message)
End Try
Seçme yaklaşımı, olayın ne sıklıkta gerçekleşmesini beklediğinize bağlıdır.
Olay sık gerçekleşmiyorsa, yani olay gerçekten olağanüstüyse ve beklenmeyen bir dosya sonu gibi bir hata gösteriyorsa özel durum işlemeyi kullanın. Özel durum işleme kullandığınızda, normal koşullarda daha az kod çalıştırılır.
Olay düzenli olarak gerçekleşirse ve normal yürütmenin bir parçası olarak kabul edilebilirse koddaki hata koşullarını denetleyin. Yaygın hata koşullarını denetlediğinizde, özel durumlardan kaçındığınızdan daha az kod yürütülür.
Not
Ön denetimler çoğu zaman özel durumları ortadan kaldırır. Ancak denetim ile işlem arasında korunan koşulun değiştiği yarış koşulları olabilir ve bu durumda yine de bir özel durumla karşınıza çıkabilir.
Özel durumları önlemek için yöntemleri çağırma Try*
Özel durumların performans maliyeti engelleyiciyse, bazı .NET kitaplık yöntemleri alternatif hata işleme biçimleri sağlar. Örneğin, Int32.Parse ayrıştırılacak değer tarafından Int32temsil edilemeyecek kadar büyükse bir OverflowException oluşturur. Ancak, Int32.TryParse bu özel durum oluşturmaz. Bunun yerine bir Boole döndürür ve başarılı olduğunda ayrıştırılmış geçerli tamsayıyı içeren bir out
parametresi vardır. Dictionary<TKey,TValue>.TryGetValue bir sözlükten değer almaya çalışırken benzer davranışlar gösterir.
İptal ve zaman uyumsuz özel durumları yakalama
Zaman uyumsuz bir yöntem çağırdığınızda , yerine TaskCanceledExceptionöğesinin OperationCanceledException türetilmiş OperationCanceledException
olması daha iyidir. Birçok zaman uyumsuz yöntem, iptal istenirse bir OperationCanceledException özel durum oluşturur. Bu özel durumlar, bir iptal isteği gözlemlendikten sonra yürütmenin verimli bir şekilde durdurulmasını ve çağrı yığınının kaldırılabilmesini sağlar.
Zaman uyumsuz yöntemler, döndürdikleri görevde yürütme sırasında oluşturulan özel durumları depolar. Döndürülen görevde bir özel durum depolanırsa, görev beklendiğinde bu özel durum oluşturulur. gibi ArgumentExceptionkullanım özel durumları hala zaman uyumlu olarak oluşturulur. Daha fazla bilgi için bkz . Zaman uyumsuz özel durumlar.
Özel durumların önlenebilmesi için sınıfları tasarlama
Bir sınıf, özel durum tetikleyebilecek bir çağrı yapmaktan kaçınmanızı sağlayan yöntemler veya özellikler sağlayabilir. Örneğin, sınıfı dosyanın FileStream sonuna ulaşılıp ulaşılmadığını belirlemeye yardımcı olan yöntemler sağlar. Dosyanın sonunu okursanız oluşan özel durumdan kaçınmak için bu yöntemleri çağırabilirsiniz. Aşağıdaki örnekte, özel durum tetiklemeden dosyanın sonuna kadar okuma işlemi gösterilmektedir:
class FileRead
{
public static void ReadAll(FileStream fileToRead)
{
ArgumentNullException.ThrowIfNull(fileToRead);
int b;
// Set the stream position to the beginning of the file.
fileToRead.Seek(0, SeekOrigin.Begin);
// Read each byte to the end of the file.
for (int i = 0; i < fileToRead.Length; i++)
{
b = fileToRead.ReadByte();
Console.Write(b.ToString());
// Or do something else with the byte.
}
}
}
Class FileRead
Public Sub ReadAll(fileToRead As FileStream)
' This if statement is optional
' as it is very unlikely that
' the stream would ever be null.
If fileToRead Is Nothing Then
Throw New System.ArgumentNullException()
End If
Dim b As Integer
' Set the stream position to the beginning of the file.
fileToRead.Seek(0, SeekOrigin.Begin)
' Read each byte to the end of the file.
For i As Integer = 0 To fileToRead.Length - 1
b = fileToRead.ReadByte()
Console.Write(b.ToString())
' Or do something else with the byte.
Next i
End Sub
End Class
Özel durumlardan kaçınmanın bir diğer yolu da, özel durum oluşturma yerine en yaygın hata durumlarına (veya varsayılan) döndürmektir null
. Yaygın bir hata durumu, normal bir denetim akışı olarak kabul edilebilir. Bu durumlarda döndürerek null
(veya varsayılan olarak), bir uygulamaya yönelik performans etkisini en aza indirirsiniz.
Değer türleri için, uygulamanızın hata göstergesi olarak mı default
kullanacağınızı Nullable<T>
göz önünde bulundurun. kullanılarak Nullable<Guid>
default
yerine Guid.Empty
olurnull
. Bazen, bir değer mevcut olduğunda veya olmadığında ekleme Nullable<T>
işlemi daha net hale gelebilir. Diğer durumlarda, ekleme Nullable<T>
işlemi gerekli olmayan ek servis talepleri oluşturabilir ve yalnızca olası hata kaynaklarını oluşturmaya hizmet edebilir.
Özel durumlar nedeniyle yöntemler tamamlanmadığında geri yükleme durumu
Bir yöntemi çağıranlar, bu yöntemde özel durum oluşturulduğunda bunun yan etkileri olmayacağını varsayabilmelidir. Örneğin, bir hesaptan para çekerek ve başka bir hesaba para yatırarak para aktaran kodunuz varsa ve depozito yürütülürken bir özel durum oluşursa, çekme işleminin geçerli kalmasını istemezsiniz.
public void TransferFunds(Account from, Account to, decimal amount)
{
from.Withdrawal(amount);
// If the deposit fails, the withdrawal shouldn't remain in effect.
to.Deposit(amount);
}
Public Sub TransferFunds(from As Account, [to] As Account, amount As Decimal)
from.Withdrawal(amount)
' If the deposit fails, the withdrawal shouldn't remain in effect.
[to].Deposit(amount)
End Sub
Yukarıdaki yöntem doğrudan herhangi bir özel durum oluşturmaz. Ancak, para yatırma işlemi başarısız olursa para çekme işleminin tersine çevrilmesi için yöntemini yazmanız gerekir.
Bu durumu ele almanın bir yolu, depozito işlemi tarafından atılan özel durumları yakalamak ve geri çekme işlemini geri almaktır.
private static void TransferFunds(Account from, Account to, decimal amount)
{
string withdrawalTrxID = from.Withdrawal(amount);
try
{
to.Deposit(amount);
}
catch
{
from.RollbackTransaction(withdrawalTrxID);
throw;
}
}
Private Shared Sub TransferFunds(from As Account, [to] As Account, amount As Decimal)
Dim withdrawalTrxID As String = from.Withdrawal(amount)
Try
[to].Deposit(amount)
Catch
from.RollbackTransaction(withdrawalTrxID)
Throw
End Try
End Sub
Bu örnekte, öğesinin özgün özel durumu yeniden oluşturmak için kullanılması throw
gösterilmektedir ve bu da çağıranların özelliği incelemek zorunda kalmadan sorunun gerçek nedenini görmelerini InnerException kolaylaştırır. Alternatif olarak yeni bir özel durum oluşturup özgün özel durumu iç özel durum olarak dahil edin.
catch (Exception ex)
{
from.RollbackTransaction(withdrawalTrxID);
throw new TransferFundsException("Withdrawal failed.", innerException: ex)
{
From = from,
To = to,
Amount = amount
};
}
Catch ex As Exception
from.RollbackTransaction(withdrawalTrxID)
Throw New TransferFundsException("Withdrawal failed.", innerException:=ex) With
{
.From = from,
.[To] = [to],
.Amount = amount
}
End Try
Özel durumları düzgün bir şekilde yakalama ve yeniden düzeltme
Özel durum oluşturulduktan sonra, taşıdığı bilgilerin bir bölümü yığın izlemesi olur. Yığın izlemesi, özel durumu oluşturan yöntemiyle başlayan ve özel durumu yakalayan yöntemle biten yöntem çağrısı hiyerarşisinin listesidir. Deyiminde throw
özel durumu belirterek bir özel durumu yeniden oluşturursanız, örneğin, throw e
yığın izlemesi geçerli yöntemde yeniden başlatılır ve özel durumu oluşturan özgün yöntem ile geçerli yöntem arasındaki yöntem çağrıları listesi kaybolur. Özgün yığın izleme bilgilerini özel durumla birlikte tutmak için, özel durumu nereden yeniden oluşturduğunuza bağlı olarak iki seçenek vardır:
- Özel durum örneğini yakalayan işleyici (
catch
blok) içinden özel durumu yeniden oluşturursanız, özel durumu belirtmeden deyimini kullanınthrow
. Kod çözümleme kuralı CA2200 , kodunuzda yanlışlıkla yığın izleme bilgilerini kaybedebileceğiniz yerler bulmanıza yardımcı olur. - Özel durumu işleyici (
catch
blok) dışında bir yerden yeniden oluşturacaksanız, işleyicide özel durumu yakalamak için ve ExceptionDispatchInfo.Throw() yeniden kullanmak istediğinizde kullanınExceptionDispatchInfo.Capture(Exception). Yakalanan özel durumu incelemek için özelliğini kullanabilirsiniz ExceptionDispatchInfo.SourceException .
Aşağıdaki örnekte sınıfın ExceptionDispatchInfo nasıl kullanılabileceğini ve çıkışın nasıl görünebileceği gösterilmektedir.
ExceptionDispatchInfo? edi = null;
try
{
var txt = File.ReadAllText(@"C:\temp\file.txt");
}
catch (FileNotFoundException e)
{
edi = ExceptionDispatchInfo.Capture(e);
}
// ...
Console.WriteLine("I was here.");
if (edi is not null)
edi.Throw();
Örnek koddaki dosya yoksa aşağıdaki çıkış oluşturulur:
I was here.
Unhandled exception. System.IO.FileNotFoundException: Could not find file 'C:\temp\file.txt'.
File name: 'C:\temp\file.txt'
at Microsoft.Win32.SafeHandles.SafeFileHandle.CreateFile(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options)
at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
at System.IO.Strategies.OSFileStreamStrategy..ctor(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
at System.IO.Strategies.FileStreamHelpers.ChooseStrategyCore(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
at System.IO.StreamReader.ValidateArgsAndOpenPath(String path, Encoding encoding, Int32 bufferSize)
at System.IO.File.ReadAllText(String path, Encoding encoding)
at Example.ProcessFile.Main() in C:\repos\ConsoleApp1\Program.cs:line 12
--- End of stack trace from previous location ---
at Example.ProcessFile.Main() in C:\repos\ConsoleApp1\Program.cs:line 24
Özel durumlar oluşturma
Aşağıdaki en iyi yöntemler özel durumları nasıl oluşturabileceğinizle ilgilidir:
- Önceden tanımlanmış özel durum türlerini kullanma
- Özel durum oluşturucu yöntemlerini kullanma
- Yerelleştirilmiş dize iletisi ekleme
- Uygun dil bilgisini kullanma
- Throw deyimlerini iyi yerleştir
- finally yan tümcelerinde özel durum oluşturmayın
- Beklenmeyen yerlerden özel durum oluşturmayın
- Bağımsız değişken doğrulama özel durumlarını zaman uyumlu olarak oluşturma
Önceden tanımlanmış özel durum türlerini kullanma
Yeni bir özel durum sınıfını yalnızca önceden tanımlanmış bir sınıf geçerli olmadığında tanıtın. Örneğin:
- Nesnenin geçerli durumu göz önüne alındığında bir özellik kümesi veya yöntem çağrısı uygun değilse, bir InvalidOperationException özel durum oluşturun.
- Geçersiz parametreler geçirilirse, bir ArgumentException özel durum veya 'den türetilen önceden tanımlanmış sınıflardan ArgumentExceptionbirini atın.
Not
Mümkün olduğunda önceden tanımlanmış özel durum türlerini kullanmak en iyisi olsa da, , NullReferenceExceptionIndexOutOfRangeExceptionve StackOverflowExceptiongibi AccessViolationExceptionbazı ayrılmış özel durum türlerini tetiklemelisiniz. Daha fazla bilgi için bkz . CA2201: Ayrılmış özel durum türlerini yükseltme.
Özel durum oluşturucu yöntemlerini kullanma
Bir sınıfın, uygulamasında farklı yerlerden aynı özel durumu oluşturması yaygındır. Aşırı kodu önlemek için, özel durumu oluşturan ve döndüren bir yardımcı yöntem oluşturun. Örneğin:
class FileReader
{
private readonly string _fileName;
public FileReader(string path)
{
_fileName = path;
}
public byte[] Read(int bytes)
{
byte[] results = FileUtils.ReadFromFile(_fileName, bytes) ?? throw NewFileIOException();
return results;
}
static FileReaderException NewFileIOException()
{
string description = "My NewFileIOException Description";
return new FileReaderException(description);
}
}
Class FileReader
Private fileName As String
Public Sub New(path As String)
fileName = path
End Sub
Public Function Read(bytes As Integer) As Byte()
Dim results() As Byte = FileUtils.ReadFromFile(fileName, bytes)
If results Is Nothing
Throw NewFileIOException()
End If
Return results
End Function
Function NewFileIOException() As FileReaderException
Dim description As String = "My NewFileIOException Description"
Return New FileReaderException(description)
End Function
End Class
Bazı anahtar .NET özel durum türleri, özel durumu ayıran ve oluşturan bu tür statik throw
yardımcı yöntemlerine sahiptir. İlgili özel durum türünü oluşturmak ve oluşturmak yerine bu yöntemleri çağırmalısınız:
- ArgumentNullException.ThrowIfNull
- ArgumentException.ThrowIfNullOrEmpty(String, String)
- ArgumentException.ThrowIfNullOrWhiteSpace(String, String)
- ArgumentOutOfRangeException.ThrowIfZero<T>(T, String)
- ArgumentOutOfRangeException.ThrowIfNegative<T>(T, String)
- ArgumentOutOfRangeException.ThrowIfEqual<T>(T, T, String)
- ArgumentOutOfRangeException.ThrowIfLessThan<T>(T, T, String)
- ArgumentOutOfRangeException.ThrowIfNotEqual<T>(T, T, String)
- ArgumentOutOfRangeException.ThrowIfNegativeOrZero<T>(T, String)
- ArgumentOutOfRangeException.ThrowIfGreaterThan<T>(T, T, String)
- ArgumentOutOfRangeException.ThrowIfLessThanOrEqual<T>(T, T, String)
- ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual<T>(T, T, String)
- ObjectDisposedException.ThrowIf
İpucu
Aşağıdaki kod çözümleme kuralları, kodunuzda şu statik throw
yardımcılardan yararlanabileceğiniz yerler bulmanıza yardımcı olabilir: CA1510, CA1511, CA1512 ve CA1513.
Zaman uyumsuz bir yöntem uyguluyorsanız, iptalin istenip istenmediğini denetlemek yerine çağrısı CancellationToken.ThrowIfCancellationRequested() yapın ve sonra oluşturup oluşturun OperationCanceledException. Daha fazla bilgi için bkz . CA2250.
Yerelleştirilmiş dize iletisi ekleme
Kullanıcının gördüğü hata iletisi, özel durum sınıfının adından değil, oluşturulmuş özel durumun özelliğinden türetilir Exception.Message . Genellikle, ileti dizesini özel durum oluşturucusunun Exception.Messagebağımsız değişkenine geçirerek özelliğine message
bir değer atarsınız.
Yerelleştirilmiş uygulamalar için, uygulamanızın oluşturabileceği her özel durum için yerelleştirilmiş bir ileti dizesi sağlamanız gerekir. Yerelleştirilmiş hata iletileri sağlamak için kaynak dosyalarını kullanırsınız. Uygulamaları yerelleştirme ve yerelleştirilmiş dizeleri alma hakkında bilgi için aşağıdaki makalelere bakın:
- Nasıl yapılır: Yerelleştirilmiş özel durum iletileriyle kullanıcı tanımlı özel durumlar oluşturma
- .NET uygulamalarındaki kaynaklar
- System.Resources.ResourceManager
Uygun dil bilgisini kullanma
Net cümleler yazın ve bitiş noktalama işaretlerini ekleyin. Özelliğine Exception.Message atanan dizedeki her tümce bir süre içinde bitmelidir. Örneğin, "Günlük tablosu taşmış." doğru dil bilgisi ve noktalama işaretlerini kullanır.
Throw deyimlerini iyi yerleştir
Yığın izlemesinin yararlı olacağı throw deyimlerini yerleştirin. Yığın izlemesi, özel durumun oluştuğu deyimde başlar ve özel durumu yakalayan deyiminde catch
biter.
finally yan tümcelerinde özel durum oluşturmayın
Yan tümcelerde finally
özel durum oluşturmayın. Daha fazla bilgi için bkz. kod çözümleme kuralı CA2219.
Beklenmeyen yerlerden özel durum oluşturmayın
, GetHashCode
ve yöntemleri, statik oluşturucular ve ToString
eşitlik işleçleri gibi Equals
bazı yöntemler özel durumlar oluşturmamalıdır. Daha fazla bilgi için bkz. kod çözümleme kuralı CA1065.
Bağımsız değişken doğrulama özel durumlarını zaman uyumlu olarak oluşturma
Görev döndüren yöntemlerde, yöntemin zaman uyumsuz bölümünü girmeden önce bağımsız değişkenleri doğrulamanız ve ve ArgumentNullExceptiongibi ArgumentException ilgili özel durumları oluşturmanız gerekir. Yöntemin zaman uyumsuz bölümünde oluşan özel durumlar, döndürülen görevde depolanır ve örneğin görev beklenene kadar ortaya çıkmaz. Daha fazla bilgi için bkz . Görev döndüren yöntemlerde özel durumlar.
Özel özel durum türleri
Aşağıdaki en iyi yöntemler özel özel durum türleriyle ilgilidir:
- Özel durum sınıfı adlarını ile sonlandır
Exception
- Üç oluşturucu dahil et
- Gerektiğinde ek özellikler sağlayın
Özel durum sınıfı adlarını ile sonlandır Exception
Özel bir özel durum gerektiğinde, bunu uygun şekilde adlandırın ve sınıfından türetin Exception . Örneğin:
public class MyFileNotFoundException : Exception
{
}
Public Class MyFileNotFoundException
Inherits Exception
End Class
Üç oluşturucu dahil et
Kendi özel durum sınıflarınızı oluştururken en az üç ortak oluşturucu kullanın: parametresiz oluşturucu, dize iletisi alan bir oluşturucu ve dize iletisi ve iç özel durum alan bir oluşturucu.
- Exception(), varsayılan değerleri kullanır.
- Exception(String)dize iletisini kabul eder.
- Exception(String, Exception), bir dize iletisi ve bir iç özel durum kabul eder.
Örnek için bkz . Nasıl yapılır: Kullanıcı tanımlı özel durumlar oluşturma.
Gerektiğinde ek özellikler sağlayın
Özel durum (özel ileti dizesine ek olarak) için yalnızca ek bilgilerin yararlı olduğu programlı bir senaryo olduğunda ek özellikler sağlayın. Örneğin, FileNotFoundException özelliği sağlar FileName .