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.
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.
Yanıtlar:
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.
ThreadAbortException
, kritik blok olduğu için ilk olarak tüm blok bitirilir.
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 catch
ve finally
komutları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 try
gibi, bir (iç) catch
blokta 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
finally
blok (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)
Bekleyen bir istisna varsa ( try
blok bir finally
ama hayır olduğunda catch
), yeni istisna bunun yerini alır.
Bekleyen bir istisna yoksa, bu sadece finally
bloğun dışına bir istisna atmak gibi çalışır .
catch
(yeniden) bir özel durum blok.
"Orijinal istisnayı" ( try
blokta atılır) kaydetmek ve "nihayet istisnayı" ( finally
blokta 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.
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
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);
}
}
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
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 finally
bloğa atılan bir istisnanın özel bir şeyi yoktur, bunu B kodunun attığı istisna olarak kabul edin.
İ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.
"Nihayet" bloğu try bloğunda bir istisna oluştuktan sonra yürütülüyorsa,
ve bu istisna ele alınmazsa
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");
}
}
}