Pertanyaan Tangkap beberapa pengecualian sekaligus?


Tidak disarankan untuk hanya menangkap System.Exception. Sebaliknya, hanya pengecualian "dikenal" yang harus ditangkap.

Sekarang, ini terkadang mengarah ke kode berulang yang tidak perlu, misalnya:

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

Saya bertanya-tanya: Apakah ada cara untuk menangkap kedua pengecualian dan hanya memanggil WebId = Guid.Empty telepon sekali?

Contoh yang diberikan agak sederhana, karena hanya a GUID. Tapi bayangkan kode di mana Anda mengubah objek beberapa kali, dan jika salah satu manipulasi gagal dengan cara yang diharapkan, Anda ingin "mengatur ulang" object. Namun, jika ada pengecualian yang tidak terduga, saya masih ingin membuangnya lebih tinggi.


1709
2017-09-25 20:56


asal


Jawaban:


Menangkap System.Exception dan aktifkan jenisnya

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

    throw;
}

1788
2017-09-25 21:01



EDIT: Saya setuju dengan orang lain yang mengatakan bahwa, pada C # 6.0, filter pengecualian sekarang adalah cara yang sangat baik untuk dilakukan: catch (Exception ex) when (ex is ... || ex is ... )

Kecuali bahwa saya masih agak membenci tata letak satu-panjang dan secara pribadi akan meletakkan kode keluar seperti berikut. Saya pikir ini fungsional seperti itu estetika, karena saya percaya itu meningkatkan pemahaman. Beberapa orang mungkin tidak setuju:

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

ASLI:

Aku tahu aku agak terlambat ke pesta di sini, tapi asap suci ...

Memotong langsung ke pengejaran, jenis ini menggandakan jawaban sebelumnya, tetapi jika Anda benar-benar ingin melakukan tindakan umum untuk beberapa jenis pengecualian dan menjaga semuanya tetap rapi dan rapi dalam lingkup satu metode, mengapa tidak menggunakan lambda / penutupan / fungsi inline untuk melakukan sesuatu seperti berikut ini? Maksud saya, kemungkinan besar Anda akan menyadari bahwa Anda hanya ingin membuat penutupan itu menjadi metode terpisah yang dapat Anda gunakan di semua tempat. Tapi kemudian akan sangat mudah untuk melakukannya tanpa benar-benar mengubah sisa kode secara struktural. Kanan?

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 ); }
}

Saya tidak bisa tidak bertanya-tanya (PERINGATAN: sedikit ironi / sarkasme di depan) mengapa semua usaha ini pada dasarnya hanya mengganti hal-hal berikut:

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

... dengan beberapa variasi gila dari bau kode berikutnya, maksud saya contoh, hanya untuk berpura-pura bahwa Anda menyimpan beberapa penekanan tombol.

// 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;
}

Karena tentunya tidak otomatis lebih mudah dibaca.

Memang, saya meninggalkan tiga contoh yang identik /* write to a log, whatever... */ return; keluar dari contoh pertama.

Tapi itu semacam maksud saya. Kalian telah mendengar fungsi / metode, bukan? Serius Tulis yang sama ErrorHandler berfungsi dan, seperti, memanggilnya dari setiap blok tangkap.

Jika Anda bertanya kepada saya, contoh kedua (dengan if dan is kata kunci) secara signifikan lebih mudah dibaca, dan secara bersamaan lebih rentan kesalahan selama fase pemeliharaan proyek Anda.

Fase pemeliharaan, bagi siapa saja yang mungkin relatif baru untuk pemrograman, akan terdiri dari 98,7% atau lebih dari keseluruhan masa hidup proyek Anda, dan orang miskin yang melakukan pemeliharaan hampir pasti akan menjadi orang lain selain Anda. Dan ada peluang yang sangat bagus bahwa mereka akan menghabiskan 50% waktunya untuk mengutuk nama Anda.

Dan tentu saja FxCop menyalak pada Anda dan Anda harus melakukannya jugamenambahkan atribut ke kode Anda yang memiliki zip tepat untuk menjalankan program, dan hanya ada untuk memberitahu FxCop untuk mengabaikan masalah bahwa dalam 99,9% kasus itu benar-benar benar dalam penandaan. Dan, maaf, saya mungkin salah, tapi bukankah itu atribut "abaikan" akhirnya benar-benar dikompilasi ke dalam aplikasi Anda?

Akan menempatkan keseluruhan if menguji pada satu baris membuatnya lebih mudah dibaca? Saya tidak berpikir demikian. Maksud saya, saya memiliki programmer lain yang berargumentasi keras sekali waktu lalu bahwa menempatkan lebih banyak kode pada satu baris akan membuatnya "berjalan lebih cepat." Tapi tentu saja dia benar-benar gila. Mencoba menjelaskan kepadanya (dengan wajah yang lurus - yang menantang) bagaimana interpreter atau kompiler akan memutuskan garis panjang itu terpisah menjadi satu-instruksi-per-baris pernyataan diskrit - pada dasarnya identik dengan hasil jika dia pergi ke depan dan hanya membuat kode yang dapat dibaca daripada mencoba untuk mengungguli kompiler - tidak memiliki efek padanya sama sekali. Tapi saya ngelantur.

Berapa banyak kurang dapat dibaca apakah ini dapatkan ketika Anda menambahkan tiga jenis pengecualian, satu atau dua bulan dari sekarang? (Jawaban: ia mendapat banyak kurang terbaca).

Salah satu poin utama, sebenarnya, adalah bahwa sebagian besar titik pemformatan kode sumber teks yang kita semua lihat setiap hari adalah membuatnya benar-benar nyata bagi manusia lain apa yang sebenarnya terjadi ketika kode berjalan. Karena compiler mengubah kode sumber menjadi sesuatu yang sama sekali berbeda dan tidak peduli tentang gaya pemformatan kode Anda. Jadi semua-on-one-line benar-benar menyebalkan juga.

Hanya mengatakan ...

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

370
2017-10-12 00:24



Seperti yang orang lain tunjukkan, Anda dapat memiliki if pernyataan di dalam blok tangkap Anda untuk menentukan apa yang sedang terjadi. C # 6 mendukung Exception Filter, sehingga yang berikut ini akan berfungsi:

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

Itu MyFilter Metode kemudian bisa terlihat seperti ini:

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

Alternatifnya, ini bisa dilakukan secara inline (sisi kanan dari pernyataan saat hanya harus berupa ekspresi boolean).

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

Ini berbeda dengan menggunakan an if pernyataan dari dalam catch blokir, menggunakan filter pengecualian tidak akan lepaskan tumpukan.

Kamu bisa mengunduh Visual Studio 2015 untuk memeriksa ini.

Jika Anda ingin terus menggunakan Visual Studio 2013, Anda dapat menginstal paket nuget berikut:

Instal-Paket Microsoft.Net.Compilers

Pada saat penulisan, ini akan termasuk dukungan untuk C # 6.

Mereferensikan paket ini akan menyebabkan proyek dibangun menggunakan   versi spesifik dari kompiler C # dan Visual Basic yang terkandung dalam   paket, yang bertentangan dengan versi sistem apa pun yang diinstal.


239
2018-04-04 13:59



Tidak dalam C # sayangnya, karena Anda memerlukan filter eksepsi untuk melakukannya dan C # tidak mengekspos fitur MSIL. VB.NET memang memiliki kemampuan ini, mis.

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

Apa yang bisa Anda lakukan adalah menggunakan fungsi anonim untuk mengenkapsulasi kode kesalahan Anda, dan kemudian memanggilnya di blok tangkapan tertentu:

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

184
2017-09-25 21:03



Demi kelengkapan, sejak .NET 4.0kode dapat ditulis ulang sebagai:

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

TryParse jangan pernah melempar pengecualian dan mengembalikan false jika format salah, atur WebId ke Guid.Empty.


Sejak C # 7 Anda dapat menghindari memperkenalkan variabel pada baris terpisah:

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

Anda juga dapat membuat metode untuk parsing tuple yang kembali, yang tidak tersedia di .NET Framework namun pada versi 4.6:

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

Dan gunakan mereka seperti ini:

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

Pembaruan tidak berguna berikutnya untuk jawaban tidak berguna ini muncul ketika dekonstruksi out-parameter diimplementasikan dalam C # 12. :)


122
2018-04-13 12:18



Jika Anda dapat meningkatkan aplikasi Anda ke C # 6, Anda beruntung. Versi C # yang baru telah menerapkan filter Exception. Jadi Anda bisa menulis ini:

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

Sebagian orang menganggap kode ini sama dengan

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

Tapi tidak. Sebenarnya ini adalah satu-satunya fitur baru di C # 6 yang tidak mungkin ditiru dalam versi sebelumnya. Pertama, melempar ulang berarti lebih banyak overhead daripada melewatkan tangkapan. Kedua, itu tidak setara semantis. Fitur baru ini mempertahankan tumpukan utuh ketika Anda mendebug kode Anda. Tanpa fitur ini, dump kecelakaan kurang bermanfaat atau bahkan tidak berguna.

Melihat sebuah diskusi tentang ini di CodePlex. Dan sebuah contoh menunjukkan perbedaannya.


62
2018-04-01 12:29



Jika Anda tidak ingin menggunakan if pernyataan dalam catch cakupan, di C# 6.0 Kamu dapat memakai Exception Filters sintaksis yang sudah didukung oleh CLR dalam versi pratinjau tetapi hanya ada di VB.NET/MSIL:

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

Kode ini akan menangkap Exception hanya ketika itu InvalidDataException atau ArgumentNullException.

Sebenarnya, Anda dapat menempatkan pada dasarnya kondisi apa pun di dalamnya when ayat:

static int a = 8;

...

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

Perhatikan bahwa dibandingkan dengan if pernyataan di dalam catchruang lingkup, Exception Filters tidak bisa melempar Exceptions, dan ketika mereka melakukannya, atau ketika kondisi tidak true, selanjutnya catch kondisi akan dievaluasi sebagai gantinya:

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");
}

Output: menangkap Umum.

Ketika ada lebih dari satu true  Exception Filter - yang pertama akan diterima:

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");
}

Output: Tangkap.

Dan seperti yang Anda lihat di MSIL kode tidak diterjemahkan ke if pernyataan, tetapi untuk Filters, dan Exceptions tidak dapat dibuang dari dalam area yang ditandai dengan Filter 1 dan Filter 2 tetapi filter membuang Exception akan gagal sebagai gantinya, juga nilai perbandingan terakhir didorong ke stack sebelum endfilter perintah akan menentukan keberhasilan / kegagalan filter (Catch 1  XOR  Catch 2 akan dieksekusi sesuai):

Exception Filters MSIL

Juga, secara khusus Guid memiliki Guid.TryParse metode.


26
2017-10-07 17:31



Jawaban yang diterima tampaknya dapat diterima, kecuali bahwa CodeAnalysis /FxCop akan mengeluh tentang fakta bahwa itu menangkap jenis pengecualian umum.

Juga, tampaknya operator "adalah" dapat menurunkan kinerja sedikit.

CA1800: Jangan membuang yang tidak perlu mengatakan "pertimbangkan untuk menguji hasil dari 'sebagai' operator", tetapi jika Anda melakukannya, Anda akan menulis lebih banyak kode daripada jika Anda menangkap setiap pengecualian secara terpisah.

Bagaimanapun, inilah yang akan saya lakukan:

bool exThrown = false;

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

if (exThrown)
{
    // Something else
}

19
2017-07-30 17:09



Ini adalah varian dari jawaban Matt (saya merasa bahwa ini sedikit lebih bersih) ... menggunakan metode:

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

    WebId = Guid.Empty;
}

Pengecualian lainnya akan dibuang dan kode WebId = Guid.Empty; tidak akan dipukul. Jika Anda tidak ingin pengecualian lain untuk merusak program Anda, cukup tambahkan ini SETELAH dua tangkapan lainnya:

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

18
2017-08-31 20:51



@Micheal

Versi kode Anda yang sedikit direvisi:

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

Perbandingan string jelek dan lambat.


17
2017-09-25 21:01