Ö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

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 InvalidOperationExceptionalı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ş OperationCanceledExceptionolması 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.Emptyolurnull. 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 eyığı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ın throw . 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 (catchblok) 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

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:

İ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:

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

, GetHashCodeve yöntemleri, statik oluşturucular ve ToString eşitlik işleçleri gibi Equalsbazı 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

Ö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.

Ö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 .

Ayrıca bkz.