Sonunda bir blok bir istisna atarsa ​​ne olur?


266

Sonunda bir blok bir istisna atarsa, tam olarak ne olur?

Özellikle, istisna bir nihayet bloğun ortasında atılırsa ne olur. Bu bloktaki diğer ifadeler (sonra) çağrılıyor mu?

İstisnaların yukarıya doğru ilerleyeceğinin farkındayım.


8
Neden sadece denemiyorsunuz? Ama bu tür şeylerde, en çok sevdiğim şey nihayet önce geri dönüp sonra nihayet bloktan başka bir şey döndürmektir. :)
38'de

8
Son olarak bir bloktaki tüm ifadeler yürütülmelidir. Geri dönüşü olamaz. msdn.microsoft.com/tr-tr/library/0hbbzekw(VS.80).aspx
Tim Scarborough

Yanıtlar:


419

Sonunda bir blok bir istisna atarsa tam olarak ne olur?

Bu istisna dışarı ve yukarı doğru yayılır ve daha yüksek bir seviyede ele alınabilir.

Sizin nihayet blok olacak değil istisnası atılır noktanın ötesine tamamlanacaktır.

Son blok daha önceki bir istisnanın işlenmesi sırasında yürütülüyorsa, ilk istisna kaybolur.

C # 4 Dil Özellikleri § 8.9.5: Son olarak blok başka bir istisna atarsa, geçerli istisnanın işlenmesi sonlandırılır.


9
Bir olmadıkça ThreadAbortException, kritik blok olduğu için ilk olarak tüm blok bitirilir.
Dmytro Shevchenko

1
@Shedal - haklısınız, ancak bu yalnızca "belirli eşzamansız özel durumlar", yani ThreadAbortException için geçerlidir. Normal 1 iş parçacıklı kod için cevabım geçerlidir.
Henk Holterman

"İlk istisna kaybolur" - bu gerçekten çok hayal kırıklığı yaratıyor, yanlışlıkla Dispose () 'de istisna atan IDisposable nesneleri buluyorum, bu da istisna "using" cümlesi içinde kayboluyor.
Alex Burtsev

"Dispose () içinde istisna oluşturan IDisposable nesneleri buluyorum" - bu en az söylemek garip. MSDN'de okuyun: Dispose (bool) içinden bir istisna atmaktan
kaçının

1
@HenkHolterman: Doğrudan bağlı birincil sabit diskte disk dolu hataları çok yaygın değildir, ancak programlar bazen çıkarılabilir veya ağa bağlı disklere dosya yazar; sorunlar bunlarla çok daha yaygın olabilir. Birisi bir dosya tamamen yazılmadan önce bir USB çubuğu çıkarırsa, onlara hemen gitmelerini söylemek, gittikleri yere ulaşana kadar ve dosyanın bozuk olduğunu bulmaktan daha iyi olur. Biri olduğunda önceki hatayı vermek mantıklı bir davranış olabilir, ancak daha erken bir hata olmadığında, sorunu bildirmeden bırakmak daha iyi olur.
supercat

101

Bu gibi sorular için genellikle Visual Studio'da boş bir konsol uygulama projesi açar ve küçük bir örnek program yazarım:

using System;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            try
            {
                throw new Exception("exception thrown from try block");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Inner catch block handling {0}.", ex.Message);
                throw;
            }
            finally
            {
                Console.WriteLine("Inner finally block");
                throw new Exception("exception thrown from finally block");
                Console.WriteLine("This line is never reached");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Outer catch block handling {0}.", ex.Message);
        }
        finally
        {
            Console.WriteLine("Outer finally block");
        }
    }
}

Programı çalıştırdığınızda catchve finallykomutların yürütüldüğü sırayı göreceksiniz . İstisna atıldıktan sonra nihayet bloğundaki kod yürütülmeyeceğini lütfen unutmayın (aslında, bu örnek programda Visual Studio erişilemez kod algıladığını bile sizi uyaracaktır):

Deneme bloğundan atılan iç yakalama bloğu işleme istisnası.
İç sonunda blok
Son bloktan atılan dış yakalama bloğu işleme istisnası.
Dış sonunda blok

Ek Açıklama

Michael Damatov'un işaret ettiği trygibi, bir (iç) catchblokta işlemezseniz bloktan bir istisna "yenir" . Aslında, yukarıdaki örnekte, yeniden yakalanan istisna dış yakalama bloğunda görünmemektedir. Aşağıdaki biraz değiştirilmiş örneğe daha da net bir şekilde bakmak için:

using System;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            try
            {
                throw new Exception("exception thrown from try block");
            }
            finally
            {
                Console.WriteLine("Inner finally block");
                throw new Exception("exception thrown from finally block");
                Console.WriteLine("This line is never reached");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Outer catch block handling {0}.", ex.Message);
        }
        finally
        {
            Console.WriteLine("Outer finally block");
        }
    }
}

Çıktıdan görebileceğiniz gibi, iç istisna "kaybolur" (yani yoksayılır):

İç sonunda blok
Son bloktan atılan dış yakalama bloğu işleme istisnası.
Dış sonunda blok

2
İç yakalamanızdaki İstisna AÇTIĞINIZ için, bu örnekte 'İç nihayet blok' hiçbir zaman ulaşılamaz
Theofanis Pantelides

4
@Teofanis Pantelides: Hayır, bir finallyblok (neredeyse) her zaman yürütülür, bu da bu durumda iç nihayet blok için de geçerlidir (sadece örnek programı kendiniz deneyin (Nihayetinde bir blok kurtarılamazsa yürütülmez) istisna, örneğin bir EngineExecutionException, ancak böyle bir durumda programınız hemen sonlandırılacaktır)
Dirk Vollmar

1
Yine de, ilk kod parçanızın ilk yakalamasında atmanın rolünün ne olduğunu görmüyorum. Bir konsol uygulaması ile birlikte ve onsuz denedim, hiçbir fark bulunamadı.
Mart'ta JohnPan

@johnpan: Mesele, hem blok hem de catch bloğunun bir istisna atmasına rağmen, nihayet bloğun her zaman yürütüldüğünü göstermekti. Konsol çıkışında gerçekten bir fark yok.
Dirk Vollmar

10

Bekleyen bir istisna varsa ( tryblok bir finallyama hayır olduğunda catch), yeni istisna bunun yerini alır.

Bekleyen bir istisna yoksa, bu sadece finallybloğun dışına bir istisna atmak gibi çalışır .


Orada ise bir özel durum da bekleyen olabilir olan bir eşleştirme catch(yeniden) bir özel durum blok.
stakx -


3

"Orijinal istisnayı" ( tryblokta atılır) kaydetmek ve "nihayet istisnayı" ( finallyblokta atılır) feda etmek için hızlı (ve oldukça açık) snippet , orijinal olanın sizin için daha önemli olması durumunda:

try
{
    throw new Exception("Original Exception");
}
finally
{
    try
    {
        throw new Exception("Finally Exception");
    }
    catch
    { }
}

Yukarıdaki kod yürütüldüğünde, "Orijinal İstisna" çağrı yığınını yükseltir ve "Sonunda İstisna" kaybolur.


2

Bir istisna nedeniyle asla açılmamış bir akışı kapatmaya çalışırken bir hata yakalamak için bunu yapmak zorunda kaldım.

errorMessage = string.Empty;

try
{
    byte[] requestBytes = System.Text.Encoding.ASCII.GetBytes(xmlFileContent);

    webRequest = WebRequest.Create(url);
    webRequest.Method = "POST";
    webRequest.ContentType = "text/xml;charset=utf-8";
    webRequest.ContentLength = requestBytes.Length;

    //send the request
    using (var sw = webRequest.GetRequestStream()) 
    {
        sw.Write(requestBytes, 0, requestBytes.Length);
    }

    //get the response
    webResponse = webRequest.GetResponse();
    using (var sr = new StreamReader(webResponse.GetResponseStream()))
    {
        returnVal = sr.ReadToEnd();
        sr.Close();
    }
}
catch (Exception ex)
{
    errorMessage = ex.ToString();
}
finally
{
    try
    {
        if (webRequest.GetRequestStream() != null)
            webRequest.GetRequestStream().Close();
        if (webResponse.GetResponseStream() != null)
            webResponse.GetResponseStream().Close();
    }
    catch (Exception exw)
    {
        errorMessage = exw.ToString();
    }
}

webRequest oluşturulduysa, ancak webRequest sırasında bir bağlantı hatası oluştu

using (var sw = webRequest.GetRequestStream())

sonunda, webRequest oluşturulduğu için açık olduğunu düşündüğü bağlantıları kapatmaya çalışan bir istisna yakalardı.

Sonunda içinde bir try-catch olmasaydı, bu kod web temizlenirken işlenmeyen bir istisnaya neden olur

if (webRequest.GetRequestStream() != null) 

oradan kod, oluşan hatayı düzgün bir şekilde işlemeden çıkıp arama yöntemi için sorunlara neden olmaz.

Umarım bu bir örnek olarak yardımcı olur


1

Başka bir kural dışı durum etkin durumdayken kural dışı durum atmak, birinci kural dışı durumun ikinci (sonraki) kural dışı durumla değiştirilmesine neden olur.

İşte ne olduğunu gösteren bazı kodlar:

    public static void Main(string[] args)
    {
        try
        {
            try
            {
                throw new Exception("first exception");
            }
            finally
            {
                //try
                {
                    throw new Exception("second exception");
                }
                //catch (Exception)
                {
                    //throw;
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }
  • Kodu çalıştırın ve "ikinci istisna" göreceksiniz
  • Denemek ve yakalamak ifadeleri uncomment ve "ilk istisna" göreceksiniz
  • Ayrıca atma uncomment; ifadesinde tekrar "ikinci istisna" göreceksiniz.

Sadece belirli bir kod bloğunun dışında yakalanan "ciddi" bir istisnanın temizlenmesinin, içinde yakalanan ve ele alınan bir istisna atmasının mümkün olduğunu belirtmek gerekir. İstisna filtreleri (C # olmasa da vb.net'te mevcuttur) kullanarak bu durumu tespit etmek mümkündür. Kod "işlemek" için yapabileceği bir sürü yok, ancak herhangi bir günlük çerçevesi kullanıyorsanız, neredeyse kesinlikle günlük değer. Temizleme içinde oluşan istisnalara sahip C ++ yaklaşımı, bir sistemin erimesini tetikler, çirkindir, ancak istisnaların ortadan kalkması IMHO iğrençliğidir.
supercat

1

Birkaç ay önce de böyle bir şeyle karşılaştım,

    private  void RaiseException(String errorMessage)
    {
        throw new Exception(errorMessage);
    }

    private  void DoTaskForFinally()
    {
        RaiseException("Error for finally");
    }

    private  void DoTaskForCatch()
    {
        RaiseException("Error for catch");
    }

    private  void DoTaskForTry()
    {
        RaiseException("Error for try");
    }


        try
        {
            /*lacks the exception*/
            DoTaskForTry();
        }
        catch (Exception exception)
        {
            /*lacks the exception*/
            DoTaskForCatch();
        }
        finally
        {
            /*the result exception*/
            DoTaskForFinally();
        }

Böyle bir sorunu çözmek için ben gibi bir yardımcı sınıf yaptı

class ProcessHandler : Exception
{
    private enum ProcessType
    {
        Try,
        Catch,
        Finally,
    }

    private Boolean _hasException;
    private Boolean _hasTryException;
    private Boolean _hasCatchException;
    private Boolean _hasFinnallyException;

    public Boolean HasException { get { return _hasException; } }
    public Boolean HasTryException { get { return _hasTryException; } }
    public Boolean HasCatchException { get { return _hasCatchException; } }
    public Boolean HasFinnallyException { get { return _hasFinnallyException; } }
    public Dictionary<String, Exception> Exceptions { get; private set; } 

    public readonly Action TryAction;
    public readonly Action CatchAction;
    public readonly Action FinallyAction;

    public ProcessHandler(Action tryAction = null, Action catchAction = null, Action finallyAction = null)
    {

        TryAction = tryAction;
        CatchAction = catchAction;
        FinallyAction = finallyAction;

        _hasException = false;
        _hasTryException = false;
        _hasCatchException = false;
        _hasFinnallyException = false;
        Exceptions = new Dictionary<string, Exception>();
    }


    private void Invoke(Action action, ref Boolean isError, ProcessType processType)
    {
        try
        {
            action.Invoke();
        }
        catch (Exception exception)
        {
            _hasException = true;
            isError = true;
            Exceptions.Add(processType.ToString(), exception);
        }
    }

    private void InvokeTryAction()
    {
        if (TryAction == null)
        {
            return;
        }
        Invoke(TryAction, ref _hasTryException, ProcessType.Try);
    }

    private void InvokeCatchAction()
    {
        if (CatchAction == null)
        {
            return;
        }
        Invoke(TryAction, ref _hasCatchException, ProcessType.Catch);
    }

    private void InvokeFinallyAction()
    {
        if (FinallyAction == null)
        {
            return;
        }
        Invoke(TryAction, ref _hasFinnallyException, ProcessType.Finally);
    }

    public void InvokeActions()
    {
        InvokeTryAction();
        if (HasTryException)
        {
            InvokeCatchAction();
        }
        InvokeFinallyAction();

        if (HasException)
        {
            throw this;
        }
    }
}

Ve böyle kullanılır

try
{
    ProcessHandler handler = new ProcessHandler(DoTaskForTry, DoTaskForCatch, DoTaskForFinally);
    handler.InvokeActions();
}
catch (Exception exception)
{
    var processError = exception as ProcessHandler;
    /*this exception contains all exceptions*/
    throw new Exception("Error to Process Actions", exception);
}

ama paramaters ve dönüş türlerini kullanmak istiyorsanız bu başka bir hikaye


1
public void MyMethod()
{
   try
   {
   }
   catch{}
   finally
   {
      CodeA
   }
   CodeB
}

CodeA ve CodeB tarafından atılan istisnaların ele alınma şekli aynıdır.

Bir finallybloğa atılan bir istisnanın özel bir şeyi yoktur, bunu B kodunun attığı istisna olarak kabul edin.


Açıklayabilir misiniz? İstisnalar aynı olduğunda ne demek istiyorsun?
Dirk Vollmar

1

İstisna çoğalır ve daha yüksek bir düzeyde ele alınmalıdır. Kural dışı durum daha üst düzeyde ele alınmazsa, uygulama kilitlenir. "Son olarak" blok yürütme, istisnanın atıldığı noktada durur.

Bir istisna olup olmadığına bakılmaksızın, "son olarak" bloğunun yürütülmesi garanti edilir.

  1. "Nihayet" bloğu try bloğunda bir istisna oluştuktan sonra yürütülüyorsa,

  2. ve bu istisna ele alınmazsa

  3. ve sonunda blok bir istisna atarsa

Sonra try bloğunda meydana gelen özgün istisna kaybolur.

public class Exception
{
    public static void Main()
    {
        try
        {
            SomeMethod();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    public static void SomeMethod()
    {
        try
        {
            // This exception will be lost
            throw new Exception("Exception in try block");
        }
        finally
        {
            throw new Exception("Exception in finally block");
        }
    }
} 

Detaylar için harika bir makale


-1

Bir istisna atar;) Bu istisnayı başka bir yakalama maddesinde yakalayabilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.