सूचनात्मक अपवादों और स्वच्छ कोड को संतुलित करने के अच्छे तरीके क्या हैं?


11

हमारे सार्वजनिक एसडीके के साथ, हम इस बारे में बहुत जानकारीपूर्ण संदेश देना चाहते हैं कि अपवाद क्यों होता है। उदाहरण के लिए:

if (interfaceInstance == null)
{
     string errMsg = string.Format(
          "Construction of Action Argument: {0}, via the empty constructor worked, but type: {1} could not be cast to type {2}.",
          ParameterInfo.Name,
          ParameterInfo.ParameterType,
          typeof(IParameter)
    );

    throw new InvalidOperationException(errMsg);
}

हालाँकि, यह कोड के प्रवाह को अव्यवस्थित करता है, क्योंकि यह कोड क्या कर रहा है के बजाय त्रुटि संदेशों पर बहुत अधिक ध्यान केंद्रित करता है।

एक सहकर्मी ने अपवाद को कुछ इस तरह से फेंकना शुरू कर दिया:

if (interfaceInstance == null)
    throw EmptyConstructor();

...

private Exception EmptyConstructor()
{
    string errMsg = string.Format(
          "Construction of Action Argument: {0}, via the empty constructor worked, but type: {1} could not be cast to type {2}.",
          ParameterInfo.Name,
          ParameterInfo.ParameterType,
          typeof(IParameter)
    );

    return new InvalidOperationException(errMsg);
}

जो कोड लॉजिक को समझने में आसान बनाता है, लेकिन एरर हैंडलिंग को करने के लिए बहुत सारे अतिरिक्त तरीके जोड़ता है।

"लंबे समय से अपवाद-संदेश अव्यवस्था तर्क" समस्या से बचने के अन्य तरीके क्या हैं? मैं मुख्य रूप से मुहावरेदार सी # /। NET के बारे में पूछ रहा हूं, लेकिन अन्य भाषाएं इसे कैसे प्रबंधित करती हैं यह भी सहायक है।

[संपादित करें]

इसके साथ ही प्रत्येक दृष्टिकोण के पेशेवरों और विपक्षों के लिए अच्छा होगा।


4
IMHO आपके सहकर्मी का समाधान बहुत अच्छा है, और मुझे लगता है कि अगर आपको वास्तव में उस तरह के बहुत सारे अतिरिक्त तरीके मिलते हैं, तो आप उनमें से कम से कम कुछ का पुन: उपयोग कर सकते हैं। जब आपके तरीकों का अच्छी तरह से नाम लिया जाता है, तो बहुत से छोटे तरीके ठीक होते हैं, जब तक कि वे आपके प्रोग्राम के आसान-से-समझने वाले बिल्डिंग ब्लॉक्स बनाते हैं - यहाँ ऐसा लगता है।
डॉक्टर ब्राउन

@DocBrown - हाँ, मुझे यह विचार पसंद आया (नीचे एक उत्तर के रूप में, पेशेवरों / विपक्षों के साथ जोड़ा गया), लेकिन दोनों के लिए और अधिक जानकारी के लिए और संभावित तरीकों के लिए, यह अव्यवस्था की तरह भी दिखाई देने लगती है।
फ्रेंडलीग्यू

1
विचार: संदेश को सभी अपवाद विवरण का वाहक न बनाएं । Exception.Dataसंपत्ति का उपयोग करने का एक संयोजन , "पिकी" अपवाद को पकड़ने, कोड को पकड़ने और अपने स्वयं के संदर्भ को जोड़ने के साथ-साथ कैप्चर किए गए कॉल स्टैक के साथ सभी योगदान जानकारी है जो बहुत कम क्रिया संदेशों की अनुमति चाहिए। अंत में System.Reflection.MethodBaseअपने "अपवाद निर्माण" विधि को पास करने के लिए विवरण प्रदान करने का वादा करता है।
राडारबॉब

@radarbob क्या आप यह सुझाव दे रहे हैं कि शायद अपवाद बहुत अधिक हैं? शायद हम इसे लॉगिंग की तरह बहुत अधिक बना रहे हैं।
हितैषी

1
@ मैकलेचन, मैंने पढ़ा कि यहाँ प्रतिमान एक संदेश में जानकारी डालना है जो यह बताने का प्रयास करता है कि 'क्या हुआ है, जरूरी है कि यह व्याकरणिक रूप से सही हो, और AI का ढोंग है: ".. खाली निर्माणकर्ता के माध्यम से काम किया है, लेकिन .." वास्तव में? मेरे भीतर के फोरेंसिक कोडर इसे स्टैक-ट्रेस-री-थ्रो-रीव्स-एंड-अनवाननेस की सामान्य त्रुटि के विकासवादी परिणाम के रूप में देखते हैं Exception.Data। जोर टेलीमेट्री पर कब्जा होना चाहिए। यहां पर रिफलेक्टिंग करना ठीक है, लेकिन यह समस्या को याद करता है।
राडारबॉब

जवाबों:


10

विशेष अपवाद वर्ग क्यों नहीं हैं?

if (interfaceInstance == null)
{
    throw new ThisParticularEmptyConstructorException(<maybe a couple parameters>);
}

यह स्वरूपण और विवरण को अपवाद के रूप में धकेलता है, और मुख्य वर्ग को अप्राप्त छोड़ देता है।


1
पेशेवरों: बहुत साफ और व्यवस्थित, प्रत्येक अपवाद के लिए सार्थक नाम प्रदान करता है। विपक्ष: संभावित रूप से बहुत सारे अतिरिक्त अपवाद वर्ग।
हितैषी

यदि यह मायने रखता है, तो आपके पास सीमित संख्या में अपवाद कक्षाएं हो सकती हैं, उस वर्ग को पास करें जहां अपवाद एक पैरामीटर के रूप में उत्पन्न हुआ है और अधिक सामान्य अपवाद कक्षाओं के अंदर एक विशाल स्विच है। लेकिन यह एक बेहोश गंध है - यकीन नहीं कि मैं वहाँ जाऊँगा।
ptyx

वास्तव में बदबू आ रही है (कक्षाओं के साथ कसकर युग्मित अपवाद)। मुझे लगता है कि अनुकूलन अपवाद संदेशों बनाम अपवादों की संख्या को संतुलित करना मुश्किल होगा।
फ्रेंडलीग्यू

7

Microsoft लगता है (.NET स्रोत को देखकर) कभी-कभी संसाधन / पर्यावरण स्ट्रिंग का उपयोग करते हैं। उदाहरण के लिए ParseDecimal:

throw new OverflowException(Environment.GetResourceString("Overflow_Decimal"));

पेशेवरों:

  • अपवाद संदेशों को केंद्रीकृत करना, पुनः उपयोग की अनुमति देना
  • अपवाद संदेश रखना (यकीनन जो कोड के लिए कोई फर्क नहीं पड़ता) तरीकों के तर्क से दूर
  • फेंके जाने वाले अपवाद का प्रकार स्पष्ट है
  • संदेशों को स्थानीय बनाया जा सकता है

विपक्ष:

  • यदि एक अपवाद संदेश बदला जाता है, तो वे सभी बदल जाते हैं
  • अपवाद संदेश अपवाद को फेंकने वाले कोड के लिए आसानी से उपलब्ध नहीं है।
  • संदेश स्थिर है और इसमें कोई जानकारी नहीं है कि क्या मान गलत हैं। यदि आप इसे प्रारूपित करना चाहते हैं, तो यह कोड में अधिक अव्यवस्था है।

2
आपने एक बड़ा लाभ छोड़ दिया: अपवाद पाठ का स्थानीयकरण
17 का 26

@ 17of26 - अच्छा बिंदु, इसे जोड़ा।
फ्रेंडलीग्यू

मैंने आपके उत्तर को रद्द कर दिया, लेकिन इस तरह से किए गए त्रुटि संदेश "स्थिर" हैं; आप उनके साथ संशोधक नहीं जोड़ सकते जैसे ओपी अपने कोड में कर रहा है। तो आप प्रभावी रूप से उसकी कुछ कार्यक्षमता को छोड़ चुके हैं।
रॉबर्ट हार्वे

@RobertHarvey - मैंने इसे एक कॉन के रूप में जोड़ा। यह बताता है कि क्यों अंतर्निहित अपवाद वास्तव में आपको स्थानीय जानकारी कभी नहीं देते हैं। इसके अलावा, FYI I मैं ओपी हूं (मुझे इस समाधान का पता था, लेकिन मैं जानना चाहता था कि क्या अन्य के पास बेहतर समाधान हैं)।
फ्रेंडलीग्यू

6
@ 17of26 एक डेवलपर के रूप में, मैं जुनून के साथ स्थानीयकृत अपवादों से नफरत करता हूं। मुझे उदाहरण के लिए हर बार उन्हें अनलॉक्लाइज़ करना होगा। समाधान के लिए गूगल।
कोनराड मोरावस्की

2

सार्वजनिक एसडीके परिदृश्य के लिए, मैं Microsoft कोड कॉन्ट्रैक्ट्स का उपयोग करने पर दृढ़ता से विचार करूंगा क्योंकि ये सूचनात्मक त्रुटियां, स्थिर जांच प्रदान करते हैं और आप XML डॉक्स और सैंडकास्टल जनरेट हेल्प फाइल्स में जोड़ने के लिए दस्तावेज भी तैयार कर सकते हैं । यह विजुअल स्टूडियो के संस्करणों के लिए भुगतान किए गए सभी में समर्थित है।

एक अतिरिक्त लाभ यह है कि यदि आपके ग्राहक C # का उपयोग कर रहे हैं, तो वे आपके कोड को चलाने से पहले ही संभावित समस्याओं का पता लगाने के लिए आपके कोड अनुबंध संदर्भ असेंबलियों का लाभ उठा सकते हैं।

कोड अनुबंधों के लिए पूर्ण प्रलेखन यहाँ है


2

मैं जिस तकनीक का उपयोग कर रहा हूं , वह एक उपयोगिता फ़ंक्शन को पूरी तरह से सत्यापन और फेंकने के लिए गठबंधन, और आउटसोर्स करना है।

सबसे महत्वपूर्ण लाभ यह है कि यह व्यापारिक तर्क में एक-लाइनर के नीचे कम हो जाता है

मैं शर्त लगाता हूं कि आप तब तक बेहतर नहीं कर सकते जब तक आप इसे और कम नहीं कर सकते - केवल व्यावसायिक असाधारण स्थितियों को ध्यान में रखते हुए, व्यापार तर्क से सभी तर्क सत्यापन और ऑब्जेक्ट-स्टेट गार्ड को समाप्त करने के लिए।

बेशक, ऐसा करने के तरीके हैं - जोरदार टाइप की गई भाषा, "किसी भी अवैध वस्तुओं को कभी भी अनुमति नहीं दी जाती" डिजाइन, अनुबंध द्वारा डिजाइन आदि।

उदाहरण:

internal static class ValidationUtil
{
    internal static void ThrowIfRectNullOrInvalid(int imageWidth, int imageHeight, Rect rect)
    {
        if (rect == null)
        {
            throw new ArgumentNullException("rect");
        }
        if (rect.Right > imageWidth || rect.Bottom > imageHeight || MoonPhase.Now == MoonPhase.Invisible)
        {
            throw new ArgumentException(
                message: "This is uselessly informative",
                paramName: "rect");
        }
    }
}

public class Thing
{
    public void DoSomething(Rect rect)
    {
        ValidationUtil.ThrowIfRectNullOrInvalid(_imageWidth, _imageHeight, rect);
        // rest of your code
    }
}

1

[नोट] मैं इस सवाल से एक जवाब में नकल के मामले में वहाँ इसके बारे में टिप्पणी कर रहे हैं।

प्रत्येक अपवाद को कक्षा की एक विधि में स्थानांतरित करें, जो कि किसी भी तर्क में ले रहा है जिसे स्वरूपण की आवश्यकता है।

private Exception EmptyConstructor()
{
    string errMsg = string.Format(
          "Construction of Action Argument: {0}, via the empty constructor worked, but type: {1} could not be cast to type {2}.",
          ParameterInfo.Name,
          ParameterInfo.ParameterType,
          typeof(IParameter)
    );

    return new InvalidOperationException(errMsg);
}

क्षेत्र में सभी अपवाद विधियों को संलग्न करें , और उन्हें कक्षा के अंत में रखें।

पेशेवरों:

  • संदेश को विधि के मूल तर्क से बाहर रखता है
  • प्रत्येक संदेश में तर्क जानकारी जोड़ने की अनुमति देता है (आप विधि में तर्क पास कर सकते हैं)

विपक्ष:

  • विधि अव्यवस्था। संभावित रूप से आपके पास बहुत सारे तरीके हो सकते हैं जो केवल अपवादों को वापस करते हैं और वास्तव में व्यावसायिक तर्क से संबंधित नहीं हैं।
  • अन्य कक्षाओं में संदेशों का पुन: उपयोग नहीं कर सकता

मुझे लगता है कि आपने जो फायदे का हवाला दिया था, उसके फायदों का हवाला दिया।
नेपोनपिर

@neontapir विपक्ष सभी आसानी से संबोधित कर रहे हैं। सहायक तरीकों को ध्यान में रखा जाना चाहिए और कुछ में वर्गीकृत किया गया है #region #endregion(जिसके कारण उन्हें डिफ़ॉल्ट रूप से आईडीई से छिपाया जा सकता है), और यदि वे विभिन्न वर्गों में लागू होते हैं, तो उन्हें एक internal static ValidationUtilityकक्षा में रखें। Btw, कभी भी C # प्रोग्रामर के सामने लंबे पहचानकर्ता नामों की शिकायत न करें।
rwong

मैं हालांकि क्षेत्रों के बारे में शिकायत करूंगा। मेरे दिमाग में, यदि आप खुद को क्षेत्रों का सहारा लेना चाहते हैं, तो कक्षा में शायद बहुत अधिक जिम्मेदारियां थीं।
नॉनटापिर

0

यदि आप थोड़ी अधिक सामान्य त्रुटियों के साथ दूर हो सकते हैं, तो आप सार्वजनिक स्थैतिक जेनेरिक कास्ट फ़ंक्शन को लिख सकते हैं, जो स्रोत प्रकार को संक्रमित करता है:

public static I CastOrThrow<I,T>(T t, string source)
{
    if (t is I)
        return (I)t;

    string errMsg = string.Format(
          "Failed to complete {0}, because type: {1} could not be cast to type {2}.",
          source,
          typeof(T),
          typeof(I)
        );

    throw new InvalidOperationException(errMsg);
}


/// and then:

var interfaceInstance = SdkHelper.CastTo<IParameter>(passedObject, "Action constructor");

ऐसे बदलाव (संभव SdkHelper.RequireNotNull()) हैं, जो इनपुट पर केवल आवश्यकताओं की जांच करते हैं, और यदि वे विफल होते हैं, तो फेंक देते हैं, लेकिन इस उदाहरण में परिणाम के निर्माण के साथ कलाकारों का संयोजन आत्मनिर्भर और कॉम्पैक्ट है।

यदि आप .net 4.5 पर हैं, तो संकलक में विधि पैरामीटर / फ़ाइल का नाम एक विधि पैरामीटर के रूप में सम्मिलित करने के तरीके हैं ( CallerMemberAttibute देखें )। लेकिन एसडीके के लिए, आपको संभवतः अपने ग्राहकों को 4.5 पर स्विच करने की आवश्यकता नहीं है।


यह सामान्य रूप से फेंकने वाले अपवादों के बारे में अधिक है (और एक अपवाद में जानकारी को प्रबंधित करना बनाम यह कोड को कितना कम करता है), कास्टिंग के इस विशिष्ट उदाहरण के बारे में नहीं।
फ्रेंडलीग्यू

0

हम व्यापार तर्क त्रुटियों के लिए क्या करना पसंद करते हैं (विशेष रूप से तर्क त्रुटियों आदि नहीं) एक एकल एनम है जो सभी प्रकार की त्रुटियों को परिभाषित करता है:

/// <summary>
/// This enum is used to identify each business rule uniquely.
/// </summary>
public enum BusinessRuleId {

    /// <summary>
    /// Indicates that a valid body weight value of a patient is missing for dose calculation.
    /// </summary>
    [Display(Name = @"DoseCalculation_PatientBodyWeightMissing")]
    PatientBodyWeightMissingForDoseCalculation = 1,

    /// <summary>
    /// Indicates that a valid body height value of a patient is missing for dose calculation.
    /// </summary>
    [Display(Name = @"DoseCalculation_PatientBodyHeightMissing")]
    PatientBodyHeightMissingForDoseCalculation = 2,

    // ...
}

[Display(Name = "...")]गुण संसाधन में प्रमुख त्रुटि संदेश का अनुवाद करने में प्रयोग की जाने वाली फाइल परिभाषित करते हैं।

साथ ही, इस फ़ाइल का उपयोग उन सभी घटनाओं को खोजने के लिए एक प्रारंभिक बिंदु के रूप में किया जा सकता है जहां आपके कोड में एक निश्चित प्रकार की त्रुटि उत्पन्न होती है।

व्यावसायिक नियमों की जाँच विशिष्ट वैध वर्गों को सौंपी जा सकती है जो उल्लंघन किए गए व्यावसायिक नियमों की सूची प्रस्तुत करते हैं।

हम फिर उल्लिखित नियमों का परिवहन करने के लिए एक कस्टम अपवाद प्रकार का उपयोग करते हैं:

[Serializable]
public class BusinessLogicException : Exception {

    /// <summary>
    /// The Business Rule that was violated.
    /// </summary>
    public BusinessRuleId ViolatedBusinessRule { get; set; }

    /// <summary>
    /// Optional: additional parameters to be used to during generation of the error message.
    /// </summary>
    public string[] MessageParameters { get; set; }

    /// <summary>
    /// This exception indicates that a Business Rule has been violated. 
    /// </summary>
    public BusinessLogicException(BusinessRuleId violatedBusinessRule, params string[] messageParameters) {
        ViolatedBusinessRule = violatedBusinessRule;
        MessageParameters = messageParameters;
    }
}

बैकएंड सेवा कॉल जेनेरिक त्रुटि हैंडलिंग कोड में लिपटे हुए हैं, जो उल्लंघन किए गए Busines नियम का एक उपयोगकर्ता के पढ़ने योग्य त्रुटि संदेश में अनुवाद करता है:

public object TryExecuteServiceAction(Action a) {
    try {
        return a();
    }
    catch (BusinessLogicException bex) {
        _logger.Error(GenerateErrorMessage(bex));
    }
}

public string GenerateErrorMessage(BusinessLogicException bex) {
    var translatedError = bex.ViolatedBusinessRule.ToTranslatedString();
    if (bex.MessageParameters != null) {
        translatedError = string.Format(translatedError, bex.MessageParameters);
    }
    return translatedError;
}

यहाँ ToTranslatedString()एक विस्तार विधि दी गई है जो विशेषताओं enumसे संसाधन कुंजियों को पढ़ सकती है [Display]और ResourceManagerइन कुंजियों का अनुवाद करने के लिए उपयोग कर सकती है। संबंधित संसाधन कुंजी के लिए मान में प्लेसहोल्डर हो सकते हैं string.Format, जो प्रदान किए गए से मेल खाते हैं MessageParameters। रेक्स फ़ाइल में एक प्रविष्टि का उदाहरण:

<data name="DoseCalculation_PatientBodyWeightMissing" xml:space="preserve">
    <value>The dose can not be calculated because the body weight observation for patient {0} is missing or not up to date.</value>
    <comment>{0} ... Patient name</comment>
</data>

उदाहरण उपयोग:

throw new BusinessLogicException(BusinessRuleId.PatientBodyWeightMissingForDoseCalculation, patient.Name);

इस दृष्टिकोण के साथ, आप त्रुटि की पीढ़ी से त्रुटि संदेश की पीढ़ी को अलग कर सकते हैं, प्रत्येक नए प्रकार की त्रुटि के लिए एक नया अपवाद वर्ग पेश करने की आवश्यकता के बिना। उपयोगी यदि अलग-अलग दृश्य अलग संदेश प्रदर्शित करें, यदि प्रदर्शित संदेश उपयोगकर्ता भाषा और / या भूमिका आदि पर निर्भर होना चाहिए।


0

मैं इस उदाहरण में गार्ड क्लॉज का उपयोग करूंगा ताकि पैरामीटर चेक का पुन: उपयोग किया जा सके।

इसके अलावा, आप बिल्डर पैटर्न का उपयोग कर सकते हैं ताकि एक से अधिक सत्यापन को जंजीर बनाया जा सके। यह धाराप्रवाह मुखरता की तरह थोड़ा सा दिखेगा ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.