سؤال

لا ينصح ببساطة الصيد System.Exception.بدلا من ذلك, فقط "المعروف" الاستثناءات يجب أن تكون اشتعلت.

الآن, هذا يؤدي في بعض الأحيان إلى unneccessary المتكررة رمز ، على سبيل المثال:

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

وأتساءل:هل هناك طريقة للقبض على كل من الاستثناءات فقط WebId = Guid.Empty اتصل مرة واحدة ؟

معين مثال بسيط بدلا من ذلك ، انها فقط GUID.ولكن تخيل البرمجية حيث يمكنك تعديل كائن عدة مرات ، إذا كان أحد التلاعب تفشل في توقع الطريقة التي تريد "إعادة تعيين" ، object.ومع ذلك ، إذا كان هناك استثناء غير متوقع, أنا لا تزال ترغب في رمي أعلى.

هل كانت مفيدة؟

المحلول

قبض System.Exception والتبديل على أنواع

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

    throw;
}

نصائح أخرى

تحرير: أنا لا أتفق مع الآخرين الذين يقولون أن# C 6.0 استثناء الفلاتر هي الآن على ما يرام تماما طريقة للذهاب: catch (Exception ex) when (ex is ... || ex is ... )

إلا أنه لا يزال نوع من الكراهية واحدة طويلة-خط تخطيط شخصيا وضع رمز مثل التالية.أعتقد أن هذا هو وظيفية كما هو الجمالية ، منذ وأعتقد أنه يحسن الفهم.البعض قد نختلف:

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

الأصل:

أعلم أنني تأخرت قليلا إلى طرف هنا لكن الدخان المقدس...

قطع مباشرة إلى مطاردة هذا النوع من التكرارات رد سابق, ولكن إذا كنت حقا ترغب في أداء عمل مشتركة لعدة استثناء أنواع والحفاظ على كل شيء نظيفة ومرتبة ضمن نطاق أسلوب واحد ، لماذا لا مجرد استخدام لامدا/إغلاق/وظيفة مضمنة أن تفعل شيئا مثل ما يلي ؟ أعني, وهناك احتمالات جيدة جدا أن عليك في نهاية المطاف أن يدركوا أن كنت تريد أن تجعل هذا الإغلاق منفصل الطريقة التي يمكنك الاستفادة في كل مكان.ولكن بعد ذلك سوف يكون من السهل فعل ذلك في الواقع دون تغيير بقية رمز هيكليا.أليس كذلك ؟

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

أنا لا يمكن أن تساعد ولكن أتساءل (تحذير: القليل من السخرية/السخرية قدما) لماذا تذهب إلى كل هذا الجهد في الأساس مجرد استبدال التالية:

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

مع بعض الاختلاف مجنون المقبلة رمز رائحة ، أعني سبيل المثال ، إلا أن تدعي أنك إنقاذ ضغطات قليلة.

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

لأنه بالتأكيد ليس تلقائيا أكثر قابلية للقراءة.

منح تركت ثلاثة متطابقة حالات /* write to a log, whatever... */ return; من المثال الأول.

ولكن هذا النوع من وجهة نظري.كلكم قد سمع من وظائف/طرق, صحيح ؟ على محمل الجد.كتابة المشتركة ErrorHandler وظيفة و يطلق عليه من كل catch.

إذا سألتني المثال الثاني (مع if و is الكلمات الرئيسية) هو أقل بكثير للقراءة و في نفس الوقت بشكل ملحوظ أكثر عرضة للخطأ خلال مرحلة الصيانة من المشروع الخاص بك.

مرحلة الصيانة ، عن أي شخص قد يكون جديد نسبيا في البرمجة, سوف تتكون 98.7% أو أكثر من إجمالي عمر المشروع الخاص بك ، المسكين القيام الصيانة يكاد يكون من المؤكد سوف يكون شخص غيرك.و هناك فرصة جيدة أنها سوف تنفق 50% من وقتهم في العمل شتم اسمك.

وبالطبع التقدم ينبح عليك و لذلك عليك أن أيضا إضافة سمة إلى التعليمات البرمجية الخاصة بك التي لديها بالضبط الرمز البريدي علاقة مع تشغيل البرنامج, و هناك فقط أن أقول FxCop تجاهل قضية في 99.9% من الحالات هو الصحيح تماما في المتعثر.و آسفة قد أكون مخطئا, ولكن لا أن "تجاهل" السمة في نهاية المطاف في الواقع جمعت في التطبيق الخاص بك ؟

قد وضع كامل if اختبار على خط واحد جعله أكثر قابلية للقراءة ؟ أنا لا أعتقد ذلك.يعني أنا لم يكون آخر مبرمج بشدة يجادل مرة منذ فترة طويلة أن وضع المزيد من كود في سطر واحد من شأنها أن تجعل "تشغيل أسرع." ولكن بالطبع كان هذيان المكسرات.أحاول أن أشرح له (مع ضحكته--التي كانت صعبة) كيف مترجم أو مترجم من شأنه كسر هذا الطابور الطويل بعيدا إلى المنفصلة واحد-التعليم-في-خط البيانات--أساسا متطابقة إلى نتيجة إذا كان قد ذهب قبل ذلك إلى رمز للقراءة بدلا من محاولة الخروج ذكي مترجم-قد لا تأثير له على الإطلاق.ولكن هذا ليس موضوعنا.

كم أقل للقراءة هل هذا يحصل عند إضافة أكثر من ثلاثة استثناء أنواع شهر أو شهرين من الآن ؟ (الجواب:فإنه يحصل على الكثير أقل للقراءة).

واحدة من النقاط الرئيسية حقا هو أن معظم من وجهة تنسيق النصوص البرمجية المصدر التي نحن نبحث عن كل يوم هو أن تجعل حقا واضحة إلى غيرهم من البشر ما يحدث في الواقع عند تشغيل التعليمات البرمجية.لأن المترجم يتحول رمز المصدر إلى شيء مختلف تماما و لا يمكن أن الرعاية أقل عن رمز نمط التنسيق.لذلك كل على سطر واحد سيء جدا جدا.

فقط أقول...

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

كما أشار آخرون ، يمكن أن يكون if بيان داخل كتلة catch لتحديد ما يجري.C#6 يدعم استثناء المرشحات حتى العمل:

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

على MyFilter طريقة ثم يمكن أن ننظر بشيء من هذا القبيل:

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

بدلا من ذلك, هذا يمكن أن يكون كل ذلك مضمنة (الجانب الأيمن من عند بيان مجرد تعبير منطقي).

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

هذا يختلف عن استخدام if بيان من داخل catch كتلة باستخدام المرشحات استثناء لن الاسترخاء المكدس.

يمكنك تحميل Visual Studio 2015 التحقق من ذلك.

إذا كنت ترغب في الاستمرار في استخدام Visual Studio 2013, يمكنك تثبيت التالية nuget الحزمة:

تثبيت حزمة Microsoft.صافي.المجمعين

في وقت كتابة هذا التقرير ، وسوف يشمل هذا الدعم C# 6.

الرجوع إلى هذه الحزمة سوف يسبب المشروع سيتم بناؤها باستخدام إصدار محدد من C# و Visual Basic المجمعين الواردة في حزمة مقابل أن أي نظام تثبيت الإصدار.

ليس في C# للأسف, كما أنك تحتاج إلى استثناء مرشح للقيام بذلك و C# لا تعرض هذه الميزة من MSIL.VB.NET هل لديك هذه القدرة الرغم من ذلك ، على سبيل المثال

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

ما يمكن أن تفعله هو استخدام وظيفة مجهول لتغليف على رمز الخطأ ، ومن ثم استدعاء في تلك المحددة قبض كتل:

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

لأجل اكتمال منذ .NET 4.0 رمز يمكن إعادة كتابتها كما يلي:

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

TryParse لم يلقي الاستثناءات وإرجاع false إذا كان تنسيق خاطئ ، ووضع WebId إلى Guid.Empty.


منذ C# 7 يمكنك تجنب إدخال متغير في سطر منفصل:

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

يمكنك أيضا إنشاء طرق تحليل عودته الصفوف التي لا تتوفر في .NET Framework حتى الآن من الإصدار 4.6:

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

واستخدامها مثل هذا:

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

القادم عديمة الفائدة تحديث هذا لا طائل منه الجواب يأتي عندما التفكيكية من المعلمات التي يتم تنفيذها في C# 12.:)

إذا كان يمكنك ترقية التطبيق الخاص بك إلى C# 6 كنت محظوظا.الجديد C# نسخة نفذت استثناء المرشحات.حتى تتمكن من كتابة هذا:

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

بعض الناس يعتقدون هذا الرمز هو نفس

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

ولكن ليس لها.في الواقع هذه الميزة الجديدة فقط في C# 6 أنه لا يمكن محاكاتها في الإصدارات السابقة.أولا إعادة رمي يعني المزيد من النفقات العامة من تخطي الصيد.ثانيا ليس لغويا ما يعادلها.ميزة جديدة يحافظ على كومة سليمة عند تصحيح التعليمات البرمجية الخاصة بك.دون هذه الميزة تفريغ التعطل هو أقل فائدة أو حتى عديمة الفائدة.

ترى النقاش حول هذا الموضوع على CodePlexمثال يوضح الفرق.

استثناء الفلاتر متوفرة الآن في c# 6+.يمكنك أن تفعل

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

في C# 7.0+ ، يمكنك الجمع بين هذا مع نمط مطابقة جدا

try
{
   await Task.WaitAll(tasks);
}
catch (Exception ex) when( ex is AggregateException ae )
{
   //do something with members of ae, say ae.InnerExceptions
}

إذا كنت لا ترغب في استخدام if البيان في catch نطاقات ، في C# 6.0 يمكنك استخدام Exception Filters بناء الجملة الذي كان بالفعل بدعم من CLR في معاينات الإصدارات ولكن موجودة فقط في VB.NET/MSIL:

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

هذا الرمز سوف قبض على Exception فقط عندما يكون InvalidDataException أو ArgumentNullException.

في الواقع, يمكنك وضع الأساس أي حالة داخل when شرط:

static int a = 8;

...

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

لاحظ أنه بدلا من if بيان داخل catch's النطاق ، Exception Filters لا يمكن رمي Exceptions, و عندما تفعل ذلك ، أو عندما تكون الحالة غير true, التالي catch الشرط سيتم تقييمها بدلا من ذلك:

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

الإخراج:العامة الصيد.

عندما يكون هناك أكثر من واحد true Exception Filter - أول واحد سوف تكون مقبولة:

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

الإخراج:قبض.

و كما ترون في MSIL رمز لم يتم ترجمتها إلى if البيانات ، ولكن أن Filters, ، Exceptions لا يمكن رمي من داخل المناطق ملحوظ مع Filter 1 و Filter 2 ولكن تصفية رمي Exception سوف تفشل بدلا من ذلك ، كما الماضي مقارنة قيمة دفع إلى المكدس قبل endfilter الأمر ستحدد نجاح/فشل مرشح (Catch 1 XOR Catch 2 سيتم تنفيذ وبناء على ذلك):

Exception Filters MSIL

أيضا ، على وجه التحديد Guid لديه Guid.TryParse الأسلوب.

الجواب المقبول يبدو مقبولا ، إلا أن CodeAnalysis/FxCop سوف يشكو حقيقة أنه هو اصطياد العامة نوع الاستثناء.

أيضا ، على ما يبدو "هو" المشغل قد تخفض الأداء قليلا.

CA1800:لا يلقي داع يقول "النظر في اختبار النتيجة من 'باسم' المشغل بدلا من ذلك" ، ولكن إذا كنت تفعل ذلك, عليك أن تكون الكتابة أكثر من رمز من إذا قبض كل استثناء على حدة.

على أية حال, وهنا ما أود أن تفعل:

bool exThrown = false;

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

if (exThrown)
{
    // Something else
}

في C# 6 النهج الموصى به هو استخدام استثناء الفلاتر هنا مثال:

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

مع C# 7 الجواب من مايكل Stum يمكن أن تتحسن مع الحفاظ على سهولة قراءة بيان التبديل:

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

هذا هو البديل من مات الإجابة (أنا أشعر أن هذا هو قليلا أكثر نظافة)...استخدام الأسلوب:

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

    WebId = Guid.Empty;
}

أي استثناءات أخرى سيتم طرح وقانون WebId = Guid.Empty; لن تصل.إذا كنت لا تريد استثناءات أخرى إلى تعطل البرنامج, فقط إضافة هذا بعد اثنين آخرين المصيد:

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

جوزيف ديغل الجواب هو حل جيد ، ولكن وجدت التالي هيكل إلى أن يكون قليلا أرتب وأقل عرضه للخطأ.

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

    // Handle exception
}

هناك عدد قليل من مزايا عكس التعبير:

  • عودة البيان ليس من الضروري
  • رمز ليس متداخلة
  • لا يوجد خطر من نسيان 'رمي' أو 'العودة' البيانات التي في يوسف حل فصل من التعبير.

حتى أنه يمكن أن يكون ضغط على خط واحد (على الرغم من عدم جميلة جدا)

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

    // Handle exception
}

تحرير: على استثناء تصفية في C# 6.0 جعل الجملة قليلا نظافة يأتي مع عدد من المزايا الأخرى على أي حل.(أبرزها ترك كومة سالمين)

هنا هو كيف نفس المشكلة تبدو باستخدام C# 6.0 الجملة:

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

@مايكل

قليلا النسخة المنقحة من التعليمات البرمجية:

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

سلسلة مقارنات قبيحة و بطيئة.

ماذا عن

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

وحذرت: نوع آخر, نمط وظيفي.

ما هو في الرابط لا يجيب على سؤالك مباشرة ، لكنه تافهة لتمديد أن تبدو مثل:

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

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

(توفر أساسا آخر فارغة Catch الزائد الذي يعود نفسه)

السؤال الأكبر أن هذا هو لماذا.أنا لا أعتقد أن التكلفة تفوق مكاسب هنا :)

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

تحديث 2015-12-15:انظر https://stackoverflow.com/a/22864936/1718702 C#6.انها أنظف و الآن القياسية في اللغة.

موجهة للناس التي تريد أكثر أناقة الحل للقبض مرة واحدة و تصفية الاستثناءات, يمكنني استخدام طريقة التمديد كما هو موضح أدناه.

أنا بالفعل هذا التمديد في مكتبتي ، وكتب في الأصل لأغراض أخرى ، لكنه يعمل فقط تماما type التدقيق على الاستثناءات.بالإضافة إلى, imho ، يبدو أنظف من مجموعة من || التصريحات.أيضا ، على عكس الإجابة المقبولة ، أنا أفضل صريحة التعامل مع استثناء حتى ex is ... قد بمكروه السلوك كما derrived الطبقات احالة أن هناك من الوالدين أنواع).

الاستخدام

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

IsAnyOf.cs امتداد (انظر الكامل معالجة الخطأ مثلا Dependancies)

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

كامل معالجة الأخطاء سبيل المثال (نسخ-لصق إلى وحدة التحكم الجديدة التطبيق)

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

اثنين من عينة NUnit وحدة الاختبارات

مطابقة السلوك Exception أنواع الدقيق (ie.الطفل لا تطابق أي من الوالدين أنواع).

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

منذ أن شعرت هذه الإجابات فقط لمست سطح حاولت أن حفر أعمق قليلا.

إذا ما كنا نريد حقا أن تفعل شيء لا تجمع, يقول:

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

السبب نريد هذا هو لأننا لا نريد معالج الاستثناء للقبض على الأشياء التي نحتاجها في وقت لاحق في هذه العملية.بالتأكيد يمكننا اللحاق استثناء تحقق مع 'إذا' ما يجب القيام به, ولكن دعونا نكون صادقين, نحن حقا لا أريد ذلك.(FxCop ، المصحح القضايا ، uglyness)

فلماذا لا هذا الرمز ترجمة و كيف يمكننا هاك في مثل هذه طريقة أنه سيكون ؟

إذا نظرنا إلى القانون ، ما يمكننا القيام به هو توجيه المكالمة.ومع ذلك ، وفقا MS القسم الثاني, IL استثناء معالج كتل لن يعمل مثل هذا ، وهو في هذه الحالة من المنطقي لأن ذلك يعني أن 'استثناء' وجوه مختلفة الأنواع.

أو أن يكتب في مدونة نسأل المترجم أن تفعل شيئا من هذا القبيل (حسنا هذا ليس صحيحا تماما ، لكنه أقرب شيء ممكن اعتقد):

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

والسبب أن هذا لن تجميع واضح تماما:ما هو نوع و قيمة أن '$استثناء' الكائن (التي هي هنا المخزنة في المتغيرات 'ه')?الطريقة التي نريد مترجم للتعامل مع هذا الأمر هو أن نلاحظ أن قاعدة مشتركة نوع من كل الاستثناءات 'استثناء', استخدام هذا المتغير أن تحتوي على استثناءات ، ثم التعامل فقط اثنين من الاستثناءات التي يتم صيدها.هذا الطريق ينفذ في IL هو 'تصفية', والذي يتوفر في VB.Net.

أن تجعل من العمل في C# ، نحن بحاجة إلى متغير مؤقت الصحيح 'استثناء' قاعدة نوع.للسيطرة على تدفق رمز ، يمكننا أن نضيف بعض الفروع.يذهب هنا:

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

عيوب واضحة لذلك هي أنه لا يمكننا إعادة رمي بشكل صحيح, حسنا دعونا نكون صادقين - أنه تماما القبيح الحل.على uglyness يمكن أن تكون ثابتة قليلا قبل أداء الفرع القضاء ، مما يجعل الحل أفضل قليلا:

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

أن يترك فقط 'إعادة رمي'.لهذا العمل ، ونحن بحاجة إلى أن تكون قادرة على أداء المناولة داخل 'الصيد' كتلة والطريقة الوحيدة لجعل هذا العمل من قبل اصطياد 'استثناء' الكائن.

عند هذه النقطة يمكننا إضافة وظيفة منفصلة أن يتعامل مع أنواع مختلفة من الاستثناءات باستخدام الزائد القرار ، أو التعامل مع الاستثناء.على حد سواء لديها عيوب.لبدء هنا طريقة للقيام بذلك مع وظيفة مساعد:

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

و الحل الآخر هو للقبض على وجوه استثناء والتعامل معها وفقا لذلك.المعنى الحرفي لهذا ، استنادا إلى السياق أعلاه هو:

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

حتى أن نستنتج:

  • إذا لم ترغب في إعادة رمي ، ونحن قد تنظر في اصطياد حق الاستثناءات ، وتخزينها بشكل مؤقت.
  • إذا كان معالج بسيط ، ونحن نريد أن إعادة استخدام التعليمات البرمجية, أفضل حل هو الأرجح إلى استحداث وظيفة مساعد.
  • إذا كنا نريد إعادة رمي لدينا أي خيار سوى وضع كود في 'استثناء' قبض معالج ، والتي سوف كسر التقدم و المصحح هو غير مسك استثناءات.

هذا هو كلاسيكي المشكلة كل C# المطور تواجه في نهاية المطاف.

دعني أوضح السؤال الخاص بك في 2 الأسئلة.الأولى ،

هل يمكنني أن أمسك استثناءات متعددة في وقت واحد ؟

باختصار, لا.

الأمر الذي يؤدي إلى السؤال التالي ،

كيف يمكنني تجنب كتابة تكرار رمز معين لا أستطيع التقاط عدة أنواع الاستثناءات في نفس الصيد() كتلة ؟

إعطاء الخاص بك محددة العينة ، حيث أن التراجع في القيمة هو رخيصة لبناء أحب أن اتبع الخطوات التالية:

  1. تهيئة WebId إلى الرجوع إلى القيمة.
  2. إنشاء Guid جديد في متغير مؤقت.
  3. مجموعة WebId بالكامل شيدت متغير مؤقت.جعل هذا البيان الختامي محاولة{} كتلة.

لذلك تبدو التعليمة البرمجية مثل:

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

إذا كان أي طرح استثناء ، ثم WebId أبدا مجموعة إلى نصف شيدت القيمة ، ويبقى Guid.فارغة.

إذا بناء التراجع قيمة باهظة الثمن ، وإعادة قيمة أرخص بكثير ، ثم أود أن خطوة إعادة تعيين التعليمات البرمجية في الدالة:

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

إذا كنت تكرار الكثير من التعليمات البرمجية داخل كل استثناء-التبديل ؟ يبدو استخراج أسلوب من شأنه أن يكون الله فكرة, أليس كذلك ؟

حتى التعليمات البرمجية الخاصة بك يأتي إلى هذا:

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

أتساءل لماذا لا أحد لاحظ أن رمز الازدواجية.

من C#6 علاوة على ذلك لديهم استثناء-المرشحات كما سبق ذكره من قبل الآخرين.لذلك يمكنك تعديل التعليمات البرمجية أعلاه إلى:

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

أردت أن أضيف بلدي قصيرة الإجابة على هذا الخيط الطويل.شيء لم يتم ذكره هو ترتيب الأسبقية من التقاط البيانات ، وبشكل أكثر تحديدا تحتاج إلى أن يكون على بينة من نطاق كل نوع من الاستثناء تحاول التقاط.

على سبيل المثال إذا كنت تستخدم "جامعا" استثناء كما استثناء وسوف بريسيد الأخرى التقاط البيانات وسوف الواضح الحصول على أخطاء برنامج التحويل البرمجي ولكن إذا كنت عكس النظام يمكنك سلسلة الخاص بك التقاط البيانات (bit مضاد نمط أعتقد) يمكنك وضع التقاط كل استثناء اكتب في أسفل هذا سوف التقاط أي الاستثناءات التي لم تلبي أعلى في try..catch:

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

أنا أوصي الناس مراجعة هذا MSDN الوثيقة:

استثناء الهرمي

ربما في محاولة للحفاظ على التعليمات البرمجية الخاصة بك بسيطة مثل وضع رمز مشترك في الأسلوب ، كما تفعل في أي جزء آخر من التعليمات البرمجية التي لا داخل قبض الحدوث ؟

E. g.:

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

// ...

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

كيف أود أن تفعل ذلك ، في محاولة للعثور على بسيطة جميلة نمط

علما بأنني لم أجد طريقة واحدة للقيام بذلك, ولكن هذا يبدو المواد اليومية WTF:

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

ومن الجدير بالذكر هنا.يمكنك الاستجابة إلى مجموعات متعددة (خطأ استثناء و الاستثناء.رسالة).

ركضت إلى استخدام سيناريو عندما يحاول أن يلقي كائن التحكم في datagrid مع أي محتوى مربع النص ، TextBlock أو مربع.في هذه الحالة عاد الاستثناء نفسه ، ولكن الرسالة متنوعة.

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
} 

أريد أن أقترح أقصر إجابة (أكثر واحد نمط وظيفي):

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

لهذا تحتاج إلى إنشاء العديد من "الصيد" طريقة الزائدة ، على غرار النظام.العمل:

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

وهلم جرا كما يحلو لك.ولكن عليك أن تفعل ذلك مرة واحدة و يمكنك استخدامه في جميع المشاريع الخاصة بك (أو إذا قمت بإنشاء حزمة nuget يمكننا استخدامه أيضا).

و CatchMany التنفيذ:

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

p.s.أنا لم تضع null يتحقق رمز البساطة ، والنظر في إضافة المعلمة التصديقات.

p.s.2 إذا كنت ترغب في إرجاع القيمة من الصيد ، فإنه من الضروري أن تفعل نفس أساليب الصيد ، ولكن مع العوائد و ظائفها بدلا من العمل في المعلمات.

فقط اتصل محاولة القبض مرتين.

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

أنها مجرد أن بسيطة!!

في c# 6.0 استثناء المرشحات تحسينات معالجة الاستثناء

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");
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top