Aracılığıyla paylaş


Ö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, belirsiz davranışa sahip bir uygulamadan daha güvenilir ve teşhis edilebilir.

Bu makalede özel durumları işlemeye ve oluşturmaya yönelik en iyi yöntemler açıklanmaktadır.

Özel durumları işleme

Aşağıdaki en iyi yöntemler, özel durumları nasıl işlediğinizle ilgilidir:

Hatalardan kurtulmak veya kaynakları serbest bırakmak için try/catch/finally bloklarını kullan.

Olası bir istisna oluşturabilecek kod ve uygulamanız bu istisnadan kurtulabileceğinde, kodu çevreleyen try/catch bloklarını kullanın. catch bloklarda her zaman en çok türetilenden en az türetilene kadar özel durumları sıralar. (Tüm özel durumlar Exception sınıfından türetilir. catch yan tümcesi, daha fazla türetilmiş özel durumları, catch yan tümcesinin temel özel durum sınıfı için önce geldiği durumda işlemez.) Kodunuz bir özel durumdan kurtulamıyorsa, o özel durumu yakalamayın. Mümkünse kurtarmak için çağrı yığınında daha fazla yöntem etkinleştirin.

using ifadeleriyle veya finally bloklar ile ayrılan kaynakları temizleyin. İstisnalar atıldığında kaynakları otomatik olarak temizlemek için using ifadelerini tercih edin. finallyuygulamayan kaynakları temizlemek için IDisposable blokları kullanın. finally yan tümcesindeki 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ı fakat bir istisna tetikleyebilecek koşullar için, onları istisnadan kaçınacak şekilde işlemeyi düşünün. Ö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 deyimi 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, InvalidOperationException özel durumunu yakalayabilirsiniz.

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 yürütü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ış durumları olabilir ve bu durumda yine de bir istisna ile karşılaşabilirsiniz.

Özel durumları önlemek için Try* yöntemlerini çağırma

Özel durumların performans maliyeti engelleyiciyse, bazı .NET kitaplık yöntemleri alternatif hata işleme biçimleri sağlar. Örneğin, ayrıştırılacak değer Int32.Parsetarafından temsil edilemeyecek kadar büyükse OverflowException bir Int32 oluşturur. Ancak Int32.TryParse bu istisnayı 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 parametresine sahiptir. Dictionary<TKey,TValue>.TryGetValue, bir sözlükten değer almaya çalışırken benzer davranışlar gösterir.

İptal ve eşzamansız özel durumları yakalama

Asenkron bir yöntem çağırırken OperationCanceledException'den türetilen TaskCanceledExceptionyerine OperationCanceledException'ı yakalamak daha iyidir. İptal istenirse birçok zaman uyumsuz yöntem OperationCanceledException özel durumu 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ürdükleri görevde yürütme sırasında fırlatılan istisnaları depolar. Döndürülen görevde bir özel durum depolanırsa, görev beklenirken bu özel durum fırlatılır. ArgumentExceptiongibi kullanım özel durumları hâlâ eşzamanlı olarak fırlatılır. 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, FileStream sınıfı dosyanın sonuna ulaşılıp ulaşılmadığını belirlemeye yardımcı olan yöntemler sağlar. Dosyanın sonunu okursanız atılacak olan istisnadan 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ında null (veya varsayılan) döndürmektir. Yaygın bir hata durumu, normal bir denetim akışı olarak kabul edilebilir. Bu durumlarda null (veya varsayılan) döndürerek, uygulama üzerindeki performans etkisini en aza indirirsiniz.

Değer türleri için uygulamanızın hata göstergesi olarak Nullable<T> mi yoksa default mi kullanacağınızı düşünün. Nullable<Guid>kullanarak defaultnullyerine Guid.Empty olur. Bazen Nullable<T> eklemek, bir değer mevcut olduğunda veya olmadığında bunu daha net hale getirir. Bazen Nullable<T> eklemek, gereksiz ek durumlar oluşturabilir ve sadece potansiyel hata kaynakları yaratmaya hizmet edebilir.

Yöntemler, özel durumlar nedeniyle tamamlanmadığında durumu geri yükleyin.

Çağıranlar, bir yöntemden istisna fırlatıldığında herhangi bir yan etkinin olmadığı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, para yatırma işlemi sırasında ortaya çıkan hataları yakalamak ve para çekme işlemini geri döndürmektir.

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 örnek, özgün özel durumu yeniden oluşturmak için throw kullanımını gösterir ve arayanların InnerException özelliğini incelemek zorunda kalmadan sorunun gerçek nedenini görmelerini 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

Bir özel durum fırlatıldığında, taşıdığı bilgilerin bir bölümü yığın izidir. 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. throw deyiminde ö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ının 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şleyiciden (catch blok) özel durumu yeniden fırlatırsanız, yakalanan özel durumu belirtmeden throw deyimini kullanın. CA2200 kod çözümleme kuralı, kodunuzda yanlışlıkla yığın izleme bilgilerini kaybedebileceğiniz yerleri bulmanıza yardımcı olur.
  • Özel durumu işleyici (catch blok) dışında bir yerden yeniden fırlatacaksanız, işleyicide özel durumu yakalamak için ExceptionDispatchInfo.Capture(Exception)'i ve yeniden fırlatmak istediğinizde ExceptionDispatchInfo.Throw()'yi kullanın. Yakalanan özel durumu incelemek için ExceptionDispatchInfo.SourceException özelliğini kullanabilirsiniz.

Aşağıdaki örnek, ExceptionDispatchInfo sınıfının nasıl kullanılabileceğini ve çıkışın nasıl görünebileceğini gösterir.

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, InvalidOperationException özel durumu oluşturun.
  • Geçersiz parametreler geçersizse, bir ArgumentException özel durumu veya ArgumentExceptionsınıfından türetilmiş önceden tanımlanmış sınıflardan birini atın.

Not

Mümkün olduğunda önceden tanımlanmış özel durum türlerini kullanmak en iyisi olsa da, , AccessViolationException, IndexOutOfRangeException ve NullReferenceExceptiongibi bazı StackOverflowException özel durum türlerini tetiklememelisiniz. Daha fazla bilgi için bkz. CA2201: Ayrılmış özel durum türlerini tetikleme.

Ö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 fırlatan statik throw yardımcı yöntemlerine sahiptir. İlgili özel durum türünü oluşturup fırlatmak yerine bu yöntemleri çağırmalısınız.

Bahşiş

Aşağıdaki kod çözümleme kuralları, kodunuzda bu statik throw yardımcılarından yararlanabileceğiniz yerler bulmanıza yardımcı olabilir: CA1510, CA1511, CA1512ve CA1513.

Zaman uyumsuz bir yöntem uyguluyorsanız, iptalin istenip istenmediğini denetleyip ardından CancellationToken.ThrowIfCancellationRequested()oluşturup atmaktansa, OperationCanceledException çağırın. 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şan özel durumun Exception.Message özelliğinden türetilir. Genellikle, ileti dizesini Exception.MessageÖzel Durum oluşturucusunun message bağımsız değişkenine geçirerek özelliğine 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. Exception.Message özelliğine 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 doğru şekilde yerleştir

Throw deyimlerini yığın izlemesinin yararlı olduğu yere yerleştirin. Yığın izlemesi, özel durumun oluştuğu deyimde başlar ve özel durumu yakalayan catch deyiminde biter.

finally bloklarında istisna oluşturmayın

finally yan tümcelerinde istisna oluşturmayın. Daha fazla bilgi için bkz. CA2219 kod çözümleme kuralı.

Beklenmeyen yerlerden özel durum oluşturmayın

Equals, GetHashCodeve ToString yöntemleri, statik oluşturucular ve eşitlik işleçleri gibi bazı yöntemler özel durumlar oluşturmamalıdır. Daha fazla bilgi için bkz. CA1065 kod çözümleme kuralı.

Bağımsız değişken doğrulama özel durumlarını eşzamanlı olarak fırlatın.

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 ArgumentException ve ArgumentNullExceptiongibi 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 istisnalar.

Özel istisna türleri

Aşağıdaki en iyi yöntemler özel özel durum türleriyle ilgilidir:

özel durum sınıfı adlarını Exception ile sonlandırma

Özel bir özel durum gerektiğinde, bunu uygun şekilde adlandırın ve Exception sınıfından türetin. Ö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 daha fazla özellik sağlayın

Yalnızca ek bilgilerin yararlı olduğu programlı bir senaryo olduğunda özel durum (özel ileti dizesine ek olarak) için daha fazla özellik sağlayın. Örneğin, FileNotFoundExceptionFileName özelliğini sağlar.

Ayrıca bkz.