Erişilemez kod, ancak bir istisna ile erişilebilir


108

Bu kod, ODBC bağlantılı bir veritabanından okuyan ve bu veritabanına yazan bir uygulamanın parçasıdır. Veritabanında bir kayıt oluşturur ve ardından bir kaydın başarıyla oluşturulup oluşturulmadığını kontrol eder ve ardından geri döner true.

Kontrol akışına ilişkin anlayışım şu şekildedir:

command.ExecuteNonQuery()Invalid​Operation​Exception"Bir yöntem çağrısı nesnenin mevcut durumu için geçersiz olduğunda" bir atma yapmak için belgelenmiştir . Bu nedenle, eğer bu olursa, trybloğun yürütülmesi durur, finallyblok yürütülür, ardından return false;altta çalıştırılır .

Ancak, return false;IDE'm erişilemez kod olduğunu iddia ediyor . Ve doğru gibi görünüyor, onu kaldırabilirim ve herhangi bir şikayet olmadan derler. Ancak, benim için, belirtilen istisnanın atıldığı kod yolu için bir dönüş değeri olmayacakmış gibi görünüyor.

private static bool createRecord(String table,
                                 IDictionary<String,String> data,
                                 System.Data.IDbConnection conn,
                                 OdbcTransaction trans) {

    [... some other code ...]

    int returnValue = 0;
    try {
        command.CommandText = sb.ToString();
        returnValue = command.ExecuteNonQuery();

        return returnValue == 1;
    } finally {
        command.Dispose();
    }

    return false;
}

Burada anlama konusundaki hatam nedir?



41
Yan not: Disposeaçıkça aramayın , ancak şunu yazın using:using (var command = ...) {command.CommandText = sb.ToString(); return command.ExecuteNonQuery(); }
Dmitry Bychenko

7
Bir finallyblok düşündüğünüzden daha başka bir şey anlamına gelir.
Thorbjørn Ravn Andersen

Yanıtlar:


149

Derleyici Uyarısı (düzey 2) CS0162

Ulaşılamayan kod tespit edildi

Derleyici asla çalıştırılmayacak bir kod tespit etti.

Yani, Derleyici Statik Analiz yoluyla ulaşılamayacağını yeterince anlar ve derlenmiş IL'den tamamen çıkarır (dolayısıyla uyarınız)

Not : Hata ayıklayıcıyla Erişilemez Koda Adım atmaya çalışarak veya bir IL Gezgini kullanarak bu gerçeği kendi kendinize kanıtlayabilirsiniz.

finallyBir yayınlanabilirken İstisna yine de olacaktır (kenara rağmen) o (bu durumda) gerçeğini değiştirmez, yakalanmamış İstisna . Ergo, returnne olursa olsun sonuncusu asla vurulmayacak.

  • Geçen üzerine devam etmek kod istiyorsanız return, tek seçenek olduğunu yakalayın İstisna ;

  • Yapmazsanız, olduğu gibi bırakın ve return.

Misal

try 
{
    command.CommandText = sb.ToString();
    returnValue = command.ExecuteNonQuery();

    return returnValue == 1;
}
catch(<some exception>)
{
   // do something
}
finally 
{
    command.Dispose();
}

return false;

Belgelerden alıntı yapmak için

deneyin-nihayet (C # Başvurusu)

Bir nihayet bloğu kullanarak, bir try bloğunda ayrılan kaynakları temizleyebilir ve try bloğunda bir istisna oluşsa bile kodu çalıştırabilirsiniz. Tipik olarak, denetim bir try deyimi bıraktığında, nihayet blok çalıştırmanın deyimleri. Kontrol aktarımı, normal yürütme, break, continue, goto veya return ifadesinin yürütülmesi veya try ifadesinden bir istisnanın yayılmasının bir sonucu olarak gerçekleşebilir.

İşlenen bir istisna dahilinde, ilişkili nihayet bloğunun çalıştırılması garanti edilir. Ancak, istisna işlenmemişse, nihayet bloğunun yürütülmesi, istisna çö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.

Genellikle, işlenmemiş bir istisna bir uygulamayı bitirdiğinde, ultimate bloğunun çalıştırılıp çalıştırılmadığı önemli değildir. Bununla birlikte, bir final bloğunda bu durumda bile çalıştırılması gereken ifadeleriniz varsa, bir çözüm, try-final ifadesine bir catch bloğu eklemektir . Alternatif olarak, çağrı yığınının yukarısındaki bir dene-nihayet ifadesinin try bloğunda fırlatılabilecek istisnayı yakalayabilirsiniz . Diğer bir deyişle, dene-nihayet ifadesini içeren yöntemi çağıran yöntemde veya bu yöntemi çağıran yöntemde veya çağrı yığınındaki herhangi bir yöntemde istisnayı yakalayabilirsiniz. İstisna yakalanmazsa, final bloğunun yürütülmesi, işletim sisteminin bir istisna çözme işlemini tetiklemeyi seçip seçmemesine bağlıdır.

son olarak

IDisposableArabirimi destekleyen (yönetilmeyen kaynakları serbest bırakmak için tasarlanmış) herhangi bir şey kullanırken , onu bir usingifadeye sarabilirsiniz . Derleyici , nesneye bir try {} finally {}ve dahili olarak çağrı Dispose()oluşturacaktır.


1
İlk cümlelerde IL ile neyi kastediyorsunuz?
Saat

2
@Clockwork IL , üst düzey .NET dillerinde yazılmış kod derlemesinin bir ürünüdür. Bu dillerden birinde yazılmış kodunuzu derledikten sonra, IL'den yapılmış bir ikili alacaksınız. Ara Dilin bazen Ortak Ara Dil (CIL) veya Microsoft Orta Dil (MSIL) olarak da adlandırıldığını unutmayın.,
Michael Randall

1
Kısa vadede, olasılıkları yakalayamadığı için: Ya deneme, dönüşe ulaşana kadar çalışır ve böylece en sonunda aşağıdaki dönüşü yok sayar VEYA bir istisna atılır ve bu dönüşe asla ulaşılmaz çünkü bir istisna nedeniyle fonksiyon çıkış yapacaktır. atılmış.
Felype

86

Nihai blok çalıştırılır, ardından yanlış dönüşü yürütür; altta.

Yanlış. finallyistisnayı yutmaz. Bunu onurlandırır ve istisna normal olarak atılır. Kodu yalnızca blok sona ermeden önce (istisnalı veya istisnasız) çalıştırır.

İstisnanın yutulmasını istiyorsanız, catchiçinde no olmayan bir blok kullanmalısınız throw.


1
yukarıdaki sinppet istisna durumunda derlenecek mi, ne döndürülecek?
Ehsan Sajjad

3
Derleme yapıyor, ancak bunun return falseyerine bir istisna atacağı için asla vurmayacak @EhsanSajjad
Patrick Hofman

1
tuhaf görünüyor, derler çünkü ya herhangi bir istisna olmaması durumunda bool için bir değer döndürecek ve istisna durumunda hiçbir şey olmayacak, bu yüzden yöntemin dönüş türünü tatmin etmek için okunaklı?
Ehsan Sajjad

2
Derleyici sadece satırı görmezden gelir, uyarı bunun içindir. Öyleyse bu neden tuhaf? @EhsanSajjad
Patrick Hofman

3
Eğlenceli bir gerçek: Aslında edilir değil istisna programında yakalandı değilse bir nihayet bloğu aday olacağını garanti. Spec bu garanti etmez ve erken CLRs vermedi DEĞİL nihayet bloğu yürütün. Sanırım 4.0 ile başlayarak (daha önce olabilirdi) davranış değişti, ancak diğer çalışma zamanları yine de farklı davranabilir. Oldukça şaşırtıcı davranışlar yaratır.
Voo

27

Uyarı, kullanmadığınız catchve yönteminizin temelde şu şekilde yazıldığı içindir:

bool SomeMethod()
{
    return true;
    return false; // CS0162 Unreachable code detected
}

Kullanmak yana finallyatmak yalnızca, tercih edilen çözüm kullanmaktır usingdesen:

using(var command = new WhateverCommand())
{
     ...
}

Neyin Disposeçağrılacağından emin olmak için bu yeterli . Bu kod bloğunun başarıyla uygulanması sonrasında veya sırasında ya çağrılacak garantili (önceki) bazı catch aşağı çağrı yığını (ana aramaları doğru, aşağı?).

İmha etmekle ilgili değilse, o zaman

try { ...; return true; } // only one return
finally { ... }

yeter, çünkü yapacaksın false yöntemin sonunda asla geri dönmek zorunda (bu satıra gerek yoktur). Yönteminiz ya komut yürütmenin sonucudur ( trueveya false) ya da aksi takdirde bir istisna atar .


Beklenen istisnaları sararak kendi istisnalarını da atmayı düşünün ( InvalidOperationException oluşturucusuna göz atın ):

try { ... }
catch(SomeExpectedException e)
{
    throw new SomeBetterExceptionWithExplanaition("...", e);
}

Bu genellikle, arayan için iç içe geçmiş çağrı istisnasının söyleyeceğinden daha anlamlı (yararlı) bir şey söylemek için kullanılır.


Çoğu zaman işlenmemiş istisnaları gerçekten önemsemezsiniz. Bazen finally, istisna işlenmemiş olsa bile çağrıldığından emin olmanız gerekir . Bu durumda, kendiniz yakalarsınız ve tekrar atarsınız ( bu cevaba bakın ):

try { ... }
catch { ...; throw; } // re-throw
finally { ... }

14

Görünüşe göre, şuna benzer bir şey arıyorsunuz:

private static bool createRecord(string table,
                                 IDictionary<String,String> data,
                                 System.Data.IDbConnection conn,
                                 OdbcTransaction trans) {
  [... some other code ...]

  // Using: do not call Dispose() explicitly, but wrap IDisposable into using
  using (var command = ...) {
    try {
      // Normal flow:
      command.CommandText = sb.ToString();

      // True if and only if exactly one record affected
      return command.ExecuteNonQuery() == 1;
    }
    catch (DbException) {
      // Exceptional flow (all database exceptions)
      return false;
    }
  }
}

, Not, o Lütfen finally yutmamasına herhangi istisna

finally {
  // This code will be executed; the exception will be efficently re-thrown
}

// And this code will never be reached

8

Bir catchbloğunuz yok, bu nedenle istisna hala atılır ve dönüşü engeller.

Nihai blok çalıştırılır, ardından yanlış dönüşü yürütür; altta.

Bu yanlıştır, çünkü nihayet bloğu çalıştırılacak ve daha sonra yakalanmamış bir istisna olacaktır.

finallybloklar temizleme için kullanılır ve istisnayı yakalamazlar. İstisna, dönüşten önce fırlatılır, bu nedenle, dönüşe asla ulaşılmaz, çünkü daha önce bir istisna atılır.

IDE'niz hiçbir zaman ulaşılamayacağı konusunda doğrudur, çünkü istisna atılacaktır. Yalnızca catchbloklar istisnaları yakalayabilir.

Belgelerden okumak ,

Genellikle, işlenmemiş bir istisna bir uygulamayı bitirdiğinde, ultimate bloğunun çalıştırılıp çalıştırılmadığı önemli değildir. Bununla birlikte, bir final bloğunda bu durumda bile çalıştırılması gereken ifadeleriniz varsa, bir çözüm, try-final ifadesine bir catch bloğu eklemektir . Alternatif olarak, çağrı yığınının yukarısındaki bir dene-nihayet ifadesinin try bloğunda fırlatılabilecek istisnayı yakalayabilirsiniz. Diğer bir deyişle, dene-nihayet ifadesini içeren yöntemi çağıran yöntemde veya bu yöntemi çağıran yöntemde veya çağrı yığınındaki herhangi bir yöntemde istisnayı yakalayabilirsiniz. İstisna yakalanmazsa, final bloğunun yürütülmesi, işletim sisteminin bir istisna çözme işlemini tetiklemeyi seçip seçmemesine bağlıdır .

Bu açıkça gösteriyor ki, nihayet istisnayı yakalamaya yönelik değildir ve catchifadeden önce boş bir ifade olsaydı haklı olurdunuz finally.


7

İstisna atıldığında, yığın bir değer döndürmeden çözülür (yürütme işlevin dışına çıkar) ve işlevin üzerindeki yığın çerçevelerindeki herhangi bir yakalama bloğu bunun yerine istisnayı yakalar.

Bu nedenle return falseasla idam edilmeyecek.

Kontrol akışını anlamak için manuel olarak bir istisna atmayı deneyin:

try {
    command.CommandText = sb.ToString();
    returnValue = command.ExecuteNonQuery();

    // Try this.
    throw new Exception("See where this goes.");

    return returnValue == 1;
} finally {
    command.Dispose();
}

5

Kodunuzda:

private static bool createRecord(String table, IDictionary<String,String> data, System.Data.IDbConnection conn, OdbcTransaction trans) {

    [... some other code ...]

    int returnValue = 0;
    try {
        command.CommandText = sb.ToString();
        returnValue = command.ExecuteNonQuery();

        return returnValue == 1; // You return here in case no exception is thrown
    } finally {
        command.Dispose(); //You don't have a catch so the exception is passed on if thrown
    }

    return false; // This is never executed because there was either one of the above two exit points of the method reached.
}

Nihai blok çalıştırılır, ardından yanlış dönüşü yürütür; altta

Bu, mantığınızdaki kusurdur çünkü finallyblok istisnayı yakalamaz ve asla son dönüş ifadesine ulaşmaz.


4

Son deyim return falseerişilemez, çünkü try bloğunda catchistisnayı ele alacak bir parça eksiktir , bu nedenle istisna bloktan sonra yeniden oluşturulur finallyve yürütme asla son ifadeye ulaşmaz.


2

Kodunuzda, ilki nedeniyle ikincisine ulaşılamayan iki dönüş yolu var. Senin son deyimi trybloğu return returnValue == 1;Ulaşmaya asla böylece, normal getiri sağlayan return false;yöntem blok sonunda.

FWIW, blokla ilgili muafiyet sırası finallyşöyledir: try bloğundaki dönüş değerini sağlayan ifade önce değerlendirilecek, ardından nihayet bloğu çalıştırılacak ve sonra hesaplanan ifade değeri döndürülecektir (try bloğunun içinde).

Bir olmadan ... istisna üzerinde akışını ilgili olarak catch, finallyistisna ardından yöntemin dışına rethrown önce istisna üzerine çalıştırılacaktır; "dönüş" yolu yoktur.

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.