Hata bastırmaya karşı argümanlar


35

Projelerimizden birinde bunun gibi bir kod parçası buldum:

    SomeClass QueryServer(string args)
    {
        try
        {
            return SomeClass.Parse(_server.Query(args));
        }
        catch (Exception)
        {
            return null;
        }
    }

Anladığım kadarıyla, bunun gibi hataları bastırmak kötü bir uygulamadır, çünkü orijinal sunucunun istisnasından yararlı bilgileri yok eder ve kodu gerçekten sonlandırması gerektiğinde devam etmesini sağlar.

Bu gibi tüm hataları tamamen önlemek ne zaman uygundur?


13
Eric Lippert, istisnaları 4 farklı kategoride sınıflandırdığı sinir bozucu istisnalar adında güzel bir blog yazısı yazdı ve her biri için doğru yaklaşımın ne olduğunu öne sürdü. C # bakış açısıyla yazılmış fakat diller arasında geçerli olduğuna inanıyorum.
Damien_The_Unbeliever

3
Bu kodun "Hızlı başarısız" prensibini ihlal ettiğine inanıyorum. Gerçek hatanın içinde gizlenme olasılığı yüksektir.
Öforik


4
Çok şey yakaladın. NRE, sınır dışı dizi dizini, konfigürasyon hatası gibi hataları da yakalarsınız. Bu, bu hataları bulmanızı önler ve sürekli olarak hasara neden olurlar.
usr

Yanıtlar:


52

Bir sürü kütüphaneyi kullanarak binlerce dosya içeren bir kod hayal edin. Hepsinin böyle kodlandığını hayal edin.

Örneğin, sunucunuzun bir güncellemesinin bir yapılandırma dosyasının kaybolmasına neden olduğunu hayal edin; ve şimdi sahip olduğunuz tek şey bir yığın izlemesi, o sınıfı kullanmayı denediğinizde boş bir işaretçi istisnasıdır: bunu nasıl çözersiniz? En azından sadece dosyanın ham yığın izini bulamıyorsanız [dosya yolu] anlık olarak çözmenizi sağlayabilir, saatler sürebilir.

Daha da kötüsü: kodunuzun daha sonra çökmesine neden olan bir güncellemeden sonra kullandığınız kitaplıklardan birindeki başarısızlık. Bunu kütüphaneye nasıl geri koyabilirsin?

Sadece hata yapmayan sağlam hatalar olmadan

throw new IllegalStateException("THIS SHOULD NOT HAPPENING")

veya

LOGGER.error("[method name]/[arguments] should not be there")

size saatlerce zaman kazandırabilir.

Ancak, istisnayı görmezden gelip gerçekten boş dönmek ya da hiçbir şey yapmamak isteyebileceğiniz bazı durumlar vardır. Özellikle, bazı kötü tasarlanmış eski kodlarla bütünleşirseniz ve bu istisna normal bir durum olarak beklenir.

Aslında bunu yaparken, istisnaları gerçekten görmezden mi geldiğiniz veya ihtiyaçlarınız için “doğru şekilde ele aldığınızdan” emin olmalısınız. Eğer null döndürmek, bu durumda sizin istisnalarınızı "uygun şekilde ele alıyorsa", o zaman yapın. Ve bunun neden uygun bir şey olduğu hakkında bir yorum ekleyin.

En iyi uygulamalar çoğu durumda izlenmesi gereken şeylerdir, belki% 80, belki% 99, ancak başvurmadıkları yerlerde daima bir tek vaka bulacaksınız. Bu durumda, kodunuzu aylar sonra okuyacak diğerlerine (veya hatta kendinize) yönelik uygulamayı neden takip etmediğinize dair bir yorum bırakın.


7
Bunu bir yorumda neden yapmanız gerektiğini düşündüğünüzü açıklamak için +1. Bir açıklama yapmadan, istisna yutmak her zaman kafamda alarm zilleri yaratacaktır.
jpmc26

Bununla ilgili sorunum, yorumun bu kodun içinde sıkışmış olması. Fonksiyonun bir tüketicisi olarak, bu yorumu asla göremeyeceğim.
corsiKa

@ jpmc26 İstisna işlenirse (örneğin, özel bir değer döndürerek), istisna yutma değildir.
Deduplicator

2
@ corsiKa bir tüketici işlevini doğru yapıyorsa, tüketicinin uygulama ayrıntılarını bilmesi gerekmez. belki apache kütüphanelerinde bunlardan bazılarıdır, ya da ilkbahar ve sen bunu bilmiyorsun.
Walfrat

@Deduplicator evet, ancak algoritmayı bir parçası olarak bir catch kullanmanın da iyi bir pratik olması gerekmiyor :)
Walfrat

14

Bu kalıbın faydalı olduğu durumlar vardır - ancak bunlar, üretilen istisna asla olmamalıdır (örneğin, normal davranış için istisnalar kullanıldığında) kullanılır.

Örneğin, bir dosyayı açan bir sınıfınız olduğunu, onu döndürülen nesnede sakladığınızı hayal edin. Dosya yoksa, bir hata olamayacağını düşünebilirsiniz, null değerini döndürür ve kullanıcının bunun yerine yeni bir dosya oluşturmasına izin verirsiniz. Dosya açma yöntemi, burada dosya bulunmadığını belirtmek için bir istisna oluşturabilir, bu nedenle sessizce yakalamak geçerli bir durum olabilir.

Bununla birlikte, çoğu zaman bunu yapmak istemezsiniz ve kod böyle bir örüntü ile karıştırılırsa, şimdi bununla ilgilenmek istersiniz. En azından böyle sessiz bir tutucunun, bunun ne olduğunu (izlemiş, doğru) ve bu davranışı açıklayan bir yorum olduğunu söyleyen bir günlük satırı yazmasını bekliyorum.


2
"üretilen istisnanın asla olmaması gerektiği zaman kullanılır" böyle bir şey yok. İstisna NOKTASI, bir şeyin çok yanlış gittiğini işaret etmektir.
Öforik

3
@Euphoric Ama bazen bir kütüphanenin yazarı, o kütüphanenin son kullanıcısının niyetini bilmiyor. Bir kişinin "korkunç yanlışı" başkasının kolayca kurtarılabilir durumu olabilir.
Simon B,

2
@Ephoric, dilin "korkunç derecede yanlış" olarak ne düşündüğüne bağlıdır: Python, (örn.) C ++ 'da akış kontrolüne çok yakın olan şeyler için istisnalar gibi gözüküyor. Boş duruma geri dönmek bazı durumlarda savunulabilir olabilir - örneğin, öğelerin aniden ve tahmin edilemez bir şekilde tahliye edilebileceği bir önbellekten okumak.
Matt Krause

10
@Euphoric, "Dosya bulunamadı normal bir istisna değil. İlk önce dosyanın mevcut olup olmadığını kontrol etmelisiniz." Yok hayır! Yok hayır! Yok hayır! Yok hayır! Bu, atomik işlemler etrafında yapılan klasik yanlış düşünmedir. Dosya var olup olmadığını kontrol etmek ve dosyaya erişmek arasında silinmiş olabilir. Bu gibi durumlarla başa çıkmanın tek doğru yolu, ona erişmek ve bu istisnalar varsa ortaya çıkmasıdır, örneğin nullseçtiğiniz dilin sunabileceği en iyi olanı döndürerek .
David Arno

3
@Euphoric: Yani, dosyaya en az iki kere erişememek için kullanacağınız kodu yazın. Bu, DRY'yi ihlal ediyor ve aynı zamanda kullanılmayan kod da bozuk kod.
Deduplicator

7

Bu% 100 bağlama bağlıdır. Böyle bir işlevin arayan bir yerde hataları görüntülemek veya kaydetmek için bir zorunluluk varsa, o zaman açıkça saçma. Arayan kişi de tüm hataları veya istisna mesajları yoksaydıysa, istisnayı veya mesajını geri döndürmenin bir anlamı olmazdı. Gereksinim, kodu yalnızca herhangi bir neden göstermeden sonlandırmaksa, ardından bir arama

   if(QueryServer(args)==null)
      TerminateProgram();

Muhtemelen yeterli olacaktır. Bunun kullanıcı tarafından hatanın nedenini bulmayı zorlaştırıp düşürmeyeceğini göz önünde bulundurmanız gerekir - eğer öyleyse, bu yanlış kullanım şeklidir. Ancak, arama kodu böyle görünüyorsa

  result = QueryServer(args);
  if(result!=null)
      DoSomethingMeaningful(result);
  // else ??? Mask the error and let the user run into trouble later

o zaman kod inceleme sırasında geliştiriciyle tartışmalısınız. Birisi, doğru gereklilikleri öğrenemeyecek kadar tembel olduğu için böyle bir yanlış kullanım biçimini uygularsa, bu kod üretime girmemelidir ve kodun yazarına bu tembelliklerin olası sonuçları hakkında bilgi verilmelidir. .


5

Programlama ve sistemleri geliştirmek için harcadığım yıllarda, söz konusu modeli faydalı bulduğum sadece iki durum var (her iki durumda da, atılan istisnaların günlüğe kaydedilmesini de içermekteydi, düz yakalama ve nulliyi bir uygulama olarak geri dönmeyi düşünmüyorum) ).

İki durum aşağıdaki gibidir:

1. İstisna istisnai bir durum olarak değerlendirilmediğinde

Bu, bazı veriler üzerinde bir işlem yaptığınızda, fırlatabilecek, fırlatabileceğini ancak uygulamanızın çalışmaya devam etmesini istediğinizden emin olabilirsiniz çünkü işlenmiş verilere ihtiyacınız yoktur. Onları alırsanız, iyidir, almazsanız, aynı zamanda iyidir.

Bir sınıfın bazı isteğe bağlı özellikleri akla gelebilir.

2. Daha önce bir uygulamada kullanılan arayüzü kullanarak bir kütüphanenin yeni (daha iyi, daha hızlı?) Bir uygulamasını sunarken

İstisnalar atmayan, ancak nullhatayla sonuçlanan eski bir kütüphaneyi kullanan bir uygulamanız olduğunu hayal edin . Böylece, kütüphanenin orijinal API'sini hemen hemen kopyalayan ve bu yeni (hala atmayan) arayüzü uygulamanızda kullanıyor ve nullçekleri kendiniz kullanıyorsunuz.

Kütüphanenin yeni bir sürümü veya belki de aynı işlevi sunan tamamen farklı bir kütüphane gelir, ki bunlar yerine nulls, istisnalar atar ve onu kullanmak istersiniz.

İstisnaları ana uygulamanıza sızdırmak istemezsiniz, bu nedenle bu yeni bağımlılığı sarmak için onları oluşturduğunuz adaptöre bastırıp günlüğe kaydedersiniz.


İlk durum bir sorun değil, kodun istenen davranışı. Bununla birlikte, ikinci durumda, her yerde nullkütüphane adaptörünün geri dönüş değeri gerçekten bir hata anlamına gelirse , bir istisna atmak ve API'yi kontrol etmek yerine yakalamak, API'yi yeniden düzenlemek nulliyi bir fikir olabilir (ve genellikle kod açısından).

Kişisel olarak istisna baskısını sadece ilk vaka için kullanıyorum. Ben sadece ikinci vaka için kullandım, uygulamanın geri kalanının nulls yerine istisnalar ile çalışmasını sağlayacak bütçemiz yoktu .


-1

Programların yalnızca nasıl yapılacağını bildikleri istisnaları yakalaması gerektiğini ve muhtemelen programcının öngörmediği istisnaları nasıl kullanabileceğini bilmek mantıklı görünse de, böyle bir iddia, birçok operasyonun başarısız olabileceği gerçeğini görmezden gelir. hiçbir yan etkisi olmayan neredeyse sınırsız sayıda yol vardır ve çoğu durumda bu tür başarısızlıkların büyük çoğunluğu için uygun kullanım aynı olacaktır; Başarısızlığın kesin detayları alakasız olacak ve sonuçta programcının bunları önceden tahmin edip etmediği önemli olmamalı.

Örneğin, bir işlevin amacı, bir belge dosyasını uygun bir nesneye okumak ve bu nesneyi göstermek için kullanıcıya yeni bir belge penceresi oluşturmaksa veya dosyanın okunamadığını kullanıcıya bildirmekse, geçersiz belge dosyası uygulamanın çökmesine neden olmamalı - bunun yerine sorunu belirten bir mesaj göstermeli, ancak bir nedenden dolayı belgeyi yükleme denemesi sistemdeki başka bir şeyin durumunu bozmadıkça uygulamanın geri kalanının normal çalışmasına izin vermesi gerekir .

Temel olarak, bir istisnanın uygun şekilde ele alınması çoğu zaman istisna türüne, atıldığı yerden daha az bağımlı olacaktır; Bir kaynak okuma-yazma kilidi tarafından korunuyorsa ve okuma için kilidi alan bir yöntem içinde bir istisna atılırsa, yöntem davranış için hiçbir şey yapamadığından kilit davranış serbest bırakılmalıdır. . Kilit yazma için edinilirken bir istisna atılırsa, korunan kaynak geçersiz bir durumda olabileceğinden, kilit çoğunlukla geçersiz kılınmalıdır; eğer kilitleme ilkeli "geçersiz kılınmış" bir duruma sahip değilse, bu geçersizliği izlemek için bir bayrak eklenmesi gerekir. Kilidi geçersiz kılmadan serbest bırakmak kötüdür çünkü diğer kod korunan nesneyi geçersiz bir durumda görebilir. Ancak, kilit sarkma bırakarak, ya uygun bir çözüm. Doğru çözüm, kilitlenmeyi geçersiz kılmaktır, böylece satın alma için bekleyen veya gelecekteki girişimler derhal başarısız olur.

Geçersiz bir kaynağın kullanılmaya çalışılmadan önce terk edildiği ortaya çıkarsa, başvuruyu düşürmesi için hiçbir neden yoktur. Geçersiz bir kaynak bir uygulamanın devam eden çalışması için kritikse, uygulamanın indirilmesi gerekir, ancak kaynağın geçersiz kılınması büyük olasılıkla bunu gerçekleştirir. Orijinal istisnayı alan kodun, hangi durumun geçerli olduğunu bilme yolu yoktur, ancak kaynağı geçersiz kılarsa, her iki durumda da doğru önlemlerin alınmasını sağlayabilir.


2
Bu soruya cevap veremez çünkü istisna ayrıntılarını bilmeden bir istisnayı ele almak! = Sessiz bir şekilde tüketmek (genel) bir istisnadır.
Taemyr

@Taemyr: Eğer bir işlevin amacı "eğer mümkünse X yapmak ya da başka bir şey olmadığını belirtmek" ise ve X mümkün değilse, bu işlevden farklı olan tüm istisna türlerini listelemek çoğu zaman pratik olmaz. ne de arayan "X yapmak mümkün değildi" ötesinde umursamaz. Pokemon istisnası işleme genellikle bu gibi durumlarda işleri yürütmek için tek pratik yoldur ve kodun beklenmedik istisnalar tarafından bozulan şeyleri geçersiz kılma konusunda disiplinli olması durumunda, bazılarının yaptığı kadar kötü olması gerekmez.
supercat

Kesinlikle. Her yöntemin bir amacı olmalıdır, eğer bir yöntemin bir istisna atıp atmadığına bakılmaksızın amacını yerine getirebilirse, o zaman istisnayı, ne olursa olsun ve işini yapmak doğru olanı yapmaktır. Hata işleme, temel olarak bir hatadan sonra görevi tamamlamakla ilgilidir. Önemli olan bu, hangi hatanın meydana geldiği değil.
jmoreno

@jmoreno: İşleri zorlaştıran şey, pek çok durumda, bir programcının özellikle beklemeyeceği, bir sorun belirtmeyecek ve bazılarının bir programcının beklediği birkaç istisna dışında birçok istisna olacağıdır. özellikle bir sorunu işaret eden ve bunları ayırt etmenin tanımlanmış sistematik bir yolu olmadığını tahmin etmeyin. Bununla başa çıkmak için bildiğim tek yol, istisnalar, bozulmuş şeyleri geçersiz kılmaktır, bu yüzden fark ederlerse ve farketmezlerse görmezden gelinirler.
supercat,

-2

Bu tür bir hatayı yutmak kimseye özellikle yararsızdır. Evet, asıl arayan istisna umrunda olmayabilir, ama başkası olabilir .

O zaman ne yaparlar? Ne istisna lezzetinin geri döndüğünü görmek için istisnayı ele almak için kod eklerler. Harika. Ancak, özel durum işleyicisi içinde bırakılırsa, orijinal arayan artık boşa çıkmaz ve başka bir yerde bir şey bozulur.

Bazı yukarı akış kodlarının bir hata atabileceğini ve bu nedenle boş dönebileceğini bilseniz bile, en azından çağıran IMHO istisnasını reddetmeye çalışmamanız ciddi şekilde eylemsiz olacaktır.


"cidden inert" - belki de "cidden inept" mi demek istediniz?
Ezoterik Ekran Adı

@EsotericScreenName Birini seç ... ;-)
Robbie Dee

-3

Üçüncü taraf kütüphanelerin potansiyel olarak yararlı yöntemlere sahip olduğu örnekleri gördüm, ancak bazı durumlarda benim için çalışması gereken istisnalar dışında. Ne yapabilirim?

  • İhtiyacım olanı tam olarak kendime uygula. Son teslim tarihi zor olabilir.
  • Üçüncü taraf kodunu değiştirin. Ama sonra her yükseltme ile birleştirme çatışmaları aramak zorundayım.
  • İstisnayı sıradan bir boş gösterici, bir boole yanlışı veya başka bir şeye dönüştürmek için bir sarmalayıcı yöntemi yazın.

Örneğin, kütüphane yöntemi

public foo findFoo() {...} 

ilk fooyu döndürür, ancak hiçbiri yoksa bir istisna atar. Ama ihtiyacım olan ilk foo veya null değerini döndürmek için bir yöntem. Bu yüzden bunu yazarım:

public foo myFindFoo() {
    try {
        return findFoo() 
    } 
    catch (NoFooException ex) {
        return null;
    }
}

Temiz değil. Ama bazen pragmatik bir çözüm.


1
"Sizin için çalışması gereken yerlere istisnalar atmak" derken ne demek istiyorsunuz?
corsiKa

@ corsiKa, aklıma gelen örnek, bir liste veya benzer bir veri yapısı içerisinde arama yapan yöntemlerdir. Hiçbir şey bulamadıklarında başarısız olurlar mı yoksa boş değer döndürürler mi? Başka bir örnek ise bir boolean false döndürmek yerine zaman aşımına uğrayan waitUntil yöntemleridir.
om
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.