Neden istisnaları düzenli kontrol akışı olarak kullanmıyorsunuz?


193

Google'da kullanabileceğim tüm standart yanıtlardan kaçınmak için, hepinizin istediği zaman saldırabileceğiniz bir örnek vereceğim.

C # ve Java (ve diğer pek çok) hiç sevmiyorum 'taşma' davranış bazı türleri bol var (örneğin type.MaxValue + type.SmallestValue == type.MinValue: örneğin int.MaxValue + 1 == int.MinValue).

Ancak, kısır doğamı gördüğümde, bu davranışı genişleterek bu yaralanmaya biraz hakaret edeceğim, diyelim ki Geçersiz kılınmış bir DateTimetip. (Ben DateTime.NET mühürlü olduğunu biliyorum , ama bu örnek uğruna, ben DateTime mühürlenmemiş olması dışında, tam olarak C # gibi bir sahte dil kullanıyorum).

Geçersiz kılınan Addyöntem:

/// <summary>
/// Increments this date with a timespan, but loops when
/// the maximum value for datetime is exceeded.
/// </summary>
/// <param name="ts">The timespan to (try to) add</param>
/// <returns>The Date, incremented with the given timespan. 
/// If DateTime.MaxValue is exceeded, the sum wil 'overflow' and 
/// continue from DateTime.MinValue. 
/// </returns>
public DateTime override Add(TimeSpan ts) 
{
    try
    {                
        return base.Add(ts);
    }
    catch (ArgumentOutOfRangeException nb)
    {
        // calculate how much the MaxValue is exceeded
        // regular program flow
        TimeSpan saldo = ts - (base.MaxValue - this);
        return DateTime.MinValue.Add(saldo)                         
    }
    catch(Exception anyOther) 
    {
        // 'real' exception handling.
    }
}

Tabii ki bir if bu kadar kolay çözebilir, ancak gerçek şu ki neden istisnaları kullanamadığınızı göremiyorum (mantıklı olarak, performans belirli durumlarda istisnalardan kaçınılması gereken bir sorun olduğunda görebiliyorum ).

Ben birçok durumda if-yapılar daha açıktır ve yöntemin herhangi bir sözleşme kırmak değil düşünüyorum.

IMHO, “Bunları asla düzenli program akışı için kullanmayın” reaksiyonu, herkesin sahip olduğu görünüşte, bu reaksiyonun gücü haklı çıkarabileceği kadar iyi bir yapıya sahip değildir.

Yoksa yanılıyor muyum?

Her türlü özel durumla ilgilenen diğer yazıları okudum, ama benim açımdan, ikiniz de yanlış bir şey yok:

  1. Açık
  2. Yönteminizin sözleşmesini onurlandırın

Vur beni.


2
+1 Aynı şekilde hissediyorum. Performansın yanı sıra, kontrol akışı için istisnaları önlemenin tek iyi nedeni, arayan kodunun dönüş değerleriyle çok daha okunabilir olmasıdır.

4
: dönüş -1 bir şey olursa, dönüş -2 başka bir şey, vb ... istisnalar sonra gerçekten daha okunabilir?
kender

1
Birinin doğruyu söylemekle olumsuz bir şöhret kazanması üzücü: Örneğinizin if ifadeleriyle birlikte yazılamayacağı. (Bu doğru / eksiksiz olduğu anlamına gelmez.)
Ingo

8
Bir istisna atmanın bazen tek seçeneğiniz olabileceğini iddia ediyorum. Örneğin, veritabanını sorgulayarak kendi kurucu içindeki iç durumunu başlatan bir iş bileşeni var. Veritabanında uygun veri bulunmadığı zamanlar vardır. Yapıcı içinde bir istisna atmak, nesnenin yapımını etkili bir şekilde iptal etmenin tek yoludur. Bu açıkça sınıfın sözleşmesinde (benim durumumda Javadoc) belirtilir, bu yüzden bileşen oluştururken ve oradan devam ederken istemci kodu bu istisna yakalamak olabilir (ve gerekir) hiçbir sorun var.
Stefan Haberl

1
Bir hipotez formüle ettiğiniz için, doğrulayıcı kanıtları / nedenleri belirtmek sizin üzerinizdedir. Yeni başlayanlar için, kodunuzun çok daha kısa, kendi kendini belgeleyen bir ifadeden üstün olmasının bir nedenini ifbelirtin. Bunu çok zor bulacaksınız. Başka bir deyişle: öncülünüz kusurludur ve bundan çıkardığınız sonuçlar yanlıştır.
Konrad Rudolph

Yanıtlar:


171

Normal çalışma sırasında saniyede beş istisna oluşturan bir programda hata ayıklamaya çalıştınız mı?

Sahibim.

Program oldukça karmaşıktı (dağıtılmış bir hesaplama sunucusuydu) ve programın bir tarafında küçük bir değişiklik kolayca farklı bir yerde bir şeyi kırabilir.

Keşke programı yeni başlatabilir ve istisnaların gerçekleşmesini bekleyebilseydim, ancak normal operasyon sırasında başlangıç ​​sırasında yaklaşık 200 istisna vardı

Benim açımdan: normal durumlar için istisnalar kullanırsanız, olağandışı (yani istisna al) durumları nasıl bulursunuz?

Tabii ki, istisnaları çok fazla kullanmamanın başka güçlü nedenleri var, özellikle performans açısından


13
Örnek: Bir .net programında hata ayıkladığımda, görsel stüdyodan başlattım ve VS'den tüm istisnaları kırmasını istiyorum. Beklenen bir davranış olarak istisnalara güveniyorsanız, artık (5tim / sn'yi kıracağından) bunu yapamam ve kodun sorunlu kısmını bulmak çok daha karmaşık.
Brann

15
+1, gerçek istisnai bir iğne bulmak için bir istisna samanlığı oluşturmak istemediğinizi belirtmek için.
Grant Wagner

16
Bu cevabı hiç anlamadım, sanırım insanlar burada yanlış anlıyorlar Hata ayıklama ile hiç bir ilgisi yok, tasarımla. Bu, korkuyorum, püresi biçiminde dairesel bir akıl yürütmedir. Ve daha da önemlisi, daha önce belirtildiği gibi sorunun yanı sıra
Peter

11
@Peter: İstisnaları bozmadan hata ayıklamak zordur ve tasarımla çok fazla varsa tüm istisnaları yakalamak acı vericidir. Hata ayıklamayı zorlaştıran bir tasarımın neredeyse kısmen kırıldığını düşünüyorum (başka bir deyişle, tasarım hata ayıklama ile ilgili bir şey var, IMO)
Brann

7
Hata ayıklamak istediğim çoğu durumun atılan istisnalara karşılık gelmediğini bile göz ardı ederseniz, sorunuzun cevabı şöyledir: "türüne göre", örneğin, hata ayıklayıcıma yalnızca AssertionError veya StandardError veya bunu yapan bir şey kötü şeylere karşılık gelir. Bununla ilgili sorun yaşıyorsanız, günlük kaydını nasıl yaparsınız - seviyeye ve sınıfa göre oturum açmazsınız, bu yüzden onlara filtre uygulayabilirsiniz? Sizce bu da kötü bir fikir mi?
Ken

158

İstisnalar, temelde yerel olmayan gotoifadelerdir ve ikincisinin tüm sonuçlarıyla birlikte. Akış denetimi için istisnalar kullanmak , en az şaşkınlık ilkesini ihlal eder , programları okumayı zorlaştırır (programların önce programcılar için yazıldığını unutmayın).

Ayrıca, derleyici satıcılarının beklediği bu değildir. İstisnaların nadiren atılmasını beklerler ve genellikle throwkodun oldukça verimsiz olmasına izin verirler . İstisnaları atmak .NET'teki en pahalı işlemlerden biridir.

Bununla birlikte, bazı diller (özellikle Python) akış kontrol yapıları olarak istisnalar kullanır. Örneğin, StopIterationbaşka öğe yoksa yineleyiciler bir istisna oluşturur. Standart dil yapıları bile (örneğin for) buna güvenir.


11
Hey, istisnalar şaşırtıcı değil! Ve "bu kötü bir fikir" dediğinde kendinle çelişiyorsun ve sonra "ama python'da iyi bir fikir" demeye devam ediyorsun.
hasen

6
Hala ikna olmadım: 1) Verimlilik sorunun yanı sıra, çok sayıda bacht olmayan program daha az umursamazdı (örn. Kullanıcı arayüzü) 2) Şaşırtıcı: Dediğim gibi sadece şaşırtıcı neden kullanılmıyor, ama soru kalıyor: neden ilk etapta id kullanmıyorsunuz? Ancak, bu cevap olduğu için
Peter

4
+1 Aslında Python ve C # arasındaki farkı belirttiğinize sevindim. Bunun bir çelişki olduğunu düşünmüyorum. Python çok daha dinamiktir ve istisnaları bu şekilde kullanma beklentisi dile dönüştürülür. Aynı zamanda Python'un EAFP kültürünün bir parçasıdır. Hangi yaklaşımın kavramsal olarak daha saf veya daha öz-tutarlı olduğunu bilmiyorum, ancak başkalarının yapmasını beklediğini yapan kod yazma fikrinden hoşlanıyorum , bu da farklı dillerde farklı stiller anlamına geliyor.
Mark E. Haase

13
Aksine goto, elbette, doğru etkileşim çağrı yığını ve sözcük kapsama alınması ile ve istisnalar yığını bırakmayın ya da karmaşa içinde kapsamları.
Lukas Eder

4
Aslında, çoğu VM satıcısı istisnalar bekler ve bunları etkin bir şekilde ele alır. @LukasEder'in belirttiği gibi, istisnalar yapılandırılmış olmalarından dolayı tamamen goto'dan farklıdır.
Marcin

30

Temel kuralım:

  • Bir hatadan kurtarmak için herhangi bir şey yapabiliyorsanız, istisnaları yakalayın
  • Hata çok yaygınsa (ör. Kullanıcı yanlış parola ile giriş yapmaya çalıştı), iade değerlerini kullanın
  • Bir hatadan kurtarmak için hiçbir şey yapamazsanız, yakalanmamış olarak bırakın (Ya da uygulamanın yarı zarif bir şekilde kapatılması için ana yakalayıcınızda yakalayın)

İstisnalar ile gördüğüm sorun tamamen sözdizimi açısından (perfomance yükü en az olduğundan eminim). Her yerde denemeyi sevmiyorum.

Bu örneği ele alalım:

try
{
   DoSomeMethod();  //Can throw Exception1
   DoSomeOtherMethod();  //Can throw Exception1 and Exception2
}
catch(Exception1)
{
   //Okay something messed up, but is it SomeMethod or SomeOtherMethod?
}

.. Başka bir örnek, bir fabrika kullanarak tanıtıcıya bir şey atamanız gerektiğinde olabilir ve bu fabrika bir istisna atabilir:

Class1 myInstance;
try
{
   myInstance = Class1Factory.Build();
}
catch(SomeException)
{
   // Couldn't instantiate class, do something else..
}
myInstance.BestMethodEver();   // Will throw a compile-time error, saying that myInstance is uninitalized, which it potentially is.. :(

Soo, kişisel olarak, nadir hata durumları (bellek dışı vb.) İçin istisnalar tutmanız ve bunun yerine hata kontrolünüzü yapmak için geri dönüş değerlerini (değerler, yapılar veya numaralar) kullanmanız gerektiğini düşünüyorum.

Umarım sorunuzu doğru anladım :)


4
re: İkinci örneğiniz - neden sadece BestMethodEver çağrısını Build bloğunun içine, Build'dan sonra koymuyorsunuz? Build () bir istisna atarsa, yürütülmez ve derleyici mutlu olur.
Blorgbeard

2
Evet, muhtemelen bununla sonuçlanacaksınız, ancak myInstance türünün kendisinin istisnalar atabileceği daha karmaşık bir örnek düşünün. Ve yöntem kapsamındaki diğer örnekler de olabilir. Bir sürü iç içe geçmiş try / catch bloğu ile
sonuçlanacaksınız

Yakalama bloğunuzda İstisna çevirisi yapmalısınız (soyutlama düzeyine uygun bir İstisna türüne). Bilginize: "Multi-catch" Java 7'ye
giriyor

Bilginize: C ++ 'da, farklı istisnaları yakalamaya çalıştıktan sonra birden çok yakalama koyabilirsiniz.
RobH

2
Shrinkwrap yazılımı için tüm istisnaları yakalamanız gerekir. En azından programın kapatılması gerektiğini açıklayan bir iletişim kutusu açın ve işte bir hata raporunda gönderebileceğiniz anlaşılmaz bir şey.
David Thornley

25

Birçok cevaba ilk tepki:

programcılar ve en az şaşkınlık ilkesi için yazıyorsunuz

Elbette! Ama bir if her zaman daha açık değildir.

Şaşırtıcı olmamalı, örneğin: divide (1 / x) catch (divisionByZero) bana göre (Conrad ve diğerlerinde) daha net. Bu tür bir programlamanın beklenmediği gerçeği tamamen gelenekseldir ve gerçekten de geçerlidir. Belki benim örneğimde bir if daha net olurdu.

Ancak DivisionByZero ve FileNotFound bu konu için ifs daha net.

Tabii ki daha az performans ve saniyede milyonlarca zaman gerekiyorsa, elbette bundan kaçınmalısınız, ancak genel tasarımdan kaçınmak için hala iyi bir neden okumadım.

En az şaşkınlık ilkesine göre: burada dairesel bir akıl yürütme tehlikesi var: tüm bir topluluğun kötü bir tasarım kullandığını varsayalım, bu tasarım beklenecek! Bu nedenle prensip bir kâse olamaz ve dikkatle değerlendirilmelidir.

normal durumlar için istisnalar, olağandışı (yani istisnai) durumları nasıl bulursunuz?

Birçok reaksiyonda sth. böyle yalak gibi. Sadece yakala, değil mi? Yönteminiz açık, iyi belgelenmiş ve sözleşmesini onurlandırmalıdır. İtiraf etmem gereken bir soru almadım.

Tüm istisnalarda hata ayıklama: aynı, bu bazen yapılır, çünkü istisnaları kullanmama tasarımı yaygındır. Sorum şuydu: ilk etapta neden yaygın?


1
1) Aramadan xönce her zaman kontrol ediyor musunuz 1/x? 2) Her bölme işlemini yakalamak için bir try-catch bloğuna sarıyor musunuz DivideByZeroException? 3) Kurtarmak için catch bloğuna hangi mantığı koydunuz DivideByZeroException?
Lightman

1
DivisionByZero ve FileNotFound hariç, istisna olarak ele alınması gereken istisnai durumlar oldukları için kötü örneklerdir.
0x6C38

Bulunamayan bir dosya hakkında, buradaki insanların "istisna karşıtı" ifadesinden daha fazla "istisnai" hiçbir şey yoktur. openConfigFile();bunu, { createDefaultConfigFile(); setFirstAppRun(); }zarif bir şekilde işlenen FileNotFound istisnasıyla yakalanmış bir FileNotFound izleyebilir ; Çarpışma yok, son kullanıcının deneyimini daha da kötüleştirmeyelim. "Ama eğer bu gerçekten ilk koşu olmasaydı ve bunu her seferinde alırlarsa" diyebilirsiniz. En azından uygulama her zaman çalışır ve her başlangıç ​​çökmesini değil! 1'den 10'a "bu korkunç": "ilk çalıştırma" her başlangıç ​​= 3 veya 4, her açılışta çökme = 10
Loduwijk

Örnekleriniz istisnalardır. Hayır, xaramadan önce her zaman kontrol etmezsiniz 1/x, çünkü genellikle iyidir. İstisnai durum, iyi olmadığı durumdur. Burada dünyayı parçalamaktan bahsetmiyoruz, ancak örneğin rastgele verilen temel bir tamsayı için x, 4294967296'da sadece 1 tanesi bölünmeyi yapamaz. Bu istisnai bir durumdur ve istisnalar bununla başa çıkmanın iyi bir yoludur. Ancak, bir switchifadenin eşdeğerini uygulamak için istisnalar kullanabilirsiniz , ancak bu oldukça saçma olurdu.
Thor84no

16

İstisnalardan önce, C'de, yığın çerçevesinin benzer bir şekilde açılmasını sağlamak için kullanılabilir setjmpve longjmpbunlar kullanılabilir.

Daha sonra aynı yapıya bir ad verildi: "İstisna". Ve cevapların çoğu, istisnaların istisnai koşullarda kullanılması amaçlandığını iddia ederek, kullanımı hakkında tartışmak için bu adın anlamına dayanmaktadır. Asla asıl amaç bu değildi longjmp. Birçok yığın karesinde kontrol akışını kesmeniz gereken durumlar vardı.

İstisnalar, aynı yığın çerçevesinde de kullanabilmeniz için biraz daha geneldir. Bu gotoyanlış olduğuna inandığım ile benzerlikler doğuruyor . Gotos (yani bir sıkı bir şekilde bağlanmış çift vardır setjmpve longjmp). İstisnalar, çok daha temiz olan gevşek bir yayın / aboneliği takip eder! Bu nedenle, bunları aynı yığın çerçevesinde kullanmak, gotos kullanmakla aynı şey değildir .

Üçüncü karışıklık kaynağı, kontrol edilmiş veya kontrol edilmemiş istisnalar olup olmadığı ile ilgilidir. Elbette, kontrolsüz istisnalar kontrol akışı ve belki de başka birçok şey için kullanmak için özellikle korkunç görünüyor.

Bununla birlikte, kontrol edilen istisnalar, tüm Victoria Hangout'larını ele geçirip biraz yaşarsanız kontrol akışı için mükemmeldir.

En sevdiğim kullanım throw new Success() , aradığı şeyi bulana kadar birbiri ardına çalışan uzun bir kod parçasının . Her şey - her mantık parçası - rastgele yuvalama olabilir, bu nedenle breakher türlü durum testi de dışarıdadır. if-elseDesen kırılgandır. Bir elsesözdizimini başka bir şekilde düzenler veya karıştırırsam, kıllı bir hata var.

kullanma throw new Success() kod akışını doğrusallaştırır . Yerel olarak tanımlanmışSuccesssınıfları kullanıyorum - tabii ki işaretli - eğer onu yakalamayı unutursam kod derlenmeyecek. Ve başka bir yönteminSuccessesiniyakalamıyorum.

Bazen kodum bir şeyi arka arkaya kontrol eder ve sadece her şey yolundaysa başarılı olur. Bu durumda benzer bir doğrusallaştırma kullanıyorum throw new Failure().

Ayrı bir işlev kullanmak, bölümlendirmenin doğal düzeyini bozar. Böylecereturn çözüm optimal değildir. Bilişsel nedenlerden ötürü bir yerde bir veya iki kodun olmasını tercih ederim. Ultra ince bölünmüş koda inanmıyorum.

Bir erişim noktası olmadığı sürece JVM'lerin veya derleyicilerin yaptıkları benim için daha az alakalı. Derleyicilerin yerel olarak atılan ve yakalanan İstisnaları tespit etmemelerinin ve onlara çok verimli davrandıklarının temel bir nedeni olduğuna inanamıyorumgoto makine kodu düzeyinde .

Bunları kontrol akışı işlevlerinde - yani istisnai durumlardan ziyade yaygın durumlar için - kullandıkları kadarıyla, birden fazla kopma, durum testleri, sadece geri yükleme yerine üç yığın çerçeveden geçmeye nasıl geri döneceklerini göremiyorum. yığını işaretçisi.

Şahsen deseni yığın çerçevelerinde kullanmıyorum ve bunu zarif bir şekilde yapmanın tasarım sofistike olmasını nasıl gerektirdiğini görebiliyorum. Ancak idareli kullanıldığında gayet iyi olmalı.

Son olarak, şaşırtıcı bakire programcılar ile ilgili olarak, bu zorlayıcı bir neden değildir. Onları nazikçe pratiğe tanıtırsanız, sevmeyi öğreneceklerdir. C programcıları şaşırtmak ve korkutmak için kullanılan C ++ hatırlıyorum.


3
Bu kalıbı kullanarak, kaba işlevlerimin çoğunun sonunda iki küçük yakalama var - biri Başarı için ve diğeri Başarısızlık için ve fonksiyon doğru sunucu uygulaması yanıtı hazırlamak veya dönüş değerleri hazırlamak gibi şeyleri tamamlıyor. Sargı yapmak için tek bir yere sahip olmak güzel. return-Pattern alternatif her tür fonksiyon için iki işlevi gerektirecektir. Sunucu uygulaması yanıtı veya diğer bu tür eylemleri hazırlamak için bir dış ve hesaplamayı yapmak için bir iç yanıt. PS: Bir İngiliz profesör muhtemelen son para "şaşırtıcı" yerine "şaşırtıcı" kullanmanızı öneririz :-)
necromancer

11

Standart cevaplayıcı, istisnaların düzenli olmadığı ve istisnai durumlarda kullanılması gerektiğidir.

Benim için önemli olan bir sebep, try-catchkoruduğum veya hata ayıkladığım bir yazılımdaki bir kontrol yapısını okuduğumda , orijinal kodlayıcının neden bir if-elseyapı yerine istisna işleme kullandığını bulmaya çalışmamdır . Ve iyi bir cevap bulmayı umuyorum.

Yalnızca bilgisayar için değil, diğer kodlayıcılar için de kod yazdığınızı unutmayın. Bir istisna işleyicisiyle ilişkili, sadece makinenin aldırış etmediği için atlayamayacağınız bir anlambilim vardır.


Bence takdirsiz bir cevap. Yutulan bir istisna bulduğunda bilgisayar çok yavaşlamayabilir, ancak başkasının kodu üzerinde çalıştığımda ve onunla karşılaştığımda, izlediğim önemli bir şeyi kaçırırsam çalıştığımda parçalarımda ölü durur bilmiyorum, ya da aslında bu anti-paternin kullanımı için herhangi bir gerekçe yoksa.
Tim Abell

9

Performans nasıl? Bir .NET web uygulamasını yük test ederken, yaygın olarak ortaya çıkan bir istisnayı düzeltene ve bu sayı 500 kullanıcıya yükseltilinceye kadar web sunucusu başına 100 simüle edilmiş kullanıcıya ulaştık.


8

Josh Bloch, bu konuyu Etkili Java'da yoğun bir şekilde ele almaktadır. Onun önerileri aydınlatıcıdır ve .Net için de geçerli olmalıdır (ayrıntılar hariç).

Özellikle istisnai durumlar için istisnalar kullanılmalıdır. Bunun nedenleri, çoğunlukla kullanılabilirlikle ilgilidir. Belirli bir yöntemin maksimum düzeyde kullanılabilir olması için, giriş ve çıkış koşulları maksimum düzeyde kısıtlanmalıdır.

Örneğin, ikinci yöntemin kullanılması ilk yöntemden daha kolaydır:

/**
 * Adds two positive numbers.
 *
 * @param addend1 greater than zero
 * @param addend2 greater than zero
 * @throws AdditionException if addend1 or addend2 is less than or equal to zero
 */
int addPositiveNumbers(int addend1, int addend2) throws AdditionException{
  if( addend1 <= 0 ){
     throw new AdditionException("addend1 is <= 0");
  }
  else if( addend2 <= 0 ){
     throw new AdditionException("addend2 is <= 0");
  }
  return addend1 + addend2;
}

/**
 * Adds two positive numbers.
 *
 * @param addend1 greater than zero
 * @param addend2 greater than zero
 */
public int addPositiveNumbers(int addend1, int addend2) {
  if( addend1 <= 0 ){
     throw new IllegalArgumentException("addend1 is <= 0");
  }
  else if( addend2 <= 0 ){
     throw new IllegalArgumentException("addend2 is <= 0");
  }
  return addend1 + addend2;
}

Her iki durumda da, arayanın API'nızı uygun şekilde kullandığından emin olmak için kontrol etmeniz gerekir. Ancak ikinci durumda, buna ihtiyacınız var (dolaylı olarak). Kullanıcı javadok okumamışsa, yumuşak Özel Durumlar atılır, ancak:

  1. Belgelemenize gerek yok.
  2. Test etmeniz gerekmez (birim test stratejinizin ne kadar agresif olduğuna bağlı olarak).
  3. Arayanın üç kullanım durumunu ele almasına gerek yoktur.

Yer seviyesinde noktası İstisnalar gerektiğidir değil sen SİZİN API, ancak arayanın API yanı sadece karmaşık ettik büyük nedeni, dönüş kodları olarak kullanılabilir.

Doğru olanı yapmak elbette bir bedeldir. Maliyet, herkesin belgeleri okuması ve izlemesi gerektiğini anlaması gerektiğidir. Umarım yine de durum böyledir.


7

Akış kontrolü için İstisnalar kullanabileceğinizi düşünüyorum. Bununla birlikte, bu tekniğin bir tersi vardır. İstisnalar oluşturmak maliyetli bir şeydir, çünkü bir yığın izlemesi oluşturmak zorundadırlar. Bu nedenle, İstisnaları istisnai bir duruma işaret etmekten daha sık kullanmak istiyorsanız, yığın izlerini oluşturmanın performansınızı olumsuz etkilemediğinden emin olmalısınız.

İstisnalar oluşturma maliyetini azaltmanın en iyi yolu, fillInStackTrace () yöntemini şu şekilde geçersiz kılmaktır:

public Throwable fillInStackTrace() { return this; }

Böyle bir istisnanın doldurulmuş yığın izleri olmayacaktır.


Yığın izleme ayrıca, arayan kişinin yığındaki tüm Throwable'ları "bilmesini" (yani bağımlı olması) gerektirir. Bu kötü bir şey. Soyutlama düzeyine uygun istisnalar atın (Hizmetlerde ServiceExceptions, Dao yöntemlerinden DaoExceptions, vb.). Sadece gerekirse çevirin.
jasonnerothin

5

Alıntı yaptığınız kodda program akışını nasıl kontrol ettiğinizi gerçekten göremiyorum. ArgumentOutOfRange istisnasının yanında asla başka bir istisna görmezsiniz. (Yani ikinci yakalama şartınız asla vurulamaz). Tüm yaptığınız bir if ifadesini taklit etmek için son derece pahalı bir atış kullanmaktır.

Ayrıca, akış kontrolünü gerçekleştirmek için başka bir yere yakalanması için sadece bir istisna attığınız operasyonları daha uğursuz hale getirmiyorsunuz. Aslında istisnai bir durumu ele alıyorsunuz.


5

İşte blog yazımda açıkladığım en iyi uygulamalar :

  • Yazılımınızda beklenmeyen bir durumu belirtmek için bir istisna atın .
  • Giriş doğrulaması için dönüş değerlerini kullanın .
  • Bir kütüphanenin attığı istisnalarla nasıl başa çıkacağınızı biliyorsanız, bunları mümkün olan en düşük seviyede yakalayın .
  • Beklenmeyen bir istisnanız varsa, geçerli işlemi tamamen atın. Onlarla nasıl başa çıkacağınızı biliyormuş gibi yapma .

4

Kodun okunması zor olduğundan, hata ayıklamada sorun yaşayabilirsiniz, hataları uzun bir süre sonra düzeltirken yeni hatalar getireceksiniz, kaynaklar ve zaman açısından daha pahalıdır ve kodunuzda hata ayıklarsanız sizi rahatsız eder ve hata ayıklayıcı her istisnanın gerçekleşmesi üzerine durur;)


4

Belirtilen nedenlerin yanı sıra, akış kontrolü için istisnaları kullanmamanın bir nedeni, hata ayıklama sürecini büyük ölçüde karmaşık hale getirebilmesidir.

Örneğin, VS'de bir hatayı izlemeye çalıştığımda tipik olarak "tüm istisnaları kır" ı açacağım. Akış kontrolü için istisnalar kullanıyorsanız, düzenli olarak hata ayıklayıcıya gireceğim ve gerçek soruna ulaşana kadar bu istisnai olmayan istisnaları görmezden gelmek zorunda kalacağım. Bu muhtemelen birini deli edecek!


1
Ben zaten bir higer: Ben tüm istisnalar hata ayıklama: aynı, bu sadece istisna kullanmama tasarım yaygın olduğu için yapılır. Sorum şuydu: ilk etapta neden yaygın?
Peter

Yani cevabınız temelde "Kötü çünkü Visual Studio'nun bu özelliği var ..." mı? Yaklaşık 20 yıldır program yapıyorum ve "tüm istisnalarda kırılma" seçeneği olduğunu fark etmedim. Yine de, "bu özellik yüzünden!" zayıf bir sebep gibi geliyor. Sadece istisnayı kaynağına kadar takip edin; umarım bunu kolaylaştıran bir dil kullanıyorsunuzdur - aksi takdirde probleminiz istisnaların kendilerinin genel kullanımıyla değil dil özellikleriyle ilgilidir.
Loduwijk

3

Bazı hesaplamalar yapan bir yönteminiz olduğunu varsayalım. Doğrulaması gereken birçok giriş parametresi vardır, daha sonra 0'dan büyük bir sayı döndürür.

Doğrulama hatasını bildirmek için dönüş değerlerini kullanmak basittir: yöntem 0'dan küçük bir sayı döndürdüyse bir hata oluştu. O zaman hangi parametrenin doğrulanmadığını nasıl anlayabilirim ?

C günlerimden bir çok fonksiyonun hata kodlarını döndürdüğünü hatırlıyorum:

-1 - x lesser then MinX
-2 - x greater then MaxX
-3 - y lesser then MinY

vb.

Bir istisna fırlattıktan ve yakaladıktan sonra gerçekten daha az okunabilir mi?


bu yüzden enums icat ettiler :) Ama sihirli sayılar tamamen farklı bir konudur .. en.wikipedia.org/wiki/…
Isak Savo

Harika bir örnek. Aynı şeyi yazmak üzereydim. @IsakSavo: Yöntemin bir anlam değeri veya nesne döndürmesi bekleniyorsa numaralandırmalar bu durumda yardımcı olmaz. Örneğin, getAccountBalance () yöntemi bir AccountBalanceResultEnum nesnesi değil bir Money nesnesi döndürmelidir. Birçok C programı, bir sentinel değerinin (0 veya null) bir hatayı temsil ettiği benzer bir desene sahiptir ve daha sonra hatanın nedenini belirlemek için ayrı bir hata kodu almak için başka bir işlevi çağırmanız gerekir. (MySQL C API'si şöyledir.)
Mark E. Haase

3

Tıpkı kontrol akışı için istisnalar kullanabileceğiniz gibi, bir vidayı döndürmek için bir çekiç çenesini kullanabilirsiniz. Bu , özelliğin amaçlanan kullanımı olduğu anlamına gelmez . ifKimin amaçlanan kullanım deyimi anlatırken kullanılır koşullar, olduğu akışını kontrol.

Bir özelliği, bu amaç için tasarlanmış özelliği kullanmamayı seçerken istemeden kullanıyorsanız, bununla ilişkili bir maliyet olacaktır. Bu durumda, netlik ve performans gerçek bir katma değer yaratmaz. İstisnaları kullanmak, geniş çapta kabul gören ifbeyanı size ne kazandırır?

Başka bir yol söyledi: sadece yapabileceğiniz için yapmanız gerektiği anlamına gelmez .


1
İstisnaya gerek olmadığını mı söylüyorsunuz, ifnormal kullanımdan sonra veya infazların kullanımı amaçlanmadığı için tasarlanmamıştır (dairesel argüman)?
Val

1
@Val: İstisnalar istisnai durumlar içindir - bunu bir istisna ve işlemek için yeterli tespit etmemiz durumunda, için yeterli bilgiye sahip değildir atmak ve hala idare. Doğrudan taşıma mantığına gidebilir ve pahalı, gereksiz deneme / yakalamayı atlayabiliriz.
Bryan Watts

Bu mantıkla, istisnalarınız olmayabilir ve istisna atmak yerine her zaman bir sistem çıkışı yapabilirsiniz. Çıkmadan önce bir şey yapmak istiyorsanız, bir sarıcı yapın ve bunu arayın. Java örneği: public class ExitHelper{ public static void cleanExit() { cleanup(); System.exit(1); } }Öyleyse bunu atmak yerine arayın: ExitHelper.cleanExit();Argümanınız sağlam olsaydı, bu tercih edilen yaklaşım olurdu ve istisna olmayacaktı. Temel olarak "İstisnaların tek nedeni farklı bir şekilde çökmek" diyorsunuz.
Loduwijk

@Aaron: İstisnayı atar ve yakalarsam, bunu önlemek için yeterli bilgiye sahibim. Bu, tüm istisnaların aniden ölümcül olduğu anlamına gelmez. Kontrol edemediğim diğer kod onu yakalayabilir ve bu iyi. Sesimdeki argümanım, aynı bağlamda bir istisna atmanın ve yakalamanın gereksiz olduğu. Tüm istisnaların süreçten çıkması gerektiğini söylemedim ya da söylemedim.
Bryan Watts

@BryanWatts Kabul edildi. Diğerleri var sadece her zaman istisnalar çökmesini olmalıdır aralarından kurtaramazsınız şey için istisnalar kullanılarak ve uzantısı gerektiğini söyledi. Bu yüzden bu şeyleri tartışmak zor; sadece 2 görüş değil, birçok görüş var. Hala sana katılmıyorum, ama pek değil. Birlikte atma / yakalama en okunabilir, bakımı en güzel görünümlü kod vardır; genellikle bu zaten başka istisnalar yakalarsanız olur, bu yüzden zaten deneyin / yakalayın ve 1 veya 2 daha fazla yakalama eklemek ayrı hata kontrollerinden daha temizdir if.
Loduwijk

3

Diğerlerinin sayısız söylediği gibi, en az şaşkınlık ilkesi, yalnızca kontrol akışı amaçları için istisnaları aşırı kullanmanızı yasaklayacaktır. Öte yandan, hiçbir kural% 100 doğru değildir ve her zaman bir istisnanın "sadece doğru araç" olduğu durumlar vardır - tıpkı gotokendisi gibi , bu arada, breakvecontinue tıpkı Java gibi, Java gibi dillerde dillerde gönderilen her zaman önlenemeyen ağır iç içe döngülerden atlamak için mükemmel bir yoldur.

Aşağıdaki blog gönderisi, yerel olmayanlar için oldukça karmaşık ancak ilginç bir kullanım durumunu açıklamaktadır ControlFlowException:

Bu bir nasıl içini açıklıyor jOOQ (Java için SQL soyutlama kitaplığı) , bu tür istisnaların, bazen "nadir" bir durum gerçekleştiğinde SQL oluşturma işlemini erken iptal etmek için zaman zaman nasıl kullanıldığını .

Bu tür durumlara örnekler:

  • Çok fazla bağlama değeri ile karşılaşıldı. Bazı veritabanları SQL deyimlerinde rasgele sayıda bağlanma değerini desteklemez (SQLite: 999, Ingres 10.1.0: 1024, Sybase ASE 15.5: 2000, SQL Server 2008: 2100). Bu durumlarda, jOOQ SQL oluşturma aşamasını iptal eder ve SQL ifadesini satır içi bağlama değerleriyle yeniden oluşturur. Misal:

    // Pseudo-code attaching a "handler" that will
    // abort query rendering once the maximum number
    // of bind values was exceeded:
    context.attachBindValueCounter();
    String sql;
    try {
    
      // In most cases, this will succeed:
      sql = query.render();
    }
    catch (ReRenderWithInlinedVariables e) {
      sql = query.renderWithInlinedBindValues();
    }

    Her seferinde saymak için bağlama değerlerini AST sorgusundan açıkça çıkarırsak, bu sorundan muzdarip olmayan sorguların% 99,9'u için değerli CPU döngüleri harcarız.

  • Bazı mantık yalnızca dolaylı olarak yalnızca "kısmen" yürütmek istediğimiz bir API aracılığıyla kullanılabilir. UpdatableRecord.store()Yöntem oluşturur INSERTveya UPDATEbağlı açıklamada, Recordbireyin iç bayrakları. "Dışarıda" dan, ne tür bir mantık içerdiğini bilmiyoruz store()(örn. İyimser kilitleme, olay dinleyicisi işleme, vb.), Böylece bir toplu ifadede birkaç kayıt sakladığımızda bu mantığı tekrarlamak istemiyoruz, burada store()sadece SQL ifadesini oluşturmak istiyoruz , aslında yürütmüyoruz. Misal:

    // Pseudo-code attaching a "handler" that will
    // prevent query execution and throw exceptions
    // instead:
    context.attachQueryCollector();
    
    // Collect the SQL for every store operation
    for (int i = 0; i < records.length; i++) {
      try {
        records[i].store();
      }
    
      // The attached handler will result in this
      // exception being thrown rather than actually
      // storing records to the database
      catch (QueryCollectorException e) {
    
        // The exception is thrown after the rendered
        // SQL statement is available
        queries.add(e.query());                
      }
    }

    Biz dışlanmışken olsaydı store()isteğe göre özelleştirilebilir "Çok kullanımlık" API içine mantığı değil SQL çalıştırmak, biz korumak için oldukça zor oluşturma içine bakıyor olurduk, pek yeniden kullanılabilir API.

Sonuç

Özünde, bu yerel olmayanları kullanımımız goto[Mason Wheeler] [5] cevabında söylediklerinin hemen yanındadır:

"Bu noktada düzgün bir şekilde başa çıkamayacağım bir durumla karşılaştım, çünkü bununla başa çıkmak için yeterli bağlamım yok, ama beni (ya da çağrı yığınının üstündeki bir şeyi) çağıran rutinle nasıl başa çıkacağını bilmeli ."

Her iki kullanımının ControlFlowExceptionsda alternatiflerine kıyasla uygulanması oldukça kolaydı, bu da ilgili iç kısımlardan yeniden düzenlemeden çok çeşitli mantığı yeniden kullanmamızı sağladı.

Ancak bunun gelecekteki koruyucular için bir sürpriz olması hissi devam ediyor. Kod oldukça hassastır ve bu durumda doğru seçim olsa da, her zaman sıradan dallanmayı kullanmaktan kaçınmanın kolay olduğu yerel kontrol akışı için istisnalar kullanmamayı tercih ederiz if - else.


2

Tipik olarak, bir istisnayı düşük seviyede tutmakla yanlış bir şey yoktur. İstisna, bir işlemin neden gerçekleştirilemediğine dair çok fazla ayrıntı sağlayan geçerli bir mesajdır. Ve eğer halledebilirsen, yapmalısın.

Genel olarak, kontrol edebileceğiniz yüksek bir arıza olasılığı olduğunu biliyorsanız ... kontrolü yapmalısınız ... yani (obj! = Null) obj.method ()

Sizin durumunuzda, tarih saatinin bir zaman damgasının sınırların dışında olup olmadığını kontrol etmenin kolay bir yolu olup olmadığını bilmek için C # kitaplığına yeterince aşina değilim. Eğer öyleyse, sadece (.isvalid (ts)) öğesini arayın, aksi takdirde kodunuz iyi durumda demektir.

Yani, temelde, hangi yolun daha temiz bir kod oluşturduğuna iner ... beklenen bir istisnayı korumak için yapılan işlem, istisnayı ele almaktan daha karmaşıksa; her yerde karmaşık korumalar oluşturmak yerine istisnayı ele alma iznim var.


Eklenti noktası: İstisnanız hata yakalama bilgileri sağlıyorsa ("Param getWhatParamMessedMeUp ()" gibi bir alıcı), API'nızın kullanıcısının bir sonraki adımda iyi bir karar vermesine yardımcı olabilir. Aksi takdirde, yalnızca hata durumuna bir ad veriyorsunuz.
jasonnerothin

2

Kontrol akışı için özel durum işleyicileri kullanıyorsanız, çok genel ve tembel olursunuz. Başka birinin belirttiği gibi, işleyicide işlemeyi gerçekleştiriyorsanız bir şey olduğunu biliyorsunuz, ama tam olarak ne? Esasen, kontrol akışı için kullanıyorsanız, else deyimi için istisnayı kullanıyorsunuz.

Hangi olası durumun meydana gelebileceğini bilmiyorsanız, beklenmedik durumlar için bir istisna işleyici kullanabilirsiniz, örneğin üçüncü taraf bir kitaplık kullanmanız gerektiğinde veya hoş bir hata göstermek için kullanıcı arayüzündeki her şeyi yakalamanız gerekir mesaj ve günlüğü istisna.

Ancak, neyin yanlış gidebileceğini biliyorsanız ve bunu kontrol etmek için bir if ifadesi veya bir şey koymazsanız, sadece tembel olursunuz. İstisna işleyicinin olabileceğini bildiğiniz şeyler için tümünü yakalama olmasına izin vermek tembeldir ve daha sonra size musallat olur, çünkü istisna işleyicinizdeki bir durumu yanlış bir varsayım temelinde düzeltmeye çalışacaksınız.

Tam olarak ne olduğunu belirlemek için istisna işleyicinize mantık koyarsanız, o mantığı try bloğunun içine koymamanız oldukça aptal olur.

İstisna işleyicileri, bir şeyin yanlış gitmesini engellemek için fikirleriniz / yollarınız bittiğinde veya işler kontrol etme yeteneğinizin ötesinde olduğunda son çare. Sunucu çöktü ve zaman aşımına uğradı ve bu özel durumun atılmasını engelleyemezsiniz.

Son olarak, tüm kontrollerin önceden yapılması, bildiğiniz veya beklediğiniz şeyin gerçekleşeceğini gösterir ve bunu açık hale getirir. Kodun amacı açık olmalıdır. Ne okumayı tercih edersin?


1
Hiç doğru değil: "Esasen başka bir ifade için istisna kullanıyorsunuz, eğer kontrol akışı için kullanıyorsanız." Eğer kontrol akışı için kullanıyorsanız, tam olarak ne yakaladığınızı biliyorsunuz ve asla genel bir yakalama kullanmıyorsunuz, ama tabii ki bir tane!
Peter

2

Ortak Lisp'in bir tür istisnaların doğru şekilde genelleştirilmesi olan durum sistemine bir göz atmak isteyebilirsiniz. Yığını kontrollü bir şekilde gevşetebildiğiniz veya kontrol edemediğiniz için, son derece kullanışlı olan "yeniden başlatma" da elde edersiniz.

Bunun diğer dillerdeki en iyi uygulamalarla ilgisi yoktur, ancak düşündüğünüz yönde (kabaca) düşünülmüş bazı tasarımlarla neler yapılabileceğini gösterir.

Tabii ki yo-yo gibi yukarı ve aşağı zıplıyorsanız hala performansla ilgili hususlar var, ancak çoğu ahlaki sistemin yakaladığı / yakaladığı “oh bok, kefalet sağlar” yaklaşımından çok daha genel bir fikir.


2

Akış kontrolü için İstisnalar kullanmanın yanlış bir şey olduğunu düşünmüyorum. İstisnalar sürekliliklere biraz benzer ve statik olarak yazılmış dillerde, İstisnalar sürekliliklerden daha güçlüdür, bu nedenle, sürekliliklere ihtiyacınız varsa, ancak dilinizde bunlara sahip değilseniz, bunları uygulamak için İstisnalar'ı kullanabilirsiniz.

Aslında, eğer sürekliliklere ihtiyacınız varsa ve diliniz onlara sahip değilse, yanlış dili seçtiniz ve farklı bir dil kullanmayı tercih etmelisiniz. Ancak bazen bir seçeneğiniz yoktur: istemci tarafı web programlama en iyi örnektir - JavaScript'i kullanmanın bir yolu yoktur.

Bir örnek: Microsoft Volta , web uygulamalarının doğrudan .NET'te yazılmasına izin veren ve çerçevenin hangi bitlerin nerede çalıştırılması gerektiğini bulmaya özen göstermesine izin veren bir projedir. Bunun bir sonucu Volta'nın istemcide kod çalıştırabilmesi için CIL'i JavaScript'e derlemesi gerektiğidir. Ancak, bir sorun var: .NET çok iş parçacıklı, JavaScript yok. Bu nedenle Volta, JavaScript İstisnalarını kullanarak JavaScript'te süreklilikleri uygular ve daha sonra bu süreklilikleri kullanarak .NET Threads uygular. Bu şekilde, iş parçacığı kullanan Volta uygulamaları değiştirilmemiş bir tarayıcıda çalışacak şekilde derlenebilir - Silverlight gerekmez.


2

Estetik bir sebep:

Bir deneme her zaman bir yakalama ile gelir, ancak bir if başka biriyle gelmek zorunda değildir.

if (PerformCheckSucceeded())
   DoSomething();

Deneme / yakalama ile çok daha ayrıntılı hale gelir.

try
{
   PerformCheckSucceeded();
   DoSomething();
}
catch
{
}

Bu 6 kod satırı çok fazla.


1

Ancak, çağırdığınız Yöntem (ler) de ne olduğunu her zaman bilemezsiniz. İstisnanın nereye atıldığını tam olarak bilemezsiniz. İstisna nesnesini daha ayrıntılı incelemeden ...


1

Örneğinizde yanlış bir şey olmadığını hissediyorum. Aksine, çağrılan fonksiyon tarafından atılan istisnayı görmezden gelmek günah olacaktır.

JVM'de, bir istisna atmak o kadar pahalı değildir, sadece yeni xyzException (...) ile istisna yaratır, çünkü ikincisi bir yığın yürüyüşü içerir. Önceden oluşturulmuş bazı istisnalarınız varsa, bunları maliyetsiz olarak birçok kez atabilirsiniz. Tabii ki, bu şekilde istisna ile birlikte veri geçiremezsiniz, ancak bunun yine de yapılması kötü bir şey olduğunu düşünüyorum.


Üzgünüm, bu çok yanlış, Brann. Duruma bağlıdır. Durumun önemsiz olduğu her zaman böyle değildir. Bu nedenle, bir if ifadesi saatler, günler veya daha uzun sürebilir.
Ingo

JVM'de bu. Bir dönüş daha pahalı değil. Git şekil. Ama soru şu ki, normal bir durumdan istisnai bir durumu anlatmak için çağrılan işlevde zaten mevcut olan kod değilse, if koduna ne yazacaksınız - böylece kod çoğaltma.
Ingo

1
Ingo: İstisnai bir durum beklemediğiniz bir durumdur. yani bir programcı olarak düşünmediğiniz biri. Benim
kuralım

1
Hiçbir zaman istisna işleyicileri yazmam, her zaman sorunu düzeltirim (hata kodu üzerinde kontrole sahip olmadığım için bunu yapamadığım zamanlar hariç). Ve yazdığım kodun başka birisinin (örneğin bir kütüphane) kullanması dışında, istisnalar atmam. Hayır bana çelişkiyi göster?
Brann

1
İstisnaları çılgınca atmamaya katılıyorum. Ama şüphesiz, "istisnai" olan bir tanım meselesidir. Bu String.parseDouble, yararlı bir sonuç veremezse bir istisna atar, örneğin, sırayla. Başka ne yapmalı? NaN geri dönecek mi? IEEE olmayan donanımlar ne olacak?
Ingo

1

Bir dilin, bir yöntemin bir değer döndürmeden çıkmasına ve bir sonraki "catch" bloğuna gevşemesine izin verebileceği birkaç genel mekanizma vardır:

  • Yöntemin çağrı sitesini belirlemek için yığın çerçevesini incelemesini sağlayın tryve çağrı yöntemi içindeki bir blok veya çağrı yönteminin arayanın adresini depoladığı konumu bulmak için çağrı sitesinin meta verilerini kullanın ; ikinci durumda, bir tryblok bulana veya yığın boş olana kadar tekrarlayarak, arayan kişinin hemen arayan ile aynı şekilde belirlemesi için meta verileri inceleyin . Bu yaklaşım, istisnasız duruma çok az ek yük ekler (bazı optimizasyonları engeller), ancak bir istisna oluştuğunda pahalıdır.

  • Yöntemin, normal bir dönüşü bir istisnandan ayıran "gizli" bir bayrak döndürmesini sağlayın ve çağıranın ayarlanmışsa, bayrağı ve dalı "istisna" yordamına kontrol etmesini sağlayın. Bu rutin istisnasız durum için 1-2 komut ekler, ancak bir istisna meydana geldiğinde nispeten az ek yük.

  • Arayanın, özel durum işleme bilgilerini veya kodunu yığılmış dönüş adresine göre sabit bir adrese yerleştirmesini sağlayın. Örneğin, ARM ile "BL altyordam" talimatını kullanmak yerine, sekans kullanılabilir:

        adr lr,next_instr
        b subroutine
        b handle_exception
    next_instr:
    

Normalde çıkmak için altyordamı basitçe yapacağını bx lrya pop {pc}; anormal bir çıkış olması durumunda, altyordam, dönüşü veya kullanımı gerçekleştirmeden önce LR'den 4 çıkarırsub lr,#4,pc (ARM varyasyonu, yürütme moduna vb. bağlı olarak).

İşaretli istisnaları kullanan bir dil veya çerçeve, yukarıdaki # 2 veya # 3 gibi bir mekanizma ile ele alınırken, işaretlenmeyen istisnalar # 1 kullanılarak ele alınabilir. Java'da kontrol edilen istisnaların uygulanması oldukça zahmetli olsa da, bir çağrı sitesinin aslında "Bu yöntem XX atma olarak ilan edildiğini söyleyebileceği bir araç olsaydı, kötü bir kavram olmazdı, ama beklemiyorum bunu yaparsa, "denetlenmemiş" bir istisna olarak yeniden düşünün.Kontrol edilmiş istisnaların bu şekilde ele alındığı bir çerçevede, bazı bağlamlarda, yüksek başarısızlık olasılığı, ancak başarısızlığın başarıdan temelde farklı bilgiler vermesi gerektiği yerde. Ancak böyle bir desen kullanan çerçevelerin farkında değilim. Bunun yerine, daha yaygın olan model, tüm istisnalar için yukarıdaki ilk yaklaşımı kullanmaktır (istisnasız durum için minimum maliyet, ancak istisnalar atıldığında yüksek maliyet).

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.