İstisnalar veya hata kodları için kurallar


118

Dün bir iş arkadaşımla tercih edilen hata raporlama yönteminin ne olacağı konusunda hararetli bir tartışma yaşıyordum. Esas olarak, uygulama katmanları veya modüller arasındaki hataları bildirmek için istisnaların veya hata kodlarının kullanımını tartışıyorduk.

Hata raporlama için istisnalar atıp atmayacağınıza veya hata kodları döndürüp döndürmeyeceğinize karar vermek için hangi kuralları kullanıyorsunuz?

Yanıtlar:


81

Üst düzey şeylerde istisnalar; düşük seviyeli şeylerde hata kodları.

Bir istisnanın varsayılan davranışı yığını çözmek ve programı durdurmaktır, eğer bir komut dosyası yazıyorsam ve sözlükte olmayan bir anahtar için gidersem bu muhtemelen bir hatadır ve programın durmasını ve izin vermesini istiyorum her şeyi biliyorum.

Bununla birlikte, olası her durumda davranışını bilmem gereken bir kod parçası yazıyorsam , o zaman hata kodları istiyorum. Aksi takdirde, ne işe yarayacağını bilmek için işlevimdeki her satır tarafından atılabilecek her istisnayı bilmem gerekir ( Bunun ne kadar zor olduğuna dair bir fikir edinmek için Bir Havayolunu Topraklayan İstisnayı Okuyun ). Her duruma (mutsuz olanlar dahil) uygun şekilde tepki veren bir kod yazmak yorucu ve zordur, ancak bunun nedeni, hatasız kod yazmanın sıkıcı ve zor olmasıdır, hata kodlarını ilettiğinizden değil.

Hem Raymond Chen hem de Joel , her şey için istisnaların kullanılmasına karşı bazı anlamlı tartışmalar yaptılar.


5
+1, bağlamın hata işleme stratejisiyle bir ilgisi olduğuna işaret ettiği için.
alx9r

2
@Tom, İyi noktalar, ancak istisnaların yakalanması garantilidir. Hata kodlarının yakalanmasını ve hatalar nedeniyle sessizce göz ardı edilmemesini nasıl sağlarız ?
Pacerier

13
Öyleyse, istisnalara karşı tek argümanınız, bir istisnayı yakalamayı unuttuğunuzda kötü bir şey olabileceğidir, ancak geri dönen değeri hatalar için kontrol etmeyi unutmanın aynı olasılık durumunu düşünmüyorsunuz? Bir hata kodunu kontrol etmeyi unuttuğunuzda hiçbir şey almadığınızda bir istisna yakalamayı unuttuğunuzda bir yığın izleme elde ettiğinizden bahsetmiyorum bile.
Esailija

3
C ++ 17 nodiscard, bir işlevin dönüş değeri depolanmazsa derleyici uyarısı verecek niteliği sunar. Unutulmuş hata kodu kontrolünü yakalamaya biraz yardımcı olur. Örnek: godbolt.org/g/6i6E0B
Zitrax

5
@ Esailija'nın yorumu tam olarak burada. Buradaki istisnalara karşı argüman, tüm hata kodlarının belgelendiği varsayımsal bir hata kodu tabanlı API ve dokümantasyonu okuyan, uygulamasında mantıksal olarak mümkün olan tüm hata durumlarını doğru bir şekilde tanımlayan ve her birini işlemek için kod yazan varsayımsal bir programcı alır. , ardından bu senaryoyu varsayımsal istisna tabanlı bir API ve programcı ile karşılaştırır, burada bir nedenden ötürü bu adımlardan biri yanlış gider ... istisna tabanlı bir API'de tüm bu adımları doğru bir şekilde almak eşit derecede kolay (tartışmalı olarak daha kolay ) olsa bile .
Mark Amery

62

Normalde istisnaları tercih ederim çünkü daha fazla bağlamsal bilgiye sahipler ve hatayı programcıya daha net bir şekilde aktarabilirler (doğru kullanıldığında).

Öte yandan, hata kodları istisnalardan daha hafiftir ancak bakımı daha zordur. Hata kontrolü yanlışlıkla ihmal edilebilir. Hata kodlarının bakımı daha zordur çünkü tüm hata kodlarını içeren bir katalog tutmanız ve ardından hangi hatanın atıldığını görmek için sonucu açmanız gerekir. Hata aralıkları burada yardımcı olabilir, çünkü ilgilendiğimiz tek şey bir hatanın varlığında olup olmadığımızsa, kontrol etmek daha kolaydır (örneğin, 0'a eşit veya daha büyük bir HRESULT hata kodu başarıdır ve sıfırdan küçük başarısızlıktır). Geliştiricinin hata kodlarını kontrol edeceği programlı bir zorlama olmadığından, yanlışlıkla ihmal edilebilirler. Öte yandan, istisnaları görmezden gelemezsiniz.

Özetlemek gerekirse, neredeyse tüm durumlarda hata kodlarına göre istisnaları tercih ederim.


4
"hata kodları istisnalardan daha hafiftir" neyi ölçtüğünüze ve nasıl ölçtüğünüze bağlıdır. İstisna tabanlı API'lerin çok daha hızlı olabileceğini gösteren testler bulmak oldukça kolaydır.
Mooing Duck

1
@smink, İyi noktalar, ancak istisnaların ek yükünü nasıl ele alacağız? Hata kodları sadece hafif değildir, temelde ağırlıksızdırlar ; istisnalar sadece orta ağırlıkta değil, yığın bilgileri ve zaten kullanmadığımız çeşitli şeyler içeren ağır nesnelerdir.
Pacerier

3
@Pacerier: Sadece istisnaların atıldığı testleri düşünüyorsunuz. C ++ istisnası atmanın hata kodu döndürmekten önemli ölçüde daha yavaş olduğu konusunda% 100 haklısınız. Eller aşağı, tartışma yok. Nereye do farklılık kodun diğer 99,999% 'dir. İstisnalar dışında, her ifade arasındaki hata kodu dönüşlerini kontrol etmemize gerek yoktur, bu da kodu% 1-50 daha hızlı hale getirir (veya derleyicinize bağlı olarak değil). Bu, kodun nasıl yazıldığına ve ne sıklıkla istisnaların atıldığına bağlı olarak tam kodun daha hızlı veya daha yavaş olabileceği anlamına gelir.
Mooing Duck

1
@Pacerier: Az önce yazdığım bu yapay test ile istisna tabanlı kod, GCC olmasa da MSVC ve Clang'daki hata kodları kadar hızlıdır: coliru.stacked-crooked.com/a/e81694e5c508945b (altta zamanlamalar). Kullandığım GCC bayraklarının diğer derleyicilere kıyasla alışılmadık derecede yavaş istisnalar oluşturduğu görülüyor. Önyargılıyım, bu yüzden lütfen testimi eleştirin ve başka bir varyantı denemekten çekinmeyin.
Mooing Duck

4
@Mooing Duck, Aynı anda hem hata kodlarını hem de istisnaları test ettiyseniz, istisnaları etkinleştirmiş olmanız gerekir. Durum böyleyse, sonuçlarınız, istisna işleme ek yükü olan hata kodlarını kullanmanın, yalnızca istisnaları kullanmaktan daha yavaş olmadığını gösterir. Anlamlı sonuçlar elde etmek için hata kodu testlerinin istisnalar devre dışı bırakılarak yürütülmesi gerekir.
Mika Haarahiltunen

24

İstisnaları tercih ederim çünkü

  • mantığın akışını kesintiye uğratırlar
  • daha fazla özellik / işlevsellik sağlayan sınıf hiyerarşisinden yararlanırlar
  • düzgün kullanıldığında geniş bir hata aralığını temsil edebilir (örneğin, bir InvalidMethodCallException da bir LogicException'tır, çünkü her ikisi de kodunuzda çalışma zamanından önce saptanması gereken bir hata olduğunda meydana gelir) ve
  • hatayı geliştirmek için kullanılabilirler (yani bir FileReadException sınıf tanımı daha sonra dosyanın var olup olmadığını veya kilitli olup olmadığını kontrol etmek için kod içerebilir)

2
Dördüncü noktanız adil değil: Bir nesneye dönüştürüldüğünde oluşan bir hata durumu, dosyanın var olup olmadığını veya kilitli olup olmadığını kontrol etmek için bir kod da içerebilir. Bu sadece stackoverflow.com/a/3157182/632951'in
Pacerier

1
"mantığın akışını kesintiye uğratırlar". İstisnaların aşağı yukarı etkileri vargoto
peterchaula

22

Hata kodları, işlevlerinizi arayanlar tarafından göz ardı edilebilir (ve genellikle!). İstisnalar, en azından onları hatayla bir şekilde ilgilenmeye zorlar. Bununla başa çıkma biçimleri boş bir yakalama işleyicisine (iç çekerek) sahip olmak olsa bile.


17

Hata kodlarında istisnalar, hiç şüphe yok. Hata kodlarında olduğu gibi istisnalardan da aynı faydaların çoğunu elde edersiniz, ancak çok daha fazlasını hata kodlarının eksiklikleri olmadan elde edersiniz. Tek istisnalar, biraz daha fazla masraflı olmasıdır; ancak günümüzde ve bu çağda, bu ek yük neredeyse tüm uygulamalar için ihmal edilebilir olarak kabul edilmelidir.

İşte iki tekniği tartışan, karşılaştıran ve karşılaştıran bazı makaleler:

Bunlarda size daha fazla bilgi verebilecek bazı iyi bağlantılar var.


16

İki modeli asla karıştırmam ... yığının hata kodları kullanan bir bölümünden istisnalar kullanan daha yüksek bir parçaya geçerken birinden diğerine dönüştürmek çok zor.

İstisnalar, "yöntemi veya alt yordamı, yapmasını istediğiniz şeyi yapmasını durduran veya engelleyen herhangi bir şey" içindir ... Düzensizlikler veya olağandışı durumlar veya sistemin durumu vb. Hakkındaki iletileri geri GÖNDERMEMEK İÇİN. Dönüş değerlerini veya ref kullanın (veya dışarı) bunun için parametreler.

İstisnalar, metodun fonksiyonuna bağlı anlambilim ile metotların yazılmasına (ve kullanılmasına) izin verir, yani bir Employee nesnesini döndüren bir metot veya Çalışanlar Listesi tam da bunu yapmak için yazılabilir ve onu arayarak kullanabilirsiniz.

Employee EmpOfMonth = GetEmployeeOfTheMonth();

Hata kodlarıyla, tüm yöntemler bir hata kodu döndürür; bu nedenle, çağıran kod tarafından kullanılmak üzere başka bir şey döndürmesi gerekenler için, bu verilerle doldurulacak bir referans değişkeni iletmeniz ve bunun için dönüş değerini test etmeniz gerekir. hata kodu ve her işlev veya yöntem çağrısında işleyin.

Employee EmpOfMonth; 
if (getEmployeeOfTheMonth(ref EmpOfMonth) == ERROR)
    // code to Handle the error here

Her yöntemin bir ve yalnızca tek bir basit şey yapacağı şekilde kodlarsanız, o zaman yöntem yöntemin istenen amacına ulaşamadığında bir istisna atmalısınız. İstisnalar, hata kodlarından çok daha zengin ve bu şekilde kullanımı daha kolaydır. Kodunuz çok daha temiz - "Normal" kod yolunun standart akışı, kesinlikle, yöntemin yapmasını istediğiniz şeyi başarabildiği duruma ayrılabilir ... Ve sonra kodun temizlenmesi veya işlenmesi Yöntemin başarıyla tamamlanmasını engelleyen kötü bir şey olduğunda "istisnai" durumlar normal koddan silinebilir. Ek olarak, istisnanın meydana geldiği yerde başa çıkamıyorsanız ve yığını bir UI'ye aktarmanız gerekiyorsa (veya daha kötüsü, orta kademe bir bileşenden bir UI'ye kablo üzerinden), o zaman istisna modeliyle,


Mükemmel cevap! Kutudan çıkar çıkmaz çözüm tam zamanında!
Cristian E.

11

Geçmişte hata kodu kampına katıldım (çok fazla C programlama yaptım). Ama şimdi ışığı gördüm.

Evet istisnalar sisteme biraz yük oluşturuyor. Ancak kodu basitleştirerek hata sayısını (ve WTF'leri) azaltırlar.

Öyleyse istisnayı kullanın ama akıllıca kullanın. Ve onlar senin arkadaşın olacaklar.

Yan not olarak. Hangi istisnanın hangi yöntemle atılabileceğini belgelemeyi öğrendim. Ne yazık ki bu çoğu dilde gerekli değildir. Ancak doğru istisnaları doğru seviyede ele alma şansını artırır.


1
yap C hepimizde bir kaç alışkanlık bırakır;)
Jorge Ferreira

11

İstisnaları temiz, açık ve doğru bir şekilde kullanmanın külfetli olduğu birkaç durum olabilir, ancak zaman istisnalarının büyük çoğunluğu bariz bir seçimdir. İstisna işlemenin hata kodlarından daha büyük yararı, iki nedenden dolayı önemli olan yürütme akışını değiştirmesidir.

Bir istisna meydana geldiğinde, uygulama artık 'normal' yürütme yolunu izlemiyor. Bunun bu kadar önemli olmasının ilk nedeni, kodun yazarı kötü olmak için iyi ve gerçekten yoldan çıkmadıkça, programın durması ve öngörülemeyen şeyler yapmaya devam etmemesidir. Bir hata kodu kontrol edilmezse ve hatalı bir hata koduna yanıt olarak uygun işlemler yapılmazsa, program yaptığı şeyi yapmaya devam eder ve bu eylemin sonucunun ne olacağını kim bilebilir. Programın 'her şeyi' yapmasını sağlamanın çok pahalı olacağı birçok durum vardır. Bir şirketin sattığı çeşitli finansal araçlar için performans bilgilerini alan ve bu bilgileri komisyonculara / toptancılara ileten bir program düşünün. Bir şeyler ters giderse ve program devam ederse, hatalı performans verilerini komisyonculara ve toptancılara gönderebilir. Başka kimseyi bilmiyorum, ancak benim kodumun neden şirketin 7 haneli yasal para cezaları almasına neden olduğunu açıklayan bir Başkan Yardımcısı ofisinde oturan kişi olmak istemiyorum. Müşterilere bir hata mesajı vermek, genellikle 'gerçek' gibi görünebilecek yanlış verilerin teslim edilmesine tercih edilir ve ikinci durum, hata kodları gibi çok daha az agresif bir yaklaşımla karşılaşılması çok daha kolaydır.

İstisnaları ve normal yürütmeyi bozmalarını sevmemin ikinci nedeni, 'normal şeyler oluyor' mantığını 'bir şeyler ters gitti mantığından' ayrı tutmayı çok çok daha kolay hale getirmesidir. Bana göre bu:

try {
    // Normal things are happening logic
catch (// A problem) {
    // Something went wrong logic
}

... buna tercih edilir:

// Some normal stuff logic
if (errorCode means error) {
    // Some stuff went wrong logic
}
// Some normal stuff logic
if (errorCode means error) {
    // Some stuff went wrong logic
}
// Some normal stuff logic
if (errorCode means error) {
    // Some stuff went wrong logic
}

İstisnalar hakkında hoş olan başka küçük şeyler de var. Bir işlevde çağrılan yöntemlerden herhangi birinin bir hata kodunun döndürülüp döndürülmediğini takip etmek için bir dizi koşullu mantığa sahip olmak ve bu hata kodunu daha yukarıya döndürmek çok fazla kazan plakasıdır. Aslında, yanlış gidebilecek çok fazla kazan plakası. Çoğu dilin istisna sistemine inancım, Fred'in yazdığı 'Üniversiteden yeni çıkmış' diye yazdığı `` eğer-değilse-eğer-fare '' ifadelerinden çok daha fazla inancım var ve yapacak çok daha iyi işlerim var kodun gözden geçirilmesinden daha çok zamanımla, dedi sıçan yuvasını gözden geçirdim.


8

İkisini de kullanmalısın. Önemli olan her birinin ne zaman kullanılacağına karar vermektir .

Bir vardır istisnalar bariz bir seçimdir birkaç senaryo :

  1. Bazı durumlarda , hata koduyla hiçbir şey yapamazsınız ve bunu çağrı yığınında daha üst bir düzeyde işlemeniz gerekir , genellikle yalnızca hatayı günlüğe kaydedin , kullanıcıya bir şey görüntüleyin veya programı kapatın. Bu gibi durumlarda, hata kodları, hata kodlarını manuel olarak seviye bazında kabarcıklandırmanızı gerektirir; bu, istisnalarla yapılması çok daha kolaydır. Mesele şu ki, bu beklenmedik ve başa çıkılamaz durumlar için.

  2. Yine de 1. durumla ilgili (beklenmedik ve idare edilemeyen bir şey olduğunda, sadece günlüğe kaydetmek istemezsiniz), istisnalar yardımcı olabilir çünkü bağlamsal bilgi ekleyebilirsiniz . Örneğin, alt düzey veri yardımcılarımda bir SqlException alırsam, bu hatayı düşük düzeyde (hataya neden olan SQL komutunu bildiğim yerde) yakalamak isteyeceğim , böylece bu bilgileri yakalayabilir ve ek bilgilerle yeniden atabilirim . Lütfen buradaki sihirli kelimeye dikkat edin: yeniden atın ve yutmayın . İstisna işlemenin ilk kuralı: istisnaları yutmayın . Ayrıca, içteki yakalamanın hiçbir şeyi kaydetmesine gerek olmadığını unutmayın çünkü dış yakalama tüm yığın izine sahip olacak ve bunu kaydedebilir.

  3. Bazı durumlarda bir dizi komutunuz vardır ve bunlardan herhangi biri başarısız olursa , bu kurtarılamaz bir durum (atılmalıdır) veya kurtarılabilir bir durum (bu durumda yapabilirsiniz) olsun veya olmasın kaynakları temizlemeniz / imha etmeniz gerekir (*). yerel olarak veya arayan kodunda ele alın ancak istisnalara ihtiyacınız yoktur). Açıkçası, her yöntemden sonra hata kodlarını test etmek yerine tüm bu komutları tek bir denemede koymak ve nihayet bloğunda temizlemek / imha etmek çok daha kolaydır. Lütfen hatanın ortaya çıkmasını istiyorsanız (ki muhtemelen istediğiniz şey budur), onu yakalamanıza bile gerek olmadığını unutmayın - sadece temizlemek / imha etmek için sonunu kullanın - sadece isterseniz catch / retrow'u kullanmalısınız bağlamsal bilgi eklemek için (bkz. madde 2).

    Bir örnek, bir işlem bloğunun içindeki SQL ifadeleri dizisi olabilir. Yine, bu aynı zamanda "başa çıkılamaz" bir durum, erken yakalamaya karar verseniz bile (en üste köpürmek yerine yerel olarak tedavi edin), en iyi sonucun her şeyi iptal etmek veya en azından büyük bir kısmı iptal etmek olduğu durumlarda hala ölümcül bir durumdur . sürecin bir parçası.
    (*) Bu, on error gotoeski Visual Basic'te kullandığımız gibidir

  4. Yapıcılarda yalnızca istisnalar atabilirsiniz.

Arayanın bazı işlemler yapabileceği / GEREKTİĞİ bazı bilgileri döndürdüğünüz diğer tüm durumlarda , dönüş kodlarını kullanmak muhtemelen daha iyi bir alternatiftir. Bu, beklenen tüm "hataları" içerir , çünkü muhtemelen hemen arayan tarafından ele alınmaları gerekir ve yığında çok fazla seviye yukarı çıkmasına gerek kalmaz.

Elbette, beklenen hataları istisna olarak ele almak ve hemen bir seviye yukarıdan yakalamak her zaman mümkündür ve ayrıca bir deneme yakalamasında her kod satırını kapsamak ve olası her hata için eylemler gerçekleştirmek de mümkündür. IMO, bu kötü bir tasarım, sadece çok daha ayrıntılı olduğu için değil, özellikle kaynak kodu okunmadan ortaya çıkabilecek olası istisnalar açık olmadığı için - ve herhangi bir derin yöntemden istisnalar atılarak görünmez gotoslar yaratılabileceği için . Kodun okunmasını ve incelenmesini zorlaştıran birden çok görünmez çıkış noktası oluşturarak kod yapısını bozarlar. Başka bir deyişle, akış kontrolü olarak istisnaları asla kullanmamalısınız, çünkü bunu başkalarının anlaması ve sürdürmesi zor olacaktır. Test için olası tüm kod akışlarını anlamak bile zor olabilir.
Tekrar: Doğru temizleme / imha için, hiçbir şey yakalamadan try-final'i kullanabilirsiniz .

Dönüş kodlarıyla ilgili en popüler eleştiri, "birisi hata kodlarını görmezden gelebilir, ancak aynı anlamda birileri istisnaları da yutabilir. Kötü istisnaları ele almak her iki yöntemde de kolaydır . Ancak iyi hata kodu tabanlı program yazmak hala çok daha kolaydır. İstisnaya dayalı bir program yazmaktansa ve herhangi bir nedenle tüm hataları (eski olanlar on error resume next) görmezden gelmeye karar verirse , bunu dönüş kodlarıyla kolayca yapabilirsiniz ve bunu çok fazla deneme yakalama şablonu olmadan yapamazsınız.

Dönüş kodlarıyla ilgili en popüler ikinci eleştiri, "ortaya çıkmasının zor olmasıdır" - ancak bunun nedeni, insanların istisnaların kurtarılamaz durumlar için olduğunu, hata kodlarının ise olmadığını anlamadıklarıdır.

İstisnalar ve hata kodları arasında karar vermek gri bir alandır. Hatta bazı yeniden kullanılabilir iş yöntemlerinden bir hata kodu almanız gerekebilir ve sonra bunu bir istisnaya (muhtemelen bilgi ekleyerek) sarmaya karar verip kabarmasına izin verin. Ancak TÜM hataların istisna olarak atılması gerektiğini varsaymak bir tasarım hatasıdır.

Özetlersek:

  • Beklenmedik bir durumla karşılaştığımda, yapacak pek bir şey olmadığında ve genellikle büyük bir kod bloğunu veya hatta tüm işlemi veya programı iptal etmek istediğimizde istisnalar kullanmayı seviyorum. Bu, eski "hata durumunda gitme" gibidir.

  • Arayan kodunun bazı eylemler yapabileceği / yapması gereken beklenen durumlar olduğunda dönüş kodlarını kullanmayı seviyorum. Bu, çoğu iş yöntemini, API'leri, doğrulamaları vb. İçerir.

İstisnalar ve hata kodları arasındaki bu fark, normal beklenen durumlar hata olarak döndürülürken, ölümcül beklenmedik durumlar için "panik" kullanan GO dilinin tasarım ilkelerinden biridir.

Yine de GO ile ilgili olarak , aynı anda bir hata veya başka bir şey döndürebileceğiniz için, dönüş kodlarının kullanılmasında çok yardımcı olan bir şey olan çoklu dönüş değerlerine de izin verir . C # / Java'da bunu parametreler, Tuple'lar veya (en sevdiğim) Generics olmadan başarabiliriz, bunlar numaralandırmalarla birleştirildiğinde arayanlara açık hata kodları sağlayabilir:

public MethodResult<CreateOrderResultCodeEnum, Order> CreateOrder(CreateOrderOptions options)
{
    ....
    return MethodResult<CreateOrderResultCodeEnum>.CreateError(CreateOrderResultCodeEnum.NO_DELIVERY_AVAILABLE, "There is no delivery service in your area");

    ...
    return MethodResult<CreateOrderResultCodeEnum>.CreateSuccess(CreateOrderResultCodeEnum.SUCCESS, order);
}

var result = CreateOrder(options);
if (result.ResultCode == CreateOrderResultCodeEnum.OUT_OF_STOCK)
    // do something
else if (result.ResultCode == CreateOrderResultCodeEnum.SUCCESS)
    order = result.Entity; // etc...

Yöntemime yeni bir olası dönüş eklersem, tüm arayanların, örneğin bir switch ifadesinde bu yeni değeri kapsayıp kapsamadığını bile kontrol edebilirim. Bunu istisnalar dışında gerçekten yapamazsınız. Dönüş kodlarını kullandığınızda, genellikle olası tüm hataları önceden bilir ve bunları test edersiniz. İstisnalar dışında, genellikle ne olabileceğini bilemezsiniz. Numaralandırmaları istisnaların içine sarmak (Jenerikler yerine) bir alternatiftir (her yöntemin atacağı istisnaların türü açık olduğu sürece), ancak IMO yine de kötü bir tasarımdır.


4

Benim mantığım, gerçekten performansa ihtiyaç duyan düşük seviyeli bir sürücü yazıyorsanız, o zaman hata kodlarını kullanın. Ancak bu kodu daha üst düzey bir uygulamada kullanıyorsanız ve biraz ek yükü kaldırabilirse, o kodu bu hata kodlarını kontrol eden ve istisnaları artıran bir arayüzle sarın.

Diğer tüm durumlarda, istisnalar muhtemelen gidilecek yoldur.


4

Burada çitin üzerinde oturuyor olabilirim, ama ...

  1. Dile bağlıdır.
  2. Hangi modeli seçerseniz seçin, onu nasıl kullandığınız konusunda tutarlı olun.

Python'da, istisnaların kullanımı standart bir uygulamadır ve kendi istisnalarımı tanımlamaktan oldukça memnunum. C'de hiçbir istisnanız yoktur.

C ++ 'da (en azından STL'de), istisnalar tipik olarak yalnızca gerçekten istisnai hatalar için atılır (onları neredeyse hiç görmüyorum). Kendi kodumda farklı bir şey yapmak için bir neden göremiyorum. Evet, dönüş değerlerini göz ardı etmek kolaydır, ancak C ++ sizi istisnaları yakalamaya da zorlamaz. Bence bunu yapma alışkanlığı kazanman gerekiyor.

Üzerinde çalıştığım kod tabanı çoğunlukla C ++ ve hemen hemen her yerde hata kodları kullanıyoruz, ancak çok istisnai olmayanlar da dahil olmak üzere herhangi bir hata için istisna yaratan bir modül var ve bu modülü kullanan tüm kodlar oldukça korkunç. Ancak bunun nedeni istisnaları ve hata kodlarını karıştırmış olmamız olabilir. Sürekli olarak hata kodlarını kullanan kodla çalışmak çok daha kolaydır. Kodumuz sürekli olarak istisnalar kullansaydı, belki o kadar kötü olmazdı. İkisini karıştırmak pek iyi sonuç vermiyor.


4

C ++ ile çalıştığım ve kullanımlarını güvenli hale getirmek için RAII'ye sahip olduğum için neredeyse sadece istisnalar kullanıyorum. Hata işlemeyi normal program akışından çıkarır ve amacı daha net hale getirir.

Yine de istisnai durumlar için istisnalar bırakıyorum. Belli bir hatanın çok sık olmasını bekliyorsam, işlemi gerçekleştirmeden önce işlemin başarılı olup olmayacağını kontrol ederim veya bunun yerine hata kodlarını kullanan bir işlev sürümünü çağırırım (Beğen TryParse())


3

Yöntem imzaları, yöntemin ne yaptığını size bildirmelidir. Uzun errorCode = getErrorCode () gibi bir şey; iyi olabilir, ancak uzun errorCode = fetchRecord (); Kafa karıştırıcı.


3

Benim yaklaşımım, her ikisini de, yani İstisnalar ve Hata kodlarını aynı anda kullanabilmemizdir.

Birkaç istisna türü (örn: DataValidationException veya ProcessInterruptExcepion) tanımlamaya alışkınım ve her istisnanın içinde her sorunun daha ayrıntılı bir açıklamasını tanımlıyorum.

Java'da Basit Bir Örnek:

public class DataValidationException extends Exception {


    private DataValidation error;

    /**
     * 
     */
    DataValidationException(DataValidation dataValidation) {
        super();
        this.error = dataValidation;
    }


}

enum DataValidation{

    TOO_SMALL(1,"The input is too small"),

    TOO_LARGE(2,"The input is too large");


    private DataValidation(int code, String input) {
        this.input = input;
        this.code = code;
    }

    private String input;

    private int code;

}

Bu şekilde kategori hatalarını tanımlamak için İstisnaları ve sorunla ilgili daha ayrıntılı bilgileri tanımlamak için hata kodlarını kullanıyorum.


2
erm ... throw new DataValidationException("The input is too small")? İstisnaların avantajlarından biri ayrıntılı bilgiye izin vermektir.
Eva

2

İstisnalar, istisnai durumlar içindir - yani, kodun normal akışının parçası olmadıklarında.

Hata kodlarının kendi başına kodun çalışmasındaki bir hatadan ziyade bir şeyin durumunu temsil ettiği İstisnaları ve hata kodlarını karıştırmak oldukça meşrudur (örneğin, bir çocuk işlemden dönüş kodunu kontrol etmek).

Ancak istisnai bir durum ortaya çıktığında, İstisnaların en etkileyici model olduğuna inanıyorum.

İstisnalar yerine hata kodları kullanmayı tercih edebileceğiniz veya sahip olabileceğiniz durumlar vardır ve bunlar zaten yeterince ele alınmıştır (derleyici desteği gibi diğer bariz kısıtlamalar dışında).

Ancak diğer yöne gitmek, İstisnaları kullanmak, hata işlemeniz için daha yüksek seviyeli soyutlamalar oluşturmanıza olanak tanır ve bu da kodunuzu daha da anlamlı ve doğal hale getirebilir. C ++ uzmanı Andrei Alexandrescu'nun "Yaptırımlar" dediği konu hakkındaki bu mükemmel, ancak hafife alınmış makalesini okumanızı şiddetle tavsiye ederim: http://www.ddj.com/cpp/184403864 . Bir C ++ makalesi olmasına rağmen ilkeler genel olarak uygulanabilir ve yaptırımlar kavramını oldukça başarılı bir şekilde C # 'a çevirdim.


2

İlk olarak, Tom'un , Servis Odaklı Mimari (SOA) olmadığı sürece, yüksek seviyeli malzeme istisnaları ve düşük seviyeli şeyler için hata kodları kullanıldığı şeklindeki cevabına katılıyorum .

Yöntemlerin farklı makinelerde çağrılabildiği SOA'da istisnalar tel üzerinden geçilemeyebilir, bunun yerine aşağıdaki gibi bir yapıya sahip başarı / başarısızlık yanıtlarını kullanırız (C #):

public class ServiceResponse
{
    public bool IsSuccess => string.IsNullOrEmpty(this.ErrorMessage);

    public string ErrorMessage { get; set; }
}

public class ServiceResponse<TResult> : ServiceResponse
{
    public TResult Result { get; set; }
}

Ve bunun gibi kullanın:

public async Task<ServiceResponse<string>> GetUserName(Guid userId)
{
    var response = await this.GetUser(userId);
    if (!response.IsSuccess) return new ServiceResponse<string>
    {
        ErrorMessage = $"Failed to get user."
    };
    return new ServiceResponse<string>
    {
        Result = user.Name
    };
}

Bunlar, hizmet yanıtlarınızda tutarlı bir şekilde kullanıldığında, uygulamadaki başarı / başarısızlıkları ele almak için çok güzel bir model oluşturur. Bu, hizmetler içinde ve hizmetler arasında eşzamansız çağrılarda daha kolay hata işlemeye izin verir.


1

İlkel bir veri türü döndüren bir işlevin beklenen hatasız bir sonucu olması dışında, tüm hata durumları için İstisnaları tercih ederim. Örneğin, daha büyük bir dizge içindeki bir alt dizenin dizinini bulmak, genellikle bir NotFoundException oluşturmak yerine -1 döndürür.

Referansı kaldırılabilecek geçersiz işaretçilerin döndürülmesi (örneğin, Java'da NullPointerException'a neden olmak) kabul edilemez.

İstemciler "<0" yerine "== -1" denetimi yapabileceğinden, aynı işlev için dönüş değerleri olarak birden çok farklı sayısal hata kodu (-1, -2) kullanmak genellikle kötü bir stildir.

Burada akılda tutulması gereken bir şey, API'lerin zaman içindeki evrimidir. İyi bir API, istemcileri bozmadan hata davranışını çeşitli şekillerde değiştirmeye ve genişletmeye izin verir. Örneğin, bir istemci hata işleyicisi 4 hata durumunu kontrol ettiyse ve işlevinize beşinci bir hata değeri eklerseniz, istemci işleyicisi bunu test edip bozabilir. İstisnaları yükseltirseniz, bu genellikle istemcilerin bir kitaplığın daha yeni bir sürümüne geçmesini kolaylaştırır.

Göz önünde bulundurulması gereken bir diğer husus, bir takımda çalışırken, tüm geliştiricilerin böyle bir karar vermesi için net bir çizgi çizilmesi gerektiğidir. Örneğin, "Üst düzey şeyler için istisnalar, düşük düzey şeyler için hata kodları" çok özneldir.

Her durumda, birden fazla önemsiz hata türünün mümkün olduğu durumlarda, kaynak kodu hiçbir zaman bir hata kodu döndürmek veya işlemek için sayısal değişmezi kullanmamalıdır (dönüş -7, eğer x == -7 ...), ancak her zaman adlandırılmış bir sabit (x == NO_SUCH_FOO ise NO_SUCH_FOO döndür).


1

Büyük proje altında çalışıyorsanız, sadece istisnaları veya sadece hata kodlarını kullanamazsınız. Farklı durumlarda farklı yaklaşımlar kullanmalısınız.

Örneğin, yalnızca istisnaları kullanmaya karar veriyorsunuz. Ancak, eşzamansız olay işlemeyi kullanmaya karar verdiğinizde. Bu durumlarda hata işleme için istisnalar kullanmak kötü bir fikirdir. Ancak uygulamanın her yerinde hata kodları kullanmak sıkıcıdır.

Bu yüzden, hem istisnaları hem de hata kodlarını aynı anda kullanmanın normal olduğunu düşünüyorum.


0

Çoğu uygulama için istisnalar daha iyidir. Bunun istisnası, yazılımın diğer cihazlarla iletişim kurması gerektiğidir. Çalıştığım alan endüstriyel kontroller. Burada hata kodları tercih edilir ve beklenir. Yani cevabım duruma bağlı olduğu.


0

Sonuçtan yığın izleme gibi bilgilere gerçekten ihtiyacınız olup olmadığına da bağlı olduğunu düşünüyorum. Cevabınız evet ise, kesinlikle sorunla ilgili birçok bilgi içeren nesnenin bulunduğu İstisna'yı seçersiniz. Ancak, sadece sonuçla ilgileniyorsanız ve bu sonucun nedenini umursamıyorsanız, o zaman hata kodunu arayın.

Örneğin, dosyayı işlerken ve IOException ile yüzleşirken, istemci bunun nereden tetiklendiğini bilmekle ilgilenebilir, dosya açma veya dosya ayrıştırma vb. Bu nedenle IOException veya belirli alt sınıfını döndürmeniz daha iyi olur. Ancak, oturum açma yönteminiz olduğu ve bunun başarılı olup olmadığını bilmek istediğiniz gibi senaryolar, orada sadece boole döndürürsünüz veya doğru mesajı göstererek hata kodunu döndürürsünüz. Burada Müşteri, mantığın hangi kısmının bu hata koduna neden olduğunu bilmekle ilgilenmez. Sadece kimlik bilgilerinin geçersiz olup olmadığını veya hesap kilidinin vs. olduğunu biliyor.

Aklıma gelen başka bir kullanım durumu, verilerin ağ üzerinde dolaşmasıdır. Uzak yönteminiz, veri aktarımını en aza indirmek için İstisna yerine yalnızca hata kodu döndürebilir.


0

Genel kuralım:

  • Bir işlevde yalnızca bir hata görünebilir: hata kodunu kullanın (işlevin parametresi olarak)
  • Birden fazla belirli hata görünebilir: istisna at

-1

Yönteminiz sayısal bir değerden başka bir şey döndürdüğünde hata kodları da çalışmaz ...


3
Hayır. Win32 GetLastError () paradigmasına bakın. Onu savunmuyorum, sadece yanlış olduğunu söylüyorum.
Tim

4
Aslında bunu yapmanın birçok yolu var. Diğer bir yol, hata kodunu ve gerçek dönüş değerini içeren bir nesneyi döndürmektir. Yine başka bir yol da referans geçişi yapmaktır.
Pacerier
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.