Tek seferde birden fazla istisna yakalamak ister misiniz?


2140

Sadece yakalamak cesaretini kırıyor System.Exception. Bunun yerine, yalnızca "bilinen" istisnalar yakalanmalıdır.

Şimdi, bu bazen gereksiz tekrarlayan koda yol açar, örneğin:

try
{
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
    WebId = Guid.Empty;
}
catch (OverflowException)
{
    WebId = Guid.Empty;
}

Acaba: Her iki istisnayı yakalamanın ve sadece bir WebId = Guid.Emptykez çağrı yapmanın bir yolu var mı ?

Verilen örnek oldukça basittir, çünkü sadece a GUID. Ancak bir nesneyi birden çok kez değiştirdiğiniz bir kod hayal edin ve manipülasyonlardan biri beklenen şekilde başarısız olursa, "sıfırlamak" istersiniz object. Ancak, beklenmedik bir istisna varsa, yine de bunu daha yükseğe atmak istiyorum.


5
.Net 4 ve üstü kullanıyorsanız aggregateexception kullanmayı tercih ederim msdn.microsoft.com/en-us/library/system.aggregateexception.aspx
Bepenfriends

2
Bepenfriends- System.Guid AggregateException'ı atmadığından , siz (veya birisi) AggregateException'a nasıl satacağınızı gösteren bir cevap gönderebilirseniz harika olur.
weir

1
Kullanımda AggregateException: Kendi
kodumda

11
"Sadece System.Exception yakalamak için cesaret kırıldı." -ve yöntem 32 çeşit istisna atabilirse, ne yapar? her biri için ayrı ayrı catch yazma?
giorgim

5
Bir yöntem 32 farklı istisna atarsa, kötü yazılmıştır. Ya kendi çağrılarının yaptığı istisnaları yakalamıyor, bir yöntemde çok fazla FAR yapıyor ya da bu 32'nin çoğunluğu / hepsi bir neden koduyla tek bir istisna olmalıdır.
Flynn1179

Yanıtlar:


2100

System.ExceptionTürleri yakalayın ve açın

catch (Exception ex)            
{                
    if (ex is FormatException || ex is OverflowException)
    {
        WebId = Guid.Empty;
        return;
    }

    throw;
}

69
Ne yazık ki, FxCop (yani - Visual Studio Kod Analizi) İstisna yakaladığınızda hoşlanmıyor.
Andrew Garrison

15
İstisna yakalamamaya katılıyorum, ancak bu durumda yakalama bir filtredir. Diğer istisna türlerini işleyecek daha yüksek bir katmanınız olabilir. Bir catch (istisna x) içermesine rağmen, bunun doğru olduğunu söyleyebilirim. Program akışını değiştirmez, yalnızca belirli istisnaları işler ve uygulamanın geri kalanının diğer istisna türleriyle ilgilenmesine izin verir.
lkg

28
FxCop'un en son sürümü yukarıdaki kod kullanıldığında bir istisna oluşturmaz.
Peter

28
İlk önce OP kodu ile neyin yanlış olduğundan emin değilim. Kabul edilen # 1 cevabı neredeyse iki kat daha fazla ve daha az okunabilir.
João Bragança

22
@ JoãoBragança: Bu örnekteki bu yanıt daha fazla satır kullanıyor olsa da, örneğin IO dosyasıyla uğraşıp uğraşmadığınızı hayal etmeye çalışın ve tek yapmanız gereken bu istisnaları yakalamak ve bazı günlük mesajlarını yapmaktır, ancak yalnızca dosya ES yöntemleri. O zaman genellikle daha fazla sayıda (yaklaşık 5 veya daha fazla) farklı istisna türü ile uğraşmanız gerekir. Bu durumda, bu yaklaşım size bazı çizgiler kazandırabilir.
Xilconic

594

EDIT: C # 6.0 itibariyle, istisna filtreleri artık gitmek için mükemmel bir yol olduğunu söyleyen diğerleri ile aynı fikirde:catch (Exception ex) when (ex is ... || ex is ... )

Dışında hala bir-uzun satır düzeni nefret ediyorum ve şahsen aşağıdaki gibi kodu koymak istiyorum. Bence bu estetik kadar işlevsel, çünkü kavrayışı geliştirdiğine inanıyorum. Bazıları buna katılmayabilir:

catch (Exception ex) when (
    ex is ...
    || ex is ...
    || ex is ...
)

ORİJİNAL:

Burada partiye biraz geç kaldığımı biliyorum, ama kutsal duman ...

Doğrudan kovalamacaya geçmek için, bu tür daha önceki bir cevabı çoğaltır, ancak gerçekten birkaç istisna türü için ortak bir eylem gerçekleştirmek ve her şeyi tek bir yöntem kapsamında düzgün ve düzenli tutmak istiyorsanız, neden sadece bir lambda kullanmıyorsunuz? / closure / satır içi işlevi aşağıdaki gibi bir şey yapmak için? Demek istediğim, bu kapıyı her yerde kullanabileceğiniz ayrı bir yöntem haline getirmek istediğinizi fark edeceksiniz. Ancak, kodun geri kalanını yapısal olarak değiştirmeden bunu yapmak çok kolay olacaktır. Sağ?

private void TestMethod ()
{
    Action<Exception> errorHandler = ( ex ) => {
        // write to a log, whatever...
    };

    try
    {
        // try some stuff
    }
    catch ( FormatException  ex ) { errorHandler ( ex ); }
    catch ( OverflowException ex ) { errorHandler ( ex ); }
    catch ( ArgumentNullException ex ) { errorHandler ( ex ); }
}

Merak edemiyorum ama merak ediyorum ( uyarı: biraz ironi / alaycı önde) neden dünyadaki temelde aşağıdakileri değiştirmek için tüm bu çabaya gidiyor:

try
{
    // try some stuff
}
catch( FormatException ex ){}
catch( OverflowException ex ){}
catch( ArgumentNullException ex ){}

... bir sonraki kod kokusunun çılgınca bir çeşitlemesiyle, örneğin, sadece birkaç tuşa basıyormuş gibi davranmak için.

// sorta sucks, let's be honest...
try
{
    // try some stuff
}
catch( Exception ex )
{
    if (ex is FormatException ||
        ex is OverflowException ||
        ex is ArgumentNullException)
    {
        // write to a log, whatever...
        return;
    }
    throw;
}

Çünkü kesinlikle otomatik olarak daha okunaklı değil.

Kabul edersek, /* write to a log, whatever... */ return;ilk örnekten üç özdeş örneği bıraktım .

Ama bu bir bakıma. Hepiniz işlevleri / yöntemleri duydunuz, değil mi? Ciddi anlamda. Ortak bir ErrorHandlerişlev yazın ve her catch bloğundan çağırın.

Bana sorarsanız, ikinci örnek ( ifve isanahtar sözcükleriyle birlikte) hem projenizin bakım aşamasında hem daha az okunabilir, hem de eşzamanlı olarak daha fazla hataya açıktır.

Programlamaya nispeten yeni olabilecek herkes için bakım aşaması, projenizin genel ömrünün% 98,7'sini veya daha fazlasını içerecektir ve bakımı yapan zayıf schmuck neredeyse sizden başka biri olacaktır. Ve zamanlarının% 50'sini isminizi lanetleyerek işte geçirecekleri çok iyi bir şans var.

Ve tabii FxCop sana ve zorunda kabukları da kesin olarak çalışan program ile yapmak zip gelmiştir Kodunuza bir öznitelik eklemek ve vakaların 99.9% olarak tamamen olduğuna bir sorunu görmezden FxCop anlatmak için sadece orada işaretlemede doğru. Ve üzgünüm, yanılmış olabilirim, ancak bu "yoksay" özelliği aslında uygulamanızda derlenmiş değil mi?

ifTestin tamamını tek bir satıra yerleştirmek daha okunabilir hale getirir mi? Ben öyle düşünmüyorum. Demek istediğim, uzunca bir zaman önce bir satırda daha fazla kod koymanın "daha hızlı çalışmasını" sağlayacağını savunan başka bir programcı vardı. Ama elbette o sert çılgınca deli olmuştu. Ona (düz bir yüzle - zorlayıcıydı), yorumlayıcının veya derleyicinin bu uzun çizgiyi satır başına ayrı bir talimat başına ayrı ayrı nasıl ayıracağını açıklamaya çalışarak - esas olarak ilerlemişse sonuçla aynıdır ve sadece derleyiciyi zekice çalıştırmaya çalışmak yerine kodu okunabilir hale getirdi - onun üzerinde hiçbir etkisi olmadı. Ama konuţuyorum.

Bundan bir veya iki ay sonra üç istisna türü daha eklediğinizde ne kadar az okunabilir? (Cevap: çok daha az okunabilir hale gelir).

Asıl önemli noktalardan biri, aslında, her gün baktığımız metinsel kaynak kodunu biçimlendirme noktasının çoğunun, kod çalıştığında gerçekte olanları diğer insanlara gerçekten, gerçekten açık hale getirmektir. Çünkü derleyici kaynak kodunu tamamen farklı bir şeye dönüştürüyor ve kod biçimlendirme stilinizi daha az önemsemiyordu. Yani hepsi bir arada tamamen berbat.

Sadece söylüyorum...

// super sucks...
catch( Exception ex )
{
    if ( ex is FormatException || ex is OverflowException || ex is ArgumentNullException )
    {
        // write to a log, whatever...
        return;
    }
    throw;
}

36
Bu soruyu ilk kez tökezlediğimde, kabul edilen cevabın üstündeydim. Serin Sadece tüm Exceptions yakalamak ve türü kontrol edebilirsiniz. Kodu temizlediğini düşündüm, ama bir şey beni soruya geri dönmeye devam etti ve aslında sorunun diğer cevaplarını okudum. Bir süre çiğnedim, ama seninle aynı fikirdeyim. Öyle daha okunabilir ve yakalamak her şeyi daha kodunuzu kurutmak için bir işlevi kullanmak sürdürülebilir bir listede, sarma kodu ve atma karşı karşılaştırarak tipini kontrol edin. Geç geldiğiniz ve alternatif ve aklı başında (IMO) bir seçenek sunduğunuz için teşekkür ederiz. +1.
14'te erroric

8
A eklemek istiyorsanız bir hata işleme işlevi kullanmak işe yaramaz throw;. Her catch bloğunda bu kod satırını tekrarlamanız gerekir (açıkçası dünyanın sonu değil, tekrarlanması gereken kod olduğu için bahsetmeye değer).
kad81

5
@ kad81, bu doğru, ancak yine de günlüğe kaydetme ve temizleme kodunu tek bir yerde yazma ve gerektiğinde tek bir yerde değiştirme avantajını elde edersiniz, temel İstisna türünü yakalamanın akılcı semantiği olmadan, istisna türü. Ve throw();her catch bloğundaki bir ekstra ifade, ödeme yapmak için küçük bir fiyattır, IMO ve yine de gerekirse ek istisna tipine özel temizlik yapma pozisyonunda kalıyor.
Craig

2
Merhaba @Reitffunk, sadece Func<Exception, MyEnumType>yerine kullanın Action<Exception>. Budur Func<T, Result>ile, Resultdönüş tipi olmak.
Craig

3
Burada tamamen katılıyorum. Ben de ilk cevabı okudum ve mantıklı geldiğini düşündüm. Tüm özel durum işleyici için genel 1'e taşındı. İçimdeki bir şey içimden kusmama neden oldu ... bu yüzden kodu geri aldım. Sonra bu güzelliğe rastladı! Bu ihtiyacı kabul cevap vermesini
Conor Gallagher

372

Diğerlerinin de belirttiği gibi, ifneler olduğunu belirlemek için catch bloğunuzun içinde bir ifade olabilir . C # 6 İstisna Filtrelerini destekler, bu nedenle aşağıdakiler çalışır:

try {  }
catch (Exception e) when (MyFilter(e))
{
    
}

MyFilterYöntemi, daha sonra böyle bir şey olabilir:

private bool MyFilter(Exception e)
{
  return e is ArgumentNullException || e is FormatException;
}

Alternatif olarak, bunların tümü satır içi yapılabilir (when ifadesinin sağ tarafı sadece bir boole ifadesi olmalıdır).

try {  }
catch (Exception e) when (e is ArgumentNullException || e is FormatException)
{
    
}

Bu kullanarak farklıdır ifiçinden deyimi catch, bloğun istisna filtreleri kullanarak olmayacaktır yığınını gevşeyin.

Bunu kontrol etmek için Visual Studio 2015'i indirebilirsiniz .

Visual Studio 2013'ü kullanmaya devam etmek istiyorsanız, aşağıdaki nuget paketini yükleyebilirsiniz:

Yükleme Paketi Microsoft.Net.Compilers

Yazma sırasında, bu C # 6 desteğini içerecektir.

Bu pakete başvuruda bulunulması, projenin, sistemde yüklü herhangi bir sürümün aksine, pakette bulunan C # ve Visual Basic derleyicilerinin belirli bir sürümünü kullanarak oluşturulmasına neden olur.


3
Sabırlı bir şekilde 6'nın resmi sürümünü bekliyor ... Bunun gerçekleştiğinde bunun kontrole geldiğini görmek istiyorum.
RubberDuck

@RubberDuck C # 6'dan null yayılım operatörü için ölüyorum. Ekibimin geri kalanının kararsız bir dil / derleyici riskinin buna değdiğine ikna etmeye çalışıyorum. Büyük etkisi olan birçok küçük iyileştirme. Cevap olarak işaretlenmeye gelince, önemli değil, insanlar bunun mümkün / mümkün olduğunu fark ettikleri sürece mutluyum.
Joe

Sağ?! Yakın gelecekte kod tabanımı iyi inceleyeceğim. =) Çekin önemli olmadığını biliyorum, ancak kabul edilen cevap yakında eski olacak, OP'nin uygun görünürlük sağlamak için bunu kontrol etmek için geri gelmesini umuyorum.
RubberDuck

Kısmen bu yüzden henüz vermedim @Joe. Bunun görünür olmasını istiyorum. Yine de netlik için satır içi filtre örneği eklemek isteyebilirsiniz.
RubberDuck

188

Ne yazık ki, C # 'da değil, bunu yapmak için bir istisna filtresine ihtiyacınız var ve C #, MSIL'in bu özelliğini göstermiyor. VB.NET bu özelliğe sahiptir, örn.

Catch ex As Exception When TypeOf ex Is FormatException OrElse TypeOf ex Is OverflowException

Yapabileceğiniz şey, hata kodunuzu kapsüllemek için anonim bir işlev kullanmak ve daha sonra bu belirli yakalama bloklarında çağırmaktır:

Action onError = () => WebId = Guid.Empty;
try
{
    // something
}
catch (FormatException)
{
    onError();
}
catch (OverflowException)
{
    onError();
}

26
İlginç bir fikir ve VB.net bazen C # üzerinde bazı ilginç avantajları vardır başka bir örnek
Michael Stum

47
@MichaelStum bu tür bir sözdizimi ile neredeyse hiç ilginç olarak adlandırmazdım ... shudder
MarioDS

17
İstisna filtreleri c # 6'da geliyor! Yeniden kullanma lehine filtreleri kullanma farkına dikkat
Arne Deruwe

@ArneDeruwe Bu bağlantı için teşekkürler! Yeniden throw e;atmamanın daha önemli bir nedenini öğrendim: stacktrace ve calltack'i throw;yok eder, "only" calltack'i yok eder (crash-dumps işe yaramaz hale getirir!) Önlenebilirse kullanmak için çok iyi bir neden!
AnorZaken

1
C # 6'dan itibaren istisna filtreleri mevcuttur! En sonunda.
Danny

134

Tamlık uğruna, .NET 4.0'dan beri kod şu şekilde yeniden yazılabilir:

Guid.TryParse(queryString["web"], out WebId);

Format yanlışsa TryParse hiçbir zaman istisna atmaz ve false değerini döndürerek WebId değerini ayarlar Guid.Empty.


Yana C # 7 ayrı satırda bir değişken tanıtan önleyebilirsiniz:

Guid.TryParse(queryString["web"], out Guid webId);

Ayrıca, sürüm 4.6'dan itibaren henüz .NET Framework'te bulunmayan dönen tuples'ları ayrıştırmak için yöntemler de oluşturabilirsiniz:

(bool success, Guid result) TryParseGuid(string input) =>
    (Guid.TryParse(input, out Guid result), result);

Ve bunları şöyle kullanın:

WebId = TryParseGuid(queryString["web"]).result;
// or
var tuple = TryParseGuid(queryString["web"]);
WebId = tuple.success ? tuple.result : DefaultWebId;

Bu işe yaramaz cevaba bir sonraki işe yaramaz güncelleme, C # 12'de dış parametrelerin yapısökümü uygulandığında gelir. :)


19
Kesinlikle - özlü ve istisna ile başa çıkmanın performans cezasını, program akışını kontrol etmek için kasıtlı olarak istisnaları kullanmanın kötü şeklini ve dönüşüm mantığınızın biraz burada ve biraz orada yayılmasının yumuşak odağını tamamen atlıyorsunuz .
Craig

9
Ne demek istediğini biliyorum, ama elbette Guid.TryParseasla geri dönmez Guid.Empty. Dize yanlış biçimdeyse, resultçıktı parametresini olarak ayarlar Guid.Empty, ancak geri döner false . Ben dize temsili olabilir Guid.TryParse(s, out guid); if (guid == Guid.Empty) { /* handle invalid s */ }, genellikle yanlış tarzı şeyler yapan kod gördüm çünkü bahsediyorum . sGuid.Empty

14
wow soruyu cevapladınız, ancak sorunun ruhu içinde değil. En büyük sorun başka bir şey :(
nawfal

6
TryParse'ı kullanmak için uygun desen, elbette, daha çok benzerdir if( Guid.TryParse(s, out guid){ /* success! */ } else { /* handle invalid s */ }, bu da giriş değerinin aslında bir Kılavuzun dize temsili olabileceği kırık örnek gibi hiçbir belirsizlik bırakmaz.
Craig

2
Bu cevap Guid.Parse ile ilgili olarak gerçekten doğru olabilir, ancak orijinal sorunun tüm noktasını kaçırmıştır. Hangi Guid.Parse ile ilgisi yoktu, ama Exception vs FormatException / OverflowException / vb yakalamak açısından oldu.
Conor Gallagher

115

İstisna filtreleri artık c # 6+ sürümünde kullanılabilir. Yapabilirsin

try
{
       WebId = new Guid(queryString["web"]);
}
catch (Exception ex) when(ex is FormatException || ex is OverflowException)
{
     WebId = Guid.Empty;
}

C # 7.0+ sürümünde, bunu desen eşleşmesi ile de birleştirebilirsiniz

try
{
   await Task.WaitAll(tasks);
}
catch (Exception ex) when( ex is AggregateException ae &&
                           ae.InnerExceptions.Count > tasks.Count/2)
{
   //More than half of the tasks failed maybe..? 
}

Bu yöntem sadece basit ve açık olduğu için değil, aynı zamanda koşullar karşılanmadığı takdirde yığını gevşetmek zorunda kalmamak için tercih edilir, bu da tekrarlamaya kıyasla daha iyi performans ve teşhis bilgisi sağlar.
joe

74

Uygulamanızı C # 6'ya yükseltebiliyorsanız şanslısınız. Yeni C # sürümü İstisna filtreleri uyguladı. Böylece şunu yazabilirsiniz:

catch (Exception ex) when (ex is FormatException || ex is OverflowException) {
    WebId = Guid.Empty;
}

Bazı insanlar bu kodun aynı olduğunu düşünüyor

catch (Exception ex) {                
    if (ex is FormatException || ex is OverflowException) {
        WebId = Guid.Empty;
    }
    throw;
}

Ama değil. Aslında bu, C # 6'daki önceki sürümlerde taklit edilmesi mümkün olmayan tek yeni özellik. İlk olarak, yeniden atış, yakalamayı atlamaktan daha fazla yük anlamına gelir. İkincisi, anlamsal olarak eşdeğer değildir. Yeni özellik, kodunuzda hata ayıklarken yığını bozulmadan korur. Bu özellik olmadan, çökme dökümü daha az yararlı veya hatta işe yaramaz.

CodePlex ile ilgili bir tartışmaya bakın . Ve farkı gösteren bir örnek .


4
İstisnasız atma yığını korur, ancak "ex atın" üzerine yazılır.
Ivan

32

Bir kullanmak istemiyorsanız ifiçinde açıklama catchgörüş cihazı, içinde C# 6.0kullanabileceğiniz Exception Filterssözdizimi zaten önizleme sürümlerinde CLR tarafından desteklenen ancak varolan edildi VB.NET/ MSIL:

try
{
    WebId = new Guid(queryString["web"]);
}
catch (Exception exception) when (exception is FormatException || ex is OverflowException)
{
    WebId = Guid.Empty;
}

Bu kod Exceptionyalnızca bir InvalidDataExceptionveyaArgumentNullException .

Aslında, bu whenmaddenin içine herhangi bir koşul koyabilirsiniz :

static int a = 8;

...

catch (Exception exception) when (exception is InvalidDataException && a == 8)
{
    Console.WriteLine("Catch");
}

'Kapsamındaki bir ififadenin aksine catch, Exception Filtersatamayacağını Exceptionsve yaptıklarında veya koşul olmadığında true, bir sonraki catchkoşulun değerlendirileceğini unutmayın:

static int a = 7;

static int b = 0;

...

try
{
    throw new InvalidDataException();
}
catch (Exception exception) when (exception is InvalidDataException && a / b == 2)
{
    Console.WriteLine("Catch");
}
catch (Exception exception) when (exception is InvalidDataException || exception is ArgumentException)
{
    Console.WriteLine("General catch");
}

Çıktı: Genel yakalama.

Birden fazla olduğunda true Exception Filter- birincisi kabul edilir:

static int a = 8;

static int b = 4;

...

try
{
    throw new InvalidDataException();
}
catch (Exception exception) when (exception is InvalidDataException && a / b == 2)
{
    Console.WriteLine("Catch");
}
catch (Exception exception) when (exception is InvalidDataException || exception is ArgumentException)
{
    Console.WriteLine("General catch");
}

Çıktı: Yakala.

İçinde gördüğünüz gibi MSILkoduna çevrilmez iftablolarda, bunlarla Filtersve Exceptionsişaretli alanların içinden atmak olamaz Filter 1ve Filter 2ancak atma filtre Exceptionyerine başarısız olur, aynı zamanda son karşılaştırma değeri önce yığınına itilir endfilterkomuta filtrenin başarısını / başarısızlığını belirleyecektir ( Catch 1 XOR Catch 2 buna göre yürütülecektir):

İstisna Filtreleri MSIL

Ayrıca, spesifik olarak Guidyer alır Guid.TryParseyöntem.


Birden çok filtre kullanıldığında ve birden çok filtre kullanıldığında ne olacağına ilişkin açıklama sağlamak için +1
steven87vt

26

C # 7 ile , bir anahtar ifadesinin okunabilirliğini korurken Michael Stum'un yanıtı geliştirilebilir:

catch (Exception ex)
{
    switch (ex)
    {
        case FormatException _:
        case OverflowException _:
            WebId = Guid.Empty;
            break;
        default:
            throw;
    }
}

Ve anahtar ifadesi olarak C # 8 ile:

catch (Exception ex)
{
    WebId = ex switch
    {
        _ when ex is FormatException || ex is OverflowException => Guid.Empty,
        _ => throw ex
    };
}

3
Bu, 2018 IMHO'dan itibaren kabul edilen cevap olmalıdır.
MemphiZ

6
Mat J'nin cevabı when, bir anahtardan çok daha zarif / uygundur.
rgoliveira

@rgoliveira: Soruda sorulan dava için Mat J'nin cevabının daha zarif ve uygun olduğunu kabul ediyorum. Ancak, istisna türüne bağlı olarak yürütmek istediğiniz farklı kodunuz varsa veya istisna örneğini gerçekten kullanmak istiyorsanız okumak zorlaşır. Tüm bu senaryolar bu anahtar deyimiyle eşit şekilde ele alınabilir.
Fabian

1
@Fabian "kural dışı durum türüne bağlı olarak yürütmek istediğiniz farklı kodunuz varsa veya kural dışı durum örneğini kullanmak istiyorsanız", yalnızca farklı bir catchblok yaparsınız veya yine de yayınlamanız gerekir .. . Tecrübelerime göre bir throw;senin içinde catchbloğun muhtemelen bir kod koku olduğunu.
rgoliveira

@rgoliveira: Birkaç vaka bakın bir catch bloğunda bir atımlık kullanma Tamam linki . Case deyimi aslında kalıp eşleme bağlantısı kullandığından , silme operatörü bağlantısını (alt çizgi) değişken bir adla değiştirirseniz yayınlamanız gerekmez . Beni yanlış anlamayın, istisna filtrelerinin bunu yapmak için daha temiz bir yol olduğunu kabul ediyorum, ancak çoklu yakalama blokları çok sayıda kıvırcık parantez ekliyor.
Fabian

20

Kabul edilen cevap kabul edilebilir görünmektedir, ancak CodeAnalysis / FxCop genel bir istisna türü yakaladığından şikayet edecektir.

Ayrıca, "olduğu" operatörün performansı biraz düşürebileceği görülmektedir.

CA1800: Gereksiz şekilde yayınlamayın , "yerine 'operatörünün sonucunu test etmeyi düşünün" der, ancak bunu yaparsanız, her bir istisnayı ayrı ayrı yakaladığınızdan daha fazla kod yazacaksınız.

Her neyse, işte ne yaparım:

bool exThrown = false;

try
{
    // Something
}
catch (FormatException) {
    exThrown = true;
}
catch (OverflowException) {
    exThrown = true;
}

if (exThrown)
{
    // Something else
}

19
Ancak, bunu böyle yaparsanız yığın izlemesini kaybetmeden istisnayı yeniden edemeyeceğinizi unutmayın. (Michael Stum'un kabul edilen cevaba yaptığı yoruma bakınız)
René

2
Bu kalıp istisna depolanarak geliştirilebilir (lütfen kötü biçimlendirmeyi bahane edin - Yorumlara nasıl kod koyacağımı anlayamıyorum): Exception ex = null; {// bir şey} yakalamayı deneyin (FormatException e) {ex = e; } catch (OverflowException e) {ex = e; } if (ex! = null) {// başka bir şey ve ex ile anlaşma}
Jesse Weigert

3
@ JesseWeigert: 1. Bir parça metne mono aralıklı yazı tipi ve açık gri arka plan vermek için backticks kullanabilirsiniz. 2. Yine de stacktrace dahil olmak üzere orijinal istisnayı geri alamazsınız .
Oliver

2
@CleverNeologism, isoperatörün kullanımının performans üzerinde hafif bir olumsuz etkisi olabileceği doğru olsa da, bir istisna işleyicinin performansı optimize etmekle ilgili aşırı endişe duyulan bir yer olmadığı da doğrudur. Uygulamanız, istisna işleyicilerinde performans optimizasyonunun uygulama performansında gerçek bir fark yaratacağı kadar fazla zaman harcıyorsa, dikkatlice bakmanız gereken başka kod sorunları da vardır. Bunu söyledikten sonra, yığın izini kaybettiğiniz ve temizleme, catch deyiminden bağlamsal olarak kaldırıldığı için bu çözümü sevmiyorum.
Craig

3
isOperatörün performansı düşürdüğü tek zaman, daha sonra bir asişlem gerçekleştirmenizdir (bu nedenle kuralı gereksiz şekilde nitelendirir ). Yaptığınız tek şey, oyuncu kadrosunu gerçekten gerçekleştirmeye gerek kalmadan test etmekse, isoperatör tam olarak kullanmak istediğiniz şeydir.
16'da saluce

19

C # 6'da önerilen yaklaşım İstisna Filtreleri kullanmaktır, işte bir örnek:

 try
 {
      throw new OverflowException();
 }
 catch(Exception e ) when ((e is DivideByZeroException) || (e is OverflowException))
 {
       // this will execute iff e is DividedByZeroEx or OverflowEx
       Console.WriteLine("E");
 }

18

Bu Matt'in cevabının bir çeşididir (bunun biraz daha temiz olduğunu hissediyorum) ... bir yöntem kullanın:

public void TryCatch(...)
{
    try
    {
       // something
       return;
    }
    catch (FormatException) {}
    catch (OverflowException) {}

    WebId = Guid.Empty;
}

Diğer istisnalar atılır ve koda WebId = Guid.Empty;çarpılmaz. Diğer istisnaların programınızı kilitlemesini istemiyorsanız, bunu diğer iki yakalamadan SONRA ekleyin:

...
catch (Exception)
{
     // something, if anything
     return; // only need this if you follow the example I gave and put it all in a method
}

-1 Bu, WebId = Guid.Emtpyherhangi bir istisna atılmadığı durumda yürütülür .
Sepster

4
@sepster Burada "// bir şey" den sonra dönüş ifadesinin ima edildiğini düşünüyorum. Çözümü gerçekten sevmiyorum, ama bu tartışmada yapıcı bir varyant. Downvote'unuzu geri almak için +1 :-)
toong

@Sepster toong doğru, oraya bir geri dönüş istiyorsanız, o zaman bir tane koyacağınızı varsaydım ... Benzer ama kesin olmayan soruları olan diğerlerinin fayda sağlayacağı durumlarda tüm cevabımı uygulamak için cevabımı genel yapmaya çalışıyordum. iyi. Ancak, iyi bir ölçü için, yanıtıma bir returnekledim. Giriş için teşekkürler.
bsara

18

Joseph Daigle'ın Cevabı iyi bir çözüm, ancak aşağıdaki yapının biraz daha düzenli ve daha az hataya eğilimli olduğunu gördüm.

catch(Exception ex)
{   
    if (!(ex is SomeException || ex is OtherException)) throw;

    // Handle exception
}

İfadeyi tersine çevirmenin birkaç avantajı vardır:

  • Bir iade beyanı gerekli değildir
  • Kod iç içe değil
  • Joseph'in çözümünde ifadeden ayrılan 'atış' veya 'dönüş' ifadelerini unutma riski yoktur.

Tek bir hatta bile sıkıştırılabilir (çok güzel olmasa da)

catch(Exception ex) { if (!(ex is SomeException || ex is OtherException)) throw;

    // Handle exception
}

Düzenleme: istisna filtreleme sözdizimini biraz daha temiz hale getirecek C # 6.0 ve birlikte gelen diğer yararları sayısı herhangi Geçerli çözüm üzerinde. (özellikle yığını zarar görmeden bırakır)

Aynı sorun C # 6.0 sözdizimi kullanarak nasıl görüneceğini:

catch(Exception ex) when (ex is SomeException || ex is OtherException)
{
    // Handle exception
}

2
+1, bu en iyi cevap. Üst yanıttan daha iyidir, çünkü hayır return, ancak durumu tersine çevirmek de biraz daha iyidir.
DCShannon

Bunu düşünmedim bile. İyi yakaladım, listeye ekleyeceğim.
Stefan T

16

@Micheal

Kodunuzun biraz revize edilmiş versiyonu:

catch (Exception ex)
{
   Type exType = ex.GetType();
   if (exType == typeof(System.FormatException) || 
       exType == typeof(System.OverflowException)
   {
       WebId = Guid.Empty;
   } else {
      throw;
   }
}

Dize karşılaştırmaları çirkin ve yavaştır.


21
Neden yalnızca "is" anahtar kelimesini kullanmıyorsunuz?
Chris Pietschmann

29
@Michael - Microsoft, diyelim ki, FormatException türetilmiş StringTooLongException tanıttı, o zaman hala bir biçim istisnası, sadece belirli bir. Bu, 'bu tam istisna türünü yakala' veya 'dizenin biçiminin yanlış olduğu anlamına gelen istisnaları yakala' semantiklerinin olup olmamasına bağlıdır.
Greg Beech

6
@Michael - Ayrıca, "catch (FormatException ex) son anlambilimine sahip olduğunu, FormatException türetilmiş her şeyi yakalayacağını unutmayın.
Greg Beech

14
@Alex No. "ex" olmadan "atmak", orijinal yığın izlemesi de dahil olmak üzere orijinal istisnayı taşır. "Ex" eklenmesi yığın izlemesini sıfırlar, böylece orijinalden farklı bir istisna elde edersiniz. Eminim başka biri bunu benden daha iyi açıklayabilir :)
Samantha Branham

13
-1: Bu kod, son derece kırılgan - bir kütüphane geliştirici yerine beklenebileceğini throw new FormatException();ile throw new NewlyDerivedFromFormatException();kütüphaneyi kullanarak kod bozmadan, ve birinin kullanıldığı yerler hariç davalarına tüm istisna için de geçerli olacaktır ==yerine is(veya basitçe catch (FormatException)).
Sam Harwell

13

Peki ya

try
{
    WebId = Guid.Empty;
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
}
catch (OverflowException)
{
}

Bu yalnızca Catch-Code tamamen Try-Block'a taşınabiliyorsa işe yarar. Ancak bir nesneye birden çok manipülasyon yaptığınız ve ortadaki bir tanesi başarısız olduğu ve nesneyi "sıfırlamak" istediğiniz görüntüleme kodu.
Michael Stum

4
Bu durumda bir sıfırlama işlevi ekler ve bunu birden fazla catch bloğundan çağırırdım.
Maurice

12
catch (Exception ex)
{
    if (!(
        ex is FormatException ||
        ex is OverflowException))
    {
        throw;
    }
    Console.WriteLine("Hello");
}

11

Dikkatli ve Uyarılmış: Yine başka bir tür, işlevsel bir tarz.

Bağlantıda ne varsa, sorunuzu doğrudan yanıtlamıyor, ancak soruyu aşağıdaki gibi göstermesi önemsiz:

static void Main() 
{ 
    Action body = () => { ...your code... };

    body.Catch<InvalidOperationException>() 
        .Catch<BadCodeException>() 
        .Catch<AnotherException>(ex => { ...handler... })(); 
}

(Temel olarak Catchkendisini döndüren başka bir boş aşırı yük sağlayın )

Bunun en büyük sorusu neden . Ben maliyet burada kazanç ağır basan sanmıyorum :)


1
Bu yaklaşımın olası bir avantajı, bir istisnayı yakalamak ve yeniden yakalamak ile onu yakalamamak arasında anlamsal bir fark olmasıdır; bazı durumlarda, kod yakalanmadan bir istisna üzerinde hareket etmelidir . Böyle bir şey vb.net mümkündür, ancak C # sürece bir kullanımları için bir sarıcı vb.net ile yazılmış ve C # aradı.
supercat

1
Bir istisnayı yakalamadan nasıl davranır? Seni tam olarak anlamıyorum.
nawfal

@nawful ... kullanarak vb filter - function filt (ex ex)): LogEx (ex): return false ... sonra catch satırında: filt (ex) olduğunda ex catch
FastAl

1
@FastAl C # 6'da istisna filtrelerinin izin verdiği bu değil mi?
HimBromBeere

Doğrudan analog olduklarından emin ol
FastAl

9

Güncelleme 2015-12-15: C # 6 için bkz. Https://stackoverflow.com/a/22864936/1718702 . Dilde daha temiz ve artık standart.

Bir kez yakalamak ve istisnaları filtrelemek için daha zarif bir çözüm isteyen insanlar için tasarlanmış , aşağıda gösterildiği gibi bir uzatma yöntemi kullanıyorum.

Bu uzantıyı başlangıçta başka amaçlar için yazılmış olan kütüphanemde zaten vardı, ancak typeistisnaları kontrol etmek için mükemmel çalıştı . Artı, imho, bir sürü ||ifadeden daha temiz görünüyor . Ayrıca, kabul edilen cevabın aksine, açık istisna işlemeyi tercih ederim, bu yüzden ex is ...istenmeyen sınıflar ebeveyn türlerine atanabilir olduğundan istenmeyen davranışları vardı).

kullanım

if (ex.GetType().IsAnyOf(
    typeof(FormatException),
    typeof(ArgumentException)))
{
    // Handle
}
else
    throw;

IsAnyOf.cs Uzantısı (Dependancies için Tam Hata İşleme Örneğine Bakın)

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter matches at least one of the passed in comparisons.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_comparisons">Values to compare against.</param>
        /// <returns>True if a match is found.</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static bool IsAnyOf<T>(this T p_parameter, params T[] p_comparisons)
        {
            // Validate
            p_parameter
                .CannotBeNull("p_parameter");
            p_comparisons
                .CannotBeNullOrEmpty("p_comparisons");

            // Test for any match
            foreach (var item in p_comparisons)
                if (p_parameter.Equals(item))
                    return true;

            // Return no matches found
            return false;
        }
    }
}

Tam Hata İşleme Örneği (Yeni Konsol uygulamasına Kopyala Yapıştır)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.FluentValidation;

namespace IsAnyOfExceptionHandlerSample
{
    class Program
    {
        static void Main(string[] args)
        {
            // High Level Error Handler (Log and Crash App)
            try
            {
                Foo();
            }
            catch (OutOfMemoryException ex)
            {
                Console.WriteLine("FATAL ERROR! System Crashing. " + ex.Message);
                Console.ReadKey();
            }
        }

        static void Foo()
        {
            // Init
            List<Action<string>> TestActions = new List<Action<string>>()
            {
                (key) => { throw new FormatException(); },
                (key) => { throw new ArgumentException(); },
                (key) => { throw new KeyNotFoundException();},
                (key) => { throw new OutOfMemoryException(); },
            };

            // Run
            foreach (var FooAction in TestActions)
            {
                // Mid-Level Error Handler (Appends Data for Log)
                try
                {
                    // Init
                    var SomeKeyPassedToFoo = "FooParam";

                    // Low-Level Handler (Handle/Log and Keep going)
                    try
                    {
                        FooAction(SomeKeyPassedToFoo);
                    }
                    catch (Exception ex)
                    {
                        if (ex.GetType().IsAnyOf(
                            typeof(FormatException),
                            typeof(ArgumentException)))
                        {
                            // Handle
                            Console.WriteLine("ex was {0}", ex.GetType().Name);
                            Console.ReadKey();
                        }
                        else
                        {
                            // Add some Debug info
                            ex.Data.Add("SomeKeyPassedToFoo", SomeKeyPassedToFoo.ToString());
                            throw;
                        }
                    }
                }
                catch (KeyNotFoundException ex)
                {
                    // Handle differently
                    Console.WriteLine(ex.Message);

                    int Count = 0;
                    if (!Validate.IsAnyNull(ex, ex.Data, ex.Data.Keys))
                        foreach (var Key in ex.Data.Keys)
                            Console.WriteLine(
                                "[{0}][\"{1}\" = {2}]",
                                Count, Key, ex.Data[Key]);

                    Console.ReadKey();
                }
            }
        }
    }
}

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter matches at least one of the passed in comparisons.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_comparisons">Values to compare against.</param>
        /// <returns>True if a match is found.</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static bool IsAnyOf<T>(this T p_parameter, params T[] p_comparisons)
        {
            // Validate
            p_parameter
                .CannotBeNull("p_parameter");
            p_comparisons
                .CannotBeNullOrEmpty("p_comparisons");

            // Test for any match
            foreach (var item in p_comparisons)
                if (p_parameter.Equals(item))
                    return true;

            // Return no matches found
            return false;
        }

        /// <summary>
        /// Validates if any passed in parameter is equal to null.
        /// </summary>
        /// <param name="p_parameters">Parameters to test for Null.</param>
        /// <returns>True if one or more parameters are null.</returns>
        public static bool IsAnyNull(params object[] p_parameters)
        {
            p_parameters
                .CannotBeNullOrEmpty("p_parameters");

            foreach (var item in p_parameters)
                if (item == null)
                    return true;

            return false;
        }
    }
}

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter is not null, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentNullException"></exception>
        public static void CannotBeNull(this object p_parameter, string p_name)
        {
            if (p_parameter == null)
                throw
                    new
                        ArgumentNullException(
                        string.Format("Parameter \"{0}\" cannot be null.",
                        p_name), default(Exception));
        }
    }
}

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter is not null or an empty collection, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public static void CannotBeNullOrEmpty<T>(this ICollection<T> p_parameter, string p_name)
        {
            if (p_parameter == null)
                throw new ArgumentNullException("Collection cannot be null.\r\nParameter_Name: " + p_name, default(Exception));

            if (p_parameter.Count <= 0)
                throw new ArgumentOutOfRangeException("Collection cannot be empty.\r\nParameter_Name: " + p_name, default(Exception));
        }

        /// <summary>
        /// Validates the passed in parameter is not null or empty, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentException"></exception>
        public static void CannotBeNullOrEmpty(this string p_parameter, string p_name)
        {
            if (string.IsNullOrEmpty(p_parameter))
                throw new ArgumentException("String cannot be null or empty.\r\nParameter_Name: " + p_name, default(Exception));
        }
    }
}

İki Örnek NUnit Birim Testi

Türler için eşleme davranışı Exceptionkesindir (yani, bir çocuk üst türlerinden hiçbiriyle eşleşmez).

using System;
using System.Collections.Generic;
using Common.FluentValidation;
using NUnit.Framework;

namespace UnitTests.Common.Fluent_Validations
{
    [TestFixture]
    public class IsAnyOf_Tests
    {
        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void IsAnyOf_ArgumentNullException_ShouldNotMatch_ArgumentException_Test()
        {
            Action TestMethod = () => { throw new ArgumentNullException(); };

            try
            {
                TestMethod();
            }
            catch (Exception ex)
            {
                if (ex.GetType().IsAnyOf(
                    typeof(ArgumentException), /*Note: ArgumentNullException derrived from ArgumentException*/
                    typeof(FormatException),
                    typeof(KeyNotFoundException)))
                {
                    // Handle expected Exceptions
                    return;
                }

                //else throw original
                throw;
            }
        }

        [Test, ExpectedException(typeof(OutOfMemoryException))]
        public void IsAnyOf_OutOfMemoryException_ShouldMatch_OutOfMemoryException_Test()
        {
            Action TestMethod = () => { throw new OutOfMemoryException(); };

            try
            {
                TestMethod();
            }
            catch (Exception ex)
            {
                if (ex.GetType().IsAnyOf(
                    typeof(OutOfMemoryException),
                    typeof(StackOverflowException)))
                    throw;

                /*else... Handle other exception types, typically by logging to file*/
            }
        }
    }
}

1
Dil geliştirilmesi olduğunu değil "daha elegent". Birçok yerde bu aslında bir bakım cehennemi yarattı. Yıllar sonra, birçok programcı yarattıkları canavardan gurur duymuyor. Okumak için alıştığın bu değil. Bir "ha?" etkisi, hatta ciddi "WTF'ler". Bazen kafa karıştırıcı. Yaptığı tek şey, daha sonra bakımda uğraşmak zorunda kalanlar için kodu kavramayı çok daha zorlaştırmaktır - çünkü tek bir programcı "zeki" olmaya çalıştı. Yıllar içinde, bu "akıllı" çözümlerin nadiren de iyi çözümler olduğunu öğrendim.
Kaii

1
ya da birkaç kelimeyle: dilin doğal olarak sağladığı olasılıklara sadık kalın. çünkü sadece, bir dilin semantiğini geçersiz kalkmayın sen de onlar gibi yok. Meslektaşlarınız (ve muhtemelen gelecekteki ben) dürüstçe teşekkür edeceğim.
Kaii

Ayrıca, çözümünüzün herhangi bir sürümünde whenolduğu gibi yalnızca C # 6 semantiğine yaklaştığını unutmayın . Gerçek değeri , filtrenin kural dışı durum yakalanmadan önce çalışması ve böylece yeniden atmanın gider / yığın bozulmasından kaçınmasıdır. Daha önce VB ve MSIL tarafından erişilebilen bir CLR özelliğinden yararlanır. catch (Exception ex) {if (...) {/*handle*/} throw;}when
Marc L.

Daha zarif? Bu örnek, bu kadar basit bir sorun için çok büyük ve kod o kadar korkunç görünüyor ki, bir göz atmaya bile değmezdi. Lütfen bu kodu gerçek bir projede başkasının sorunu haline getirmeyin.
KthProg

tüm IsAnyOfyönteminiz basitçe yeniden yazılabilirp_comparisons.Contains(p_parameter)
maksymiuk

7

Bu cevapların sadece yüzeye dokunduğunu hissettiğim için, biraz daha derine inmeye çalıştım.

Yani gerçekten yapmak istediğimiz şey derlenmeyen bir şey, deyin:

// Won't compile... damn
public static void Main()
{
    try
    {
        throw new ArgumentOutOfRangeException();
    }
    catch (ArgumentOutOfRangeException)
    catch (IndexOutOfRangeException) 
    {
        // ... handle
    }

Bunu istememizin nedeni, istisna işleyicinin daha sonra süreçte ihtiyaç duyduğumuz şeyleri yakalamasını istemememizdir. Elbette, bir İstisna yakalayabilir ve ne yapılacağını 'if' ile kontrol edebiliriz, ama dürüst olalım, bunu gerçekten istemiyoruz. (FxCop, hata ayıklayıcı sorunları, çirkinlik)

Öyleyse bu kod neden derlenmiyor - ve bunu öyle olacak şekilde nasıl hackleyebiliriz?

Koda bakarsak, gerçekten yapmak istediğimiz şey çağrıyı yönlendirmektir. Bununla birlikte, MS Bölüm II'ye göre, IL istisna işleyici blokları bu şekilde çalışmaz, bu durumda mantıklıdır çünkü bu 'istisna' nesnesinin farklı tiplere sahip olabileceği anlamına gelir.

Veya kodda yazmak için derleyiciden böyle bir şey yapmasını istiyoruz (bu tamamen doğru değil, ama sanırım mümkün olan en yakın şey):

// Won't compile... damn
try
{
    throw new ArgumentOutOfRangeException();
}
catch (ArgumentOutOfRangeException e) {
    goto theOtherHandler;
}
catch (IndexOutOfRangeException e) {
theOtherHandler:
    Console.WriteLine("Handle!");
}

Bunun derlenmemesinin nedeni oldukça açıktır: '$ exception' nesnesinin hangi türü ve değeri olurdu (burada 'e' değişkenlerinde saklanır)? Derleyicinin bunu ele almasını istediğimiz yol, her iki istisnanın ortak taban türünün 'İstisna' olduğunu, bir değişkenin her iki istisnayı da içermesi için kullandığını ve yalnızca yakalanan iki istisnayı ele aldığını belirtmektir. Bunun IL'de uygulanma şekli, VB.Net'te bulunan 'filtre' şeklindedir.

C # 'da çalışması için doğru' Exception 'taban türüne sahip geçici bir değişkene ihtiyacımız var. Kodun akışını kontrol etmek için bazı dallar ekleyebiliriz. İşte gidiyor:

    Exception ex;
    try
    {
        throw new ArgumentException(); // for demo purposes; won't be caught.
        goto noCatch;
    }
    catch (ArgumentOutOfRangeException e) {
        ex = e;
    }
    catch (IndexOutOfRangeException e) {
        ex = e;
    }

    Console.WriteLine("Handle the exception 'ex' here :-)");
    // throw ex ?

noCatch:
    Console.WriteLine("We're done with the exception handling.");

Bunun bariz dezavantajları, düzgün bir şekilde tekrar atamamamız ve - dürüst olalım - oldukça çirkin bir çözüm olmasıdır. Çirkinlik, çözümü ortadan kaldırarak şube eliminasyonu yaparak biraz sabitlenebilir:

Exception ex = null;
try
{
    throw new ArgumentException();
}
catch (ArgumentOutOfRangeException e)
{
    ex = e;
}
catch (IndexOutOfRangeException e)
{
    ex = e;
}
if (ex != null)
{
    Console.WriteLine("Handle the exception here :-)");
}

Bu sadece 'yeniden atış' bırakır. Bunun işe yaraması için 'catch' bloğunun içindeki işlemeyi yapabilmemiz gerekir - ve bu işi yapmanın tek yolu çekici bir 'İstisna' nesnesidir.

Bu noktada, aşırı yük çözünürlüğü kullanarak veya İstisna'yı ele almak için farklı İstisna türlerini işleyen ayrı bir fonksiyon ekleyebiliriz. Her ikisinin dezavantajları vardır. Başlamak için, bunu bir yardımcı işlevle yapmanın yolu:

private static bool Handle(Exception e)
{
    Console.WriteLine("Handle the exception here :-)");
    return true; // false will re-throw;
}

public static void Main()
{
    try
    {
        throw new OutOfMemoryException();
    }
    catch (ArgumentException e)
    {
        if (!Handle(e)) { throw; }
    }
    catch (IndexOutOfRangeException e)
    {
        if (!Handle(e)) { throw; }
    }

    Console.WriteLine("We're done with the exception handling.");

Ve diğer çözüm, İstisna nesnesini yakalamak ve ona göre işlemektir. Yukarıdaki bağlama göre bunun için en gerçek çeviri şudur:

try
{
    throw new ArgumentException();
}
catch (Exception e)
{
    Exception ex = (Exception)(e as ArgumentException) ?? (e as IndexOutOfRangeException);
    if (ex != null)
    {
        Console.WriteLine("Handle the exception here :-)");
        // throw ?
    }
    else 
    {
        throw;
    }
}

Sonuç olarak:

  • Yeniden atmak istemiyorsak, doğru istisnaları yakalamayı ve geçici olarak saklamayı düşünebiliriz.
  • İşleyici basitse ve kodu tekrar kullanmak istiyorsak, en iyi çözüm muhtemelen bir yardımcı işlev tanıtmaktır.
  • Yeniden atmak istiyorsak, kodu FxCop ve hata ayıklayıcının yakalanmamış istisnalarını kıracak bir 'İstisna' yakalama işleyicisine koymaktan başka seçeneğimiz yok.

7

Bu, her C # geliştiricisinin sonunda karşılaştığı klasik bir sorundur.

Sorunuzu 2 soruya böleyim. İlk,

Aynı anda birden fazla istisna yakalayabilir miyim?

Kısacası, hayır.

Bu da bir sonraki soruya yol açar,

Aynı catch () bloğunda birden fazla istisna türü yakalayamadığım için yinelenen kod yazmayı nasıl önleyebilirim?

Geri dönüş değerinin oluşturulması ucuz olduğu özel örneğiniz göz önüne alındığında, aşağıdaki adımları izlemeyi seviyorum:

  1. WebId'yi geri dönüş değerine başlatın.
  2. Geçici bir değişkente yeni bir Kılavuz oluşturun.
  3. WebId'yi tam olarak yapılandırılmış geçici değişkene ayarlayın. Bunu try {} bloğunun son ifadesi yap.

Kod şöyle görünür:

try
{
    WebId = Guid.Empty;
    Guid newGuid = new Guid(queryString["web"]);
    // More initialization code goes here like 
    // newGuid.x = y;
    WebId = newGuid;
}
catch (FormatException) {}
catch (OverflowException) {}

Herhangi bir istisna atılırsa, WebId hiçbir zaman yarı yapılandırılmış değere ayarlanmaz ve Guid.Empty olarak kalır.

Geri dönüş değerini oluşturmak pahalıysa ve bir değeri sıfırlamak çok daha ucuzsa, sıfırlama kodunu kendi işlevine taşıyacağım:

try
{
    WebId = new Guid(queryString["web"]);
    // More initialization code goes here.
}
catch (FormatException) {
    Reset(WebId);
}
catch (OverflowException) {
    Reset(WebId);
}

Bu güzel, "ekolojik kodlama" yani kodunuzu ve veri ayak izinizi düşünüyor ve yarı işlenmiş değerlerde sızıntı olmadığından emin oluyorsunuz. Bu kalıbı takip etmek güzel teşekkürler Jeffrey!
Tahir Khalid

6

Yani her istisna anahtarında çok sayıda kod mu tekrarlıyorsunuz? Bir yöntemi çıkarmak gibi bir şey tanrı fikri olurdu, değil mi?

Kodunuz şu şekildedir:

MyClass instance;
try { instance = ... }
catch(Exception1 e) { Reset(instance); }
catch(Exception2 e) { Reset(instance); }
catch(Exception) { throw; }

void Reset(MyClass instance) { /* reset the state of the instance */ }

Neden kimse bu kod çoğaltma fark ettim merak ediyorum.

C # 6'dan başkaları tarafından daha önce bahsedildiği gibi istisna filtreleri de vardır. Böylece yukarıdaki kodu şu şekilde değiştirebilirsiniz:

try { ... }
catch(Exception e) when(e is Exception1 || e is Exception2)
{ 
    Reset(instance); 
}

3
"Kimse neden kod çoğaltma fark etmedi merak ediyorum." - Ah ne? Sorunun tüm nokta kodu tekrarını ortadan kaldırmaktır.
Mark Amery

4

Bu uzun konuya benim kısa cevabımı eklemek istedim. Belirtilmeyen bir şey, catch deyimlerinin öncelik sırasıdır, daha spesifik olarak yakalamaya çalıştığınız her istisna türünün kapsamının farkında olmanız gerekir.

Örneğin bir "ucu açık" istisna kullanırsanız İstisna diğer tüm yakalamak ifadeleri preceed ve siz (sizin yakalamak tabloların kadar bence bir anti-desen biraz sen zincir can düzenini tersine eğer belli ki ancak derleyici hataları alacak ) tüm catch- Exception türünü en altına koyabilirsiniz ve bu, try..catch bloğunuzda daha yükseğe çıkmayan istisnaları yakalayacaktır:

            try
            {
                // do some work here
            }
            catch (WebException ex)
            {
                // catch a web excpetion
            }
            catch (ArgumentException ex)
            {
                // do some stuff
            }
            catch (Exception ex)
            {
                // you should really surface your errors but this is for example only
                throw new Exception("An error occurred: " + ex.Message);
            }

Millet bu MSDN belgesini incelemenizi tavsiye ederim:

İstisna Hiyerarşisi


4

Belki bir catch yan tümcesi içinde olmayan kodun herhangi bir bölümünde yaptığınız gibi, ortak kodu bir yönteme koymak gibi kodunuzu basit tutmaya çalışın?

Örneğin:

try
{
    // ...
}
catch (FormatException)
{
    DoSomething();
}
catch (OverflowException)
{
    DoSomething();
}

// ...

private void DoSomething()
{
    // ...
}

Bunu nasıl yapacağım, basit olanı bulmaya çalışmak güzel bir desen


3

Bunu yapmanın bir yolunu bulduğumu unutmayın, ancak bu Günlük WTF için daha çok malzeme gibi görünüyor :

catch (Exception ex)
{
    switch (ex.GetType().Name)
    {
        case "System.FormatException":
        case "System.OverflowException":
            WebId = Guid.Empty;
            break;
        default:
            throw;
    }
}

9
-1 oy, +5 WTF :-) Bu bir yanıt olarak işaretlenmemeliydi, ama çok büyük.
Aaron

1
Ne kadar basit yapabileceğimiz önemli değil. Ama boşta oturmadı ve çözmek için görüşlerini ortaya koydu. Gerçekten takdir ediyorum.
Maxymus

2
Aslında bunu yapmayın, C # 6'da İstisna Filtreleri veya diğer cevaplardan birini kullanın - Bunu özellikle "Bu bir yol, ama kötü ve daha iyi bir şey yapmak istiyorum" olarak koydum.
Michael Stum

NEDEN bu kötü? Bir switch deyiminde istisnayı doğrudan kullanamadığınıza şaşırdım.
MKesper

3
@MKesper Kötü olmasının birkaç sebebini görüyorum. Tam olarak nitelenmiş sınıf adlarının, derleyicinin sizi kurtaramadığı yazım hatalarına açık olan dize değişmezleri olarak yazılmasını gerektirir. (Bu, birçok mağazada hata durumlarının daha az test edildiğinden ve bu nedenle önemsiz hataların gözden kaçma olasılığının daha yüksek olması nedeniyle önemlidir.) Ayrıca , belirtilen durumlardan birinin alt sınıfı olan bir istisnayla eşleşemez . Ve, dizeler olduğu için, vakalar VS'nin "Tüm Referansları Bul" gibi araçlar tarafından özlenecektir - belirli bir istisnanın yakalandığı her yere bir temizleme adımı eklemek istiyorsanız ilgili.
Mark Amery

2

Burada bahsetmeye değer. Birden fazla kombinasyona yanıt verebilirsiniz (İstisna hatası ve istisna.mesaj).

TextBox, TextBlock veya CheckBox olarak içerikle bir datagrid'de kontrol nesnesini yayınlamaya çalışırken bir kullanım senaryosu senaryosuna girdim. Bu durumda, döndürülen İstisna aynıydı, ancak mesaj değişti.

try
{
 //do something
}
catch (Exception ex) when (ex.Message.Equals("the_error_message1_here"))
{
//do whatever you like
} 
catch (Exception ex) when (ex.Message.Equals("the_error_message2_here"))
{
//do whatever you like
} 

0

En kısa cevabı (bir tane daha fonksiyonel stil ) önermek istiyorum :

        Catch<FormatException, OverflowException>(() =>
            {
                WebId = new Guid(queryString["web"]);
            },
            exception =>
            {
                WebId = Guid.Empty;
            });

Bunun için System.Action benzeri birkaç "Catch" yöntemi aşırı yüklemesi oluşturmanız gerekir:

    [DebuggerNonUserCode]
    public static void Catch<TException1, TException2>(Action tryBlock,
        Action<Exception> catchBlock)
    {
        CatchMany(tryBlock, catchBlock, typeof(TException1), typeof(TException2));
    }

    [DebuggerNonUserCode]
    public static void Catch<TException1, TException2, TException3>(Action tryBlock,
        Action<Exception> catchBlock)
    {
        CatchMany(tryBlock, catchBlock, typeof(TException1), typeof(TException2), typeof(TException3));
    }

ve böylece istediğiniz kadar. Ancak bunu bir kez yapmanız gerekir ve tüm projelerinizde kullanabilirsiniz (veya bir nuget paketi oluşturduysanız da kullanabiliriz).

Ve CatchMany uygulaması:

    [DebuggerNonUserCode]
    public static void CatchMany(Action tryBlock, Action<Exception> catchBlock,
        params Type[] exceptionTypes)
    {
        try
        {
            tryBlock();
        }
        catch (Exception exception)
        {
            if (exceptionTypes.Contains(exception.GetType())) catchBlock(exception);
            else throw;
        }
    }

ps Kod sadeliği için boş kontroller koymadım, parametre doğrulamaları eklemeyi düşünün.

ps2 Yakalamadan bir değer döndürmek istiyorsanız, aynı Yakalama yöntemlerini yapmak gerekir, ancak parametrelerde Eylem yerine return ve Func ile.


-15

Sadece dene ve iki kez yakala.

try
{
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
    WebId = Guid.Empty;
}
try
{
    WebId = new Guid(queryString["web"]);
}
catch (OverflowException)
{
    WebId = Guid.Empty;
}

Sadece bu kadar basit !!


3
um. bu sorunun amacını yenmektir. Bu sorudan yinelenen koddan kurtulmasını ister. bu cevap daha fazla yinelenen kod ekler.
James Esh

-23

C # 6.0 sürümünde, İstisna Filtreleri özel durum işleme için iyileştirmelerdir

try
{
    DoSomeHttpRequest();
}
catch (System.Web.HttpException e)
{
    switch (e.GetHttpCode())
    {
        case 400:
            WriteLine("Bad Request");
        case 500:
            WriteLine("Internal Server Error");
        default:
            WriteLine("Generic Error");
    }
}

13
Bu örnek, istisna filtrelerinin hiçbir kullanımını göstermez.
kullanıcı247702

Bu, c # 6.0'daki istisnaları filtrelemenin standart yoludur
Kashif

5
İstisna filtrelerinin tam olarak ne olduğuna bir göz atın. Örneğinizde bir istisna filtresi kullanmıyorsunuz. Sizden bir yıl önce gönderilen bu cevapta uygun bir örnek var .
user247702

6
İstisna filtrelemeye bir örnek catch (HttpException e) when e.GetHttpCode() == 400 { WriteLine("Bad Request"; }
saluce
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.