कंस्ट्रक्टर पैरामीटर सत्यापन सी # में - सर्वोत्तम प्रथाओं


34

कंस्ट्रक्टर पैरामीटर सत्यापन के लिए सबसे अच्छा अभ्यास क्या है?

मान लीजिए कि C # का एक साधारण सा हिस्सा है:

public class MyClass
{
    public MyClass(string text)
    {
        if (String.IsNullOrEmpty(text))
            throw new ArgumentException("Text cannot be empty");

        // continue with normal construction
    }
}

क्या एक अपवाद फेंकना स्वीकार्य होगा?

झटपट से पहले मुझे जो विकल्प मिला वह पूर्व-मान्यता था:

public class CallingClass
{
    public MyClass MakeMyClass(string text)
    {
        if (String.IsNullOrEmpty(text))
        {
            MessageBox.Show("Text cannot be empty");
            return null;
        }
        else
        {
            return new MyClass(text);
        }
    }
}

10
"एक अपवाद फेंकने के लिए स्वीकार्य है?" विकल्प क्या है?
एस.लॉट

10
@ एस.लॉट: कुछ मत करो। एक डिफ़ॉल्ट मान सेट करें। कंसोल / लॉगफाइल के लिए एक मेसैज को प्रिंट करें। घंटी बजाओ। पलक झपकना।
FrustratedWithFormsDesigner

3
@FrustratedWithFormsDesigner: "कुछ नहीं?" "पाठ खाली नहीं होगा" कैसे संतुष्ट होगा? "एक डिफ़ॉल्ट मान सेट करें"? पाठ तर्क मान का उपयोग कैसे किया जाएगा? अन्य विचार ठीक हैं, लेकिन उन दोनों को एक राज्य में एक वस्तु का नेतृत्व करना होगा जो इस वर्ग पर बाधाओं का उल्लंघन करता है।
एसएलटी

1
@ S.Lott खुद को दोहराने के डर से, मैं वहाँ कुछ अन्य दृष्टिकोण होने की संभावना के लिए खुला था, जो मुझे नहीं पता था। मुझे विश्वास है कि मैं सभी को जान नहीं रहा हूँ, इतना मैं निश्चित रूप से जानता हूँ।
MPelletier

3
@MPelletier, समय से पहले मापदंडों को मान्य करने से DRY का उल्लंघन होगा। (खुद को दोहराएं नहीं) जैसा कि आपको उस वर्ग के लिए पूर्व-सत्यापन की प्रतिलिपि बनाने की आवश्यकता होगी जो इस वर्ग को तत्काल करने की कोशिश करता है।
कैफीक

जवाबों:


26

मैं कंस्ट्रक्टर में अपने सभी सत्यापन करने के लिए जाता हूं। यह एक जरूरी है क्योंकि मैं लगभग अपरिवर्तनीय वस्तुओं का निर्माण करता हूं। आपके विशिष्ट मामले के लिए मुझे लगता है कि यह स्वीकार्य है।

if (string.IsNullOrEmpty(text))
    throw new ArgumentException("message", "text");

यदि आप .NET 4 का उपयोग कर रहे हैं तो आप ऐसा कर सकते हैं। बेशक यह इस बात पर निर्भर करता है कि क्या आप उस स्ट्रिंग पर विचार करते हैं जिसमें अमान्य होने के लिए केवल सफेद स्थान होता है।

if (string.IsNullOrWhiteSpace(text))
    throw new ArgumentException("message", "text");

मुझे यकीन नहीं है कि उनका "विशिष्ट मामला" इस तरह की विशिष्ट सलाह देने के लिए पर्याप्त विशिष्ट है। अगर यह हम पता नहीं है समझ में आता है कि इस संदर्भ में एक अपवाद फेंक या बस वस्तु वैसे भी अस्तित्व के लिए अनुमति देते हैं।
FrustratedWithFormsDesigner

@FrustratedWithFormsDesigner - मुझे लगता है कि आपने इसके लिए मुझे वोट दिया है? बेशक आप सही हैं कि मैं एक्सट्रपलेशन कर रहा हूं लेकिन स्पष्ट रूप से यह सैद्धांतिक वस्तु अमान्य होनी चाहिए अगर इनपुट टेक्स्ट खाली है। क्या आप वास्तव में Initकिसी प्रकार का त्रुटि कोड प्राप्त करने के लिए कॉल करना चाहते हैं जब एक अपवाद बस काम करेगा और अपनी वस्तु को हमेशा एक वैध स्थिति बनाए रखने के लिए मजबूर करेगा?
ChaosPandion

2
मैं उस पहले मामले को दो अलग-अलग अपवादों में विभाजित करता हूं: एक जो अशक्तता के लिए परीक्षण करता है ArgumentNullExceptionऔर एक फेंकता है और दूसरा जो खाली के लिए परीक्षण करता है और फेंकता है ArgumentException। यदि वे इसके अपवाद से निपटने के लिए चाहते हैं, तो कॉलर को अधिक योग्य होने की अनुमति देता है।
जेसी सी। स्लाइसर

1
@ जेसे - मैंने उस तकनीक का उपयोग किया है लेकिन मैं अभी भी अपनी समग्र उपयोगिता के बारे में बाड़ पर हूं।
ChaosPandion

3
अपरिवर्तनीय वस्तुओं के लिए +1! मैं जहां भी संभव हो उनका उपयोग करता हूं (मैं व्यक्तिगत रूप से लगभग हमेशा पाता हूं)।

22

बहुत सारे लोग कहते हैं कि कंस्ट्रक्टरों को अपवाद नहीं फेंकना चाहिए। इस पृष्ठ पर काइलजी , उदाहरण के लिए, बस यही करता है। ईमानदारी से, मैं एक कारण के बारे में सोच नहीं सकता क्यों नहीं।

सी ++ में, एक कंस्ट्रक्टर से एक अपवाद को फेंकना एक बुरा विचार है, क्योंकि यह आपको आवंटित स्मृति के साथ छोड़ देता है जिसमें एक असंगठित वस्तु होती है जिसका आपके पास कोई संदर्भ नहीं है (यानी यह एक क्लासिक मेमोरी लीक है)। शायद यही वह जगह है जहां से कलंक आता है - पुराने स्कूल C ++ डेवलपर्स के एक झुंड ने अपने C # सीखने को आधा कर दिया और सिर्फ वही लागू किया जो वे C ++ से जानते थे। इसके विपरीत, ऑब्जेक्टिव-सी में Apple ने आवंटन कदम को इनिशियलाइज़ेशन स्टेप से अलग किया, इसलिए उस भाषा में कंस्ट्रक्टर अपवादों को फेंक सकते हैं।

C # मेमोरी को किसी कंस्ट्रक्टर को असफल कॉल से लीक नहीं कर सकता है। यहां तक ​​कि .NET फ्रेमवर्क में कुछ कक्षाएं उनके निर्माणकर्ताओं में अपवादों को फेंक देंगी।


आप C ++ टिप्पणी के साथ कुछ पंख रगड़ने वाले हैं। :)
ChaosPandion

5
एह, सी ++ एक महान भाषा है, लेकिन मेरे पालतू जानवरों में से एक लोग ऐसे हैं जो एक भाषा को अच्छी तरह से सीखते हैं और फिर हर समय उसी तरह लिखते हैं । C # C ++ नहीं है।
चींटी

10
यह अभी भी C # में ctor के अपवादों को फेंकना खतरनाक हो सकता है क्योंकि ctor ने अप्रबंधित संसाधनों को आवंटित किया हो सकता है जिसे तब कभी निपटाया नहीं जाएगा। और फाइनल करने वाले को उस परिदृश्य के खिलाफ रक्षात्मक होने के लिए लिखा जाना चाहिए जहां एक अधूरे निर्माण वाली वस्तु को अंतिम रूप दिया जाता है। लेकिन ये परिदृश्य अपेक्षाकृत दुर्लभ हैं। सी # देव केंद्र पर एक आगामी लेख इन परिदृश्यों और उनके चारों ओर रक्षात्मक रूप से कोड को कवर करने के लिए होगा, इसलिए विवरण के लिए उस स्थान को देखें।
एरिक लिपर्ट

6
एह? ctor से एक अपवाद फेंकने केवल एक चीज आप ग में कर सकते हैं ++ यदि आप वस्तु का निर्माण नहीं कर सकता है, और कोई कारण नहीं है है एक स्मृति रिसाव पैदा करने के लिए (हालांकि स्पष्ट रूप से अपने अप करने के लिए)।
जे.के.

@jk: हम्म, तुम सही हो! हालांकि ऐसा लगता है कि एक विकल्प खराब वस्तुओं को "लाश" के रूप में फ़्लैग करने के लिए है, जो एसटीएल जाहिरा तौर पर अपवादों को फेंकने के बदले में करता है: yosefk.com/c++fqa/exception.html#fqa-17.2 ऑब्जेक्टिव-सी का समाधान बहुत टिडियर है।
चींटी

12

IFF को एक अपवाद फेंको, वर्ग को इसके शब्दार्थ उपयोग के संबंध में एक सुसंगत स्थिति में नहीं रखा जा सकता है। नहीं तो नहीं। कभी भी किसी वस्तु को असंगत स्थिति में रहने की अनुमति नहीं देता है। इसमें पूर्ण कंस्ट्रक्टर प्रदान नहीं करना शामिल है (जैसे कि खाली कंस्ट्रक्टर + इनिशियलाइज़ करना) (इससे पहले कि आपकी वस्तु वास्तव में पूरी तरह से निर्मित हो) ... जस्ट एनओआरई NO!

चुटकी में, हर कोई करता है। मैंने इसे दूसरे दिन एक तंग दायरे में बहुत ही कम उपयोग की गई वस्तु के लिए किया। किसी दिन सड़क के नीचे, मैं या कोई और शायद उस पर्ची की कीमत का अभ्यास में भुगतान करेंगे।

मुझे ध्यान देना चाहिए कि "कंस्ट्रक्टर" से मेरा मतलब है कि ग्राहक उस वस्तु का निर्माण करने के लिए कहता है। वह आसानी से एक वास्तविक निर्माण के अलावा कुछ और हो सकता है जो "कंस्ट्रक्टर" नाम से जाता है। उदाहरण के लिए, C ++ में ऐसा कुछ सिद्धांत IMNSHO का उल्लंघन नहीं करेगा:

struct funky_object
{
  ...
private:
  funky_object();
  bool initialize(std::string);

  friend boost::optional<funky_object> build_funky(std::string);
};
boost::optional<funky_object> build_funky(std::string str)
{
  funky_object fo;
  if (fo.initialize(str)) return fo;
  return boost::optional<funky_object>();
}

चूंकि बनाने का एकमात्र तरीका एक अमान्य वस्तु को कभी अनुमति नहीं देने के सिद्धांत को funky_objectकॉल build_funkyकरके मौजूद है, भले ही वास्तविक "कन्स्ट्रक्टर" काम खत्म नहीं करता है।

यह बहुत सारे अतिरिक्त काम है, हालांकि संदिग्ध लाभ के लिए (शायद नुकसान भी)। मैं अभी भी अपवाद मार्ग पसंद करूंगा।


9

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

public class MyClass
{
    private MyClass(string text)
    {
        //normal construction
    }

    public static MyClass MakeMyClass(string text)
    {
        if (String.IsNullOrEmpty(text))
            return null;
        else
            return new MyClass(text);
    }
}
public class CallingClass
{
    public MyClass MakeMyClass(string text)
    {
        var cls = MyClass.MakeMyClass(text);
        if(cls == null)
             //show messagebox or throw exception
        return cls;
    }
}

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


1
मुझे फायदा नहीं दिख रहा है। अशक्त के लिए एक कारखाने के परिणाम का परीक्षण करना बेहतर क्यों है कि कहीं-न-कहीं ऐसा प्रयास हो सकता है जो निर्माणकर्ता से अपवाद को पकड़ सके? आपका दृष्टिकोण "अपवादों की उपेक्षा करना" प्रतीत होता है, त्रुटियों के लिए तरीकों से वापसी मान को स्पष्ट रूप से जांचने के लिए वापस जाएं। हमने बहुत पहले स्वीकार किया था कि अपवाद आम तौर पर त्रुटियों के लिए प्रत्येक विधि रिटर्न-वैल्यू का स्पष्ट रूप से परीक्षण करने के लिए बेहतर होते हैं, इसलिए आपकी सलाह संदिग्ध लगती है। मैं कुछ औचित्य देखना चाहता हूं कि आपको क्यों लगता है कि सामान्य नियम यहां लागू नहीं होता है।
DW

हमने कभी स्वीकार नहीं किया कि अपवाद कोड वापस करने के लिए बेहतर थे, यदि वे बहुत बार फेंके जाते हैं तो वे एक विनम्र प्रदर्शन हो सकते हैं। हालाँकि, एक कंस्ट्रक्टर से एक अपवाद को फेंकने से मेमोरी लीक हो जाएगी, यहां तक ​​कि .NET में, यदि आपने कुछ ऐसा आवंटित किया है जो अप्रबंधित है। इस केस के लिए आपको अपने फाइनल में बहुत काम करना है। वैसे भी, कारखाना यहां एक अच्छा विचार है, और टीबीएच को एक अशक्त लौटने के बजाय एक अपवाद फेंकना चाहिए। मान लें कि आप केवल कुछ 'खराब' कक्षाएं बनाते हैं, तो कारखाने को फेंक देना बेहतर होता है।
gbjbaanb

2
  • एक कंस्ट्रक्टर का कोई दुष्प्रभाव नहीं होना चाहिए।
    • निजी क्षेत्र के आरंभीकरण से अधिक कुछ भी साइड-इफेक्ट के रूप में देखा जाना चाहिए।
    • साइड-इफ़ेक्ट वाला एक कंस्ट्रक्टर सिंगल-रिस्पांसिबिलिटी-प्रिंसिपल (SRP) को तोड़ता है और ऑब्जेक्ट-ओरिएंटेड-प्रोग्रामिंग (OOP) की भावना के विपरीत चलता है।
  • एक निर्माता को हल्का होना चाहिए और कभी भी असफल नहीं होना चाहिए।
    • उदाहरण के लिए, मैं हमेशा एक कंपकंपी के अंदर एक ब्लॉक को पकड़ने की कोशिश करते हुए देखता हूं। एक निर्माता को अपवाद या लॉग त्रुटियों को नहीं फेंकना चाहिए।

कोई इन दिशानिर्देशों पर यथोचित सवाल कर सकता है और कह सकता है, "लेकिन मैं इन नियमों का पालन नहीं करता, और मेरा कोड ठीक काम करता है!" उस तक, मैं जवाब देता हूं, "ठीक है कि यह सच हो सकता है, जब तक कि यह नहीं है।"

  • एक निर्माणकर्ता के अंदर अपवाद और त्रुटियां बहुत अप्रत्याशित हैं। जब तक उन्हें ऐसा करने के लिए नहीं कहा जाता है, भविष्य के प्रोग्रामर इन कंस्ट्रक्टर कॉल्स को रक्षात्मक कोड के साथ घेरने के लिए इच्छुक नहीं होंगे।
  • यदि कुछ भी उत्पादन में विफल रहता है, तो उत्पन्न स्टैक ट्रेस को पार्स करना मुश्किल हो सकता है। स्टैक ट्रेस का शीर्ष, कंस्ट्रक्टर कॉल को इंगित कर सकता है, लेकिन कंस्ट्रक्टर में बहुत सी चीज़ें होती हैं, और यह वास्तविक LOC को इंगित नहीं कर सकता है जो विफल हो गया।
    • मैंने कई .NET स्टैक ट्रैस को पार्स किया है जहां यह मामला था।

0

यह निर्भर करता है कि MyClass क्या कर रहा है। यदि MyClass वास्तव में एक डेटा रिपॉजिटरी वर्ग था, और पैरामीटर टेक्स्ट एक कनेक्शन स्ट्रिंग था, तो सबसे अच्छा अभ्यास एक ArgumentException को फेंकने के लिए होगा। हालाँकि, यदि MyClass StringBuilder वर्ग (उदाहरण के लिए) है, तो आप इसे खाली छोड़ सकते हैं।

तो यह इस बात पर निर्भर करता है कि विधि के लिए कितना आवश्यक पैरामीटर पाठ है - क्या वस्तु अशक्त या रिक्त मान के साथ है?


2
मुझे लगता है कि प्रश्न का तात्पर्य है कि वर्ग को वास्तव में स्ट्रिंग की आवश्यकता है खाली नहीं है। यह आपके StringBuilder उदाहरण में मामला नहीं है।
सर्जियो अकॉस्टा

तब जवाब हां होना चाहिए - अपवाद को फेंकना स्वीकार्य है। इस त्रुटि को आज़माने / पकड़ने से बचने के लिए, तब आप अपने लिए ऑब्जेक्ट निर्माण को संभालने के लिए फ़ैक्टरी पैटर्न का उपयोग कर सकते हैं, जिसमें एक खाली स्ट्रिंग केस शामिल होगा।
स्टीवन स्ट्रिगा

0

मेरी प्राथमिकता एक डिफ़ॉल्ट सेट करना है, लेकिन मुझे पता है कि जावा में "अपाचे कॉमन्स" पुस्तकालय है जो ऐसा कुछ करता है, और मुझे लगता है कि यह एक अच्छा विचार है। मुझे एक अपवाद को फेंकने के साथ कोई समस्या नहीं दिखती है यदि अमान्य मान ऑब्जेक्ट को अनुपयोगी स्थिति में डाल देगा; एक स्ट्रिंग एक अच्छा उदाहरण नहीं है लेकिन क्या होगा अगर यह गरीब आदमी के डीआई के लिए था? यदि आप इसके nullस्थान पर मान लेते हैं तो आप काम नहीं कर सकते हैं , आइए एक ICustomerRepositoryइंटरफ़ेस कहते हैं । इस तरह की स्थिति में, एक अपवाद को फेंकना चीजों को संभालने का सही तरीका है, मैं कहूंगा।


-1

जब मैं सी ++ में काम करता था, तो मैंने निर्माण रिसाव में अपवाद को फेंकने के लिए उपयोग नहीं किया क्योंकि स्मृति रिसाव समस्या के कारण यह हो सकता है, मैंने इसे कठिन तरीके से सीखा था। सी ++ के साथ काम करने वाला कोई भी व्यक्ति जानता है कि स्मृति रिसाव कितना मुश्किल और समस्याग्रस्त हो सकता है।

लेकिन अगर आप c # / Java में हैं, तो आपको यह समस्या नहीं है, क्योंकि कचरा संग्रहकर्ता मेमोरी एकत्रित करेगा। यदि आप C # का उपयोग कर रहे हैं, तो मुझे लगता है कि यह सुनिश्चित करने के लिए कि डेटा की कमी की गारंटी के लिए अच्छे और सुसंगत तरीके से संपत्ति का उपयोग करना बेहतर है।

public class MyClass
{
    private string _text;
    public string Text 
    {
        get
        {
            return _text;
        } 
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("Text cannot be empty");
            _text = value;
        } 
    }

    public MyClass(string text)
    {
        Text = text;
        // continue with normal construction
    }
}

-3

अपवाद को फेंकना आपके वर्ग के उपयोगकर्ताओं के लिए एक वास्तविक दर्द होगा। यदि सत्यापन एक मुद्दा है तो मैं आम तौर पर ऑब्जेक्ट को 2 चरण प्रक्रिया बनाता हूं।

1 - उदाहरण बनाएँ

2 - वापसी परिणाम के साथ एक प्रारंभिक विधि को बुलाओ।

या

विधि / फ़ंक्शन को कॉल करें जो आपके लिए वर्ग बनाता है जो सत्यापन कर सकता है।

या

एक अलग ध्वज है, जो मुझे विशेष रूप से पसंद नहीं है, लेकिन कुछ लोग करते हैं।


3
@ डंक, यह सिर्फ गलत है।
कैफीक

4
@ चाड, वह आपकी राय है, लेकिन मेरी राय गलत नहीं है। यह आपसे अलग है। मुझे पढ़ने के लिए ट्राइ-कैच कोड अधिक कठिन लगता है और मैंने तब और अधिक त्रुटियां देखी हैं जब लोग अधिक पारंपरिक तरीकों की तुलना में तुच्छ त्रुटि से निपटने के लिए ट्राइ-कैच का उपयोग करते हैं। यह मेरा अनुभव रहा है और यह गलत नहीं है। यह है जो यह है।
डंक

5
सूत्र यह है कि मैं एक निर्माता को कॉल करने के बाद, अगर यह नहीं बनाता है क्योंकि इनपुट अमान्य है, तो यह एक अपवाद है। यदि मैं केवल ऑब्जेक्ट बनाता हूं और एक ध्वज कह रहा हूं, तो एप्लिकेशन का डेटा अब असंगत / अमान्य स्थिति में है isValid = false। एक वर्ग के बनने के बाद परीक्षण करना यदि यह वैध है तो भयानक, बहुत त्रुटि वाला डिज़ाइन है। और, आपने कहा, एक निर्माता है, तो इनिशियलाइज़ कॉल करें ... क्या होगा यदि मैं इनिशियलाइज़ नहीं कहता हूं? फिर क्या? और क्या होगा अगर इनिशियलाइज़ को खराब डेटा मिलता है, तो क्या मैं अब एक अपवाद फेंक सकता हूं? आपकी कक्षा का उपयोग करना मुश्किल नहीं होना चाहिए।
कैफीक

5
@ डंक, यदि आपको अपवादों का उपयोग कठिन लगता है, तो आप उनका गलत उपयोग कर रहे हैं। सीधे शब्दों में, एक कंस्ट्रक्टर को खराब इनपुट प्रदान किया गया था। वह एक अपवाद है। जब तक यह तय नहीं हो जाता है तब तक ऐप को नहीं चलना चाहिए। अवधि। यदि ऐसा होता है, तो आप एक खराब समस्या, खराब डेटा के साथ समाप्त हो जाते हैं। यदि आप अपवाद को संभालना नहीं जानते हैं, तो आप इसे कॉल स्टैक तक बबल नहीं करते हैं, जब तक कि इसका अर्थ यह नहीं है कि इसका अर्थ केवल लॉगिंग है, और निष्पादन को रोकना है। सीधे शब्दों में कहें, तो आपको अपवादों को संभालने की जरूरत नहीं है, न ही उनके लिए परीक्षण करें। वे बस काम करते हैं।
कैफीक

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