Bir Try bloğunda bir değer döndürürsem, bir Son deyimindeki kod patlar mı?


237

Bir arkadaşım için bazı kodları gözden geçiriyorum ve bir try-nihayet bloğu içinde bir return deyimi kullandığını söylüyor. Son olarak bölümündeki kod, try bloğunun geri kalanı olmasa bile yine de tetikleniyor mu?

Misal:

public bool someMethod()
{
  try
  {
    return true;
    throw new Exception("test"); // doesn't seem to get executed
  }
  finally
  {
    //code in question
  }
}


Burada ele alınmak : yakalanmak demektir. Global işleyicinizdeki boş bir catch bloğu bile yeterlidir. : Ayrıca, ele alınamaz istisnaları vardır StackOverflowException, ExecutionEngineExceptionbunlardan bazıları edilir. Ve ele finallyalınamayacakları için koşmayacaklar.
Abel

8
@Abel: Farklı bir durumdan bahsediyor gibisin. Bu soru bir blok içinde geri dönmekle ilgilidirtry . Ani bir program hakkında hiçbir şey iptal edilmez.
Jon Skeet

6
@Abel: "İstisnadan sonra geri dön" derken ne demek istediğinizden emin değilim, ama burada sorulan şey bu görünmüyor. Koda bakın - trybloğun ilk ifadesi bir returnifadedir. (Bu bloğun ikinci ifadesine ulaşılamaz ve bir uyarı oluşturur.)
Jon Skeet

4
@Abel: Aslında, ve soru "Sonunda bir deyimde kod her zaman her durumda yürütmek" olsaydı ilgili olurdu. Ama sorulan bu değildi.
Jon Skeet

Yanıtlar:


265

Basit cevap: Evet.


9
@Edi: Hım, arka plan iş parçacıklarının bununla ne ilgisi olduğunu göremiyorum. Temel olarak, tüm süreç iptal edilmedikçe, finallyblok yürütülür.
Jon Skeet

3
Uzun cevap, Yığın Taşması, Bellek Dışı istisnası, bir tür zor bir çökme veya birisinin makinenizi doğru zamanda çıkarması gibi felaketli bir şey olursa nihayet çalışacak bir blok alamayacağınızdır. . Ancak tüm niyetler ve amaçlar için, çok çok yanlış bir şey yapmazsanız, nihayet blok her zaman ateş edecektir.
Andrew Rollings

Eğer denemeden önce kod bazı nedenlerden dolayı başarısız olursa, sonunda hala idam olduğunu fark ettim. Bu durumda, nihayetinde ancak altında kod başarılı bir şekilde yürütüldüğünde nihayet yürütülecek kriterleri karşılayan bir koşul ekleyebilirsiniz
kjosh

200

Normalde evet. Son olarak, istisnalar veya iade ifadesi de dahil olmak üzere her şeyin gerçekleştirileceği garanti edilir. Bu kuralın bir istisnası iş parçacığında ( OutOfMemoryException, StackOverflowException) gerçekleşen eşzamansız bir istisnadır .

Bu durumlarda zaman uyumsuz özel durumlar ve güvenilir kod hakkında daha fazla bilgi edinmek için, kısıtlanmış yürütme bölgeleri hakkında bilgi edinin .


154

İşte küçük bir test:

class Class1
{
    [STAThread]
    static void Main(string[] args)
    {
        Console.WriteLine("before");
        Console.WriteLine(test());
        Console.WriteLine("after");
    }

    static string test()
    {
        try
        {
            return "return";
        }
        finally
        {
            Console.WriteLine("finally");
        }
    }
}

Sonuç:

before
finally
return
after

10
@CodeBlend: WriteLineYöntem çağrısının sonucunun gerçekte ne zaman yapıldığına bağlıdır. Bu durumda returnçağrı, yöntem sonucunu ayarlar ve son olarak yöntem çıkmadan önce konsola yazar. Sonra WriteLineMain yönteminde metin dönüş çağrısından tükürür.
NotMe

38

MSDN'den alıntı

son olarak, önceki try bloğunun nasıl çıkıldığına bakılmaksızın, bir kod bloğunun çalıştırıldığını garanti etmek için kullanılır .


3
Mehrdad'ın istisnai durumlarına ek olarak, nihayet bir deneme bloğunda hata ayıklıyor ve daha sonra hata ayıklamayı durdurursanız da çağrılmayacak :). Hayatta hiçbir şey garanti edilemez.
StuartLC

1
Tam olarak değil, MSDN'den alıntı : Ancak, kural dışı durum işlenmezse, nihayet bloğun yürütülmesi kural dışı durum çözme işleminin nasıl tetiklendiğine bağlıdır. Bu da bilgisayarınızın nasıl kurulduğuna bağlıdır. .
Abel

1
@Abel burada çok önemli bir noktaya değiniyor. Herkes, örneğin program görev yöneticisi aracılığıyla iptal edilirse, nihayet bir bloğun nasıl yürütülmediğini görebilir. Ancak bu cevap, durduğu gibi, basit bir yanlıştır: finallyaslında mükemmel sıradan programlar için bile hiçbir şey garanti etmez (bu istisnayı attı).
Peter - Monica'yı eski

19

Genellikle evet, sonunda koşacak.

Aşağıdaki üç senaryo için, sonunda DAİMA çalışır:

  1. İstisna yok
  2. Senkron istisnalar (normal program akışında meydana gelen istisnalar).
    Bu, System.Exception özel durumundan türeyen CLS uyumlu özel durumlar ve System.Exception özel durumundan türeyen CLS uyumlu olmayan özel durumları içerir. CLS uyumlu olmayan özel durumlar RuntimeWrappedException tarafından otomatik olarak sarılır. C #, CLS dışı şikayet istisnaları alamaz, ancak C ++ gibi diller kullanabilir. C #, CLS uyumlu olmayan özel durumlar oluşturabilen bir dilde yazılmış koda çağrı yapıyor olabilir.
  3. Zamanuyumsuz ThreadAbortException
    .NET 2.0'dan itibaren, bir ThreadAbortException bir sonun çalışmasını artık engellemez. ThreadAbortException artık nihayetten önce veya sonra kaldırılıyor. Son olarak her zaman çalışır ve iş parçacığı iptali gerçekleşmeden önce deneme girildiği sürece bir iş parçacığı iptali tarafından kesintiye uğramaz.

Aşağıdaki senaryo, sonunda çalışmaz:

Eşzamansız StackOverflowException.
.NET 2.0'dan itibaren yığın taşması işlemin sonlandırılmasına neden olur. Sonunda bir CER (Kısıtlı Yürütme Bölgesi) yapmak için başka bir kısıtlama uygulanmadıkça, sonuncu çalıştırılmaz. CER'ler genel kullanıcı kodunda kullanılmamalıdır. Yalnızca temizleme kodunun her zaman çalışmasının kritik olduğu yerlerde kullanılmalıdır - tüm işlem yine de yığın taşması üzerinde kapatıldıktan ve yönetilen tüm nesneler varsayılan olarak temizlenecektir. Bu nedenle, bir CER'nin ilgili olması gereken tek yer, işlemin dışında tahsis edilen kaynaklar, örneğin yönetilmeyen tutamaçlar içindir.

Tipik olarak, yönetilmeyen kod, kullanıcı kodu tarafından kullanılmadan önce bazı yönetilen sınıf tarafından sarılır. Yönetilen sarıcı sınıfı, yönetilmeyen tutamacı sarmak için genellikle bir SafeHandle kullanır. SafeHandle, kritik bir sonlandırıcı ve temizleme kodunun yürütülmesini garanti etmek için bir CER'de çalıştırılan bir Release yöntemini uygular. Bu nedenle, CER'lerin dışa aktarılan kullanıcı kodu tarafından çevrilmelerini görmemelisiniz.

Bu nedenle, nihayet StackOverflowException üzerinde çalışmadığı gerçeği, işlem yine de sona ereceğinden, kullanıcı kodu üzerinde hiçbir etkisi olmamalıdır. Bir SafeHandle veya CriticalFinalizerObject dışında, yönetilmeyen bazı kaynakları temizlemeniz gereken bazı uç durumunuz varsa, aşağıdaki gibi bir CER kullanın; ancak lütfen bunun kötü bir uygulama olduğunu unutmayın; yönetilmeyen kavram, yönetilen sınıf (lar) a ve uygun SafeHandle (lara) tasarıma göre soyutlanmalıdır.

Örneğin,

// No code can appear after this line, before the try
RuntimeHelpers.PrepareConstrainedRegions();
try
{ 
    // This is *NOT* a CER
}
finally
{
    // This is a CER; guaranteed to run, if the try was entered, 
    // even if a StackOverflowException occurs.
}

Bir SOE durumunda CER bile çalışmaz. Bunu yazdığınızda, CER hakkındaki MSDN belgeleri yanlış / eksikti. Bir SOE FailFastdahili olarak tetikleyecektir . Ben bunları yakalamak başardı tek yolu CLR çalışma zamanı barındırma özelleştirmektir . Puanınızın diğer bazı eşzamansız istisnalar için hala geçerli olduğunu unutmayın.
Abel

10

Başka bir cevapta belirtilmediğini gördüğüm ve (18 yıl C # programlamasından sonra) bilmediğime inanamadığım çok önemli bir istisna var.

Bloğunuzun içinde herhangi bir tür istisna atar veya tetiklerseniz catch(sadece garip StackOverflowExceptionsve o ilk şeyleri değil ) ve tüm try/catch/finallybloğu başka bir try/catchbloğun içinde yoksa, finallybloğunuz yürütülmez. Bu kolayca gösterilebilir - ve bunu kendim görmeseydim, ne kadar sıklıkla okuduğum göz önüne alındığında, bir finallybloğun yürütülmemesine neden olabilecek gerçekten garip, küçük köşe vakaları, buna inanmazdım.

static void Main(string[] args)
{
    Console.WriteLine("Beginning demo of how finally clause doesn't get executed");
    try
    {
        Console.WriteLine("Inside try but before exception.");
        throw new Exception("Exception #1");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Inside catch for the exception '{ex.Message}' (before throwing another exception).");
        throw;
    }
    finally
    {
        Console.WriteLine("This never gets executed, and that seems very, very wrong.");
    }

    Console.WriteLine("This never gets executed, but I wasn't expecting it to."); 
    Console.ReadLine();
}

Bunun bir nedeni olduğundan eminim, ancak daha yaygın olarak bilinmemesi tuhaf. ( Örneğin burada belirtilmiştir , ancak bu özel sorunun hiçbir yerinde yoktur.)


Eh, finallyblok sadece bir catch değildir catchbloğu.
Peter - Monica'yı

7

Partiye geç kaldığımı, ancak MSDN durumlarının ( https://msdn.microsoft.com/en-us/library/zwc8s4fz.aspx ) atıldığı senaryoda (OP örneğinden farklı) fark ediyorum : "İstisna yakalanmazsa, nihayet bloğun yürütülmesi , işletim sisteminin bir istisna çözme işlemini tetiklemeyi seçip seçmediğine bağlıdır ."

Nihayet bloğunun, çağrı çağrısının daha da ilerisindeki başka bir fonksiyonun (Main gibi) istisnayı yakalaması durumunda çalışması garanti edilir. Bu ayrıntı genellikle sorun değildir, çünkü tüm çalışma zamanı ortamları (CLR ve OS) C # programları, bir işlemden çıkarken (dosya tanıtıcıları vb.) Sahip olduğu kaynakların çoğunda ücretsiz olarak çalışır. Bazı durumlarda bu çok önemli olabilir: Yarı yolda devam etmek istediğiniz bir veritabanı işlemi. açma; veya işletim sistemi tarafından otomatik olarak kapatılmayacak ve daha sonra bir sunucuyu engelleyebilecek bazı uzak bağlantılar.


3

Evet. Aslında nihayet bir açıklamanın ana noktası budur. Catestrophic bir şey meydana gelmedikçe (bellek yetersiz, bilgisayar takılı değil, vb.) Nihayet deyim her zaman yürütülmelidir.


1
Katılmıyorum yalvarıyorum. Krş cevabım. Deneme bloğundaki mükemmel bir normal istisna, finallybu istisna asla yakalanmazsa bloğu atlamak için yeterlidir . Bu beni şaşırttı ;-).
Peter - Monica'yı eski

@ PeterA.Schneider - Bu ilginç. Tabii ki, bunun sonuçları gerçekten fazla değişmez. Eğer çağrı yığını hiçbir şey istisnayı yakalayamazsa, o zaman (doğru anlıyorsam) sürecin sona ermek üzere olduğu anlamına gelmelidir. Bu yüzden, fişin çekilmesine benzer. Sanırım bu paket servisi olan restoran her zaman üst düzey veya işlenmemiş-istisna yakalamak olmalıdır.
Jeffrey L Whitledge

Kesinlikle, ben de anlıyorum. Bunu da şaşırtıcı buldum. Doğru: En yaygın kaynaklar, özellikle bellek ve açılan dosyalar olmak üzere otomatik olarak yayınlanır. Ancak işletim sisteminin bilmediği kaynaklar - örnek olarak açık sunucu veya veritabanı bağlantıları - hiçbir zaman düzgün kapatılmadığı için uzak tarafta açık kalabilir; soketler devam ediyor, vb. Sanırım bir üst düzey istisna işleyicisine sahip olmak ihtiyatlı, evet.
Peter - Reinstate Monica


2

nihayet System.exit (0) kullanarak uygulamadan çıkmanız durumunda çalışmaz; de olduğu gibi

try
{
    System.out.println("try");
    System.exit(0);
}
finally
{
   System.out.println("finally");
}

sonuç sadece: denemek


4
Sanırım yanlış dilde cevap veriyorsunuz, soru bunun hakkındaydı c#, ama öyle görünüyor Java. Ve bunun yanı sıra, çoğu durumda System.exit()kötü bir tasarımın bir ipucu :-)
z00l

0

Senaryoların% 99'u, finallybloğun içindeki kodun çalışacağını garanti edecektir, ancak şu senaryoyu düşünün: try-> finallyblock (no catch) değerine sahip bir iş parçacığınız var ve bu iş parçacığında işlenmeyen bir özel durum var. Bu durumda, iş parçacığı çıkacak ve finallybloğu yürütülmeyecektir (uygulama bu durumda çalışmaya devam edebilir)

Bu senaryo oldukça nadirdir, ancak sadece cevabın HER ZAMAN "Evet" olmadığını, çoğu zaman "Evet" ve bazen nadir durumlarda "Hayır" olduğunu göstermektir.


0

Nihayet bloğun temel amacı, içinde yazılanları yürütmektir. Denemek veya yakalamak ne olursa olsun bağlı olmamalıdır.Ancak System.Environment.Exit (1) ile uygulama sonraki kod satırına geçmeden çıkacaktır.

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.