क्लास ए पर एक संपत्ति को कैसे लागू किया जाए जो क्लास ए के एक बच्चे की संपत्ति की संपत्ति को संदर्भित करता है


9

हमारे पास यह कोड है, जो सरल होने पर, इस तरह दिखता है:

public class Room
{
    public Client Client { get; set; }

    public long ClientId
    {
        get
        {
            return Client == null ? 0 : Client.Id;
        }
    }
}

public class Client 
{
    public long Id { get; set; }
}

अब हमारे पास तीन दृष्टिकोण हैं।

1) यह अच्छा कोड है क्योंकि Clientसंपत्ति को हमेशा सेट किया जाना चाहिए (यानी शून्य नहीं) इसलिए ऐसा Client == nullकभी नहीं होगा और आईडी मूल्य 0किसी भी तरह से गलत आईडी दर्शाता है (यह कोड के लेखक की राय है; ;-))

2) आप यह जानने के लिए कॉल करने वाले पर भरोसा नहीं कर सकते कि 0यह एक गलत मूल्य है Idऔर जब Clientसंपत्ति को हमेशा सेट किया जाना चाहिए exception, getतो आपको Clientसंपत्ति को शून्य होने पर फेंक देना चाहिए

3) जब Clientसंपत्ति को हमेशा सेट किया जाना चाहिए, तो आप वापस लौटें Client.Idऔर कोड को NullRefअपवाद छोड़ दें जब Clientसंपत्ति शून्य हो।

इनमें से कौन सबसे सही है? या कोई चौथी संभावना है?


5
एक जवाब नहीं बल्कि सिर्फ एक नोट: कभी संपत्ति पाने वालों के अपवादों को मत फेंको।
ब्रैंडन

3
ब्रैंडन की बात पर विस्तार से जानने के लिए: stackoverflow.com/a/1488488/569777
MetaFight

1
ज़रूर: सामान्य विचार ( msdn.microsoft.com/en-us/library/… , stackoverflow.com/questions/1488472/… , दूसरों के बीच) यह है कि गुण जितना संभव हो उतना कम करना चाहिए, हल्का होना चाहिए और स्वच्छ का प्रतिनिधित्व करना चाहिए , सार्वजनिक रूप से आपकी वस्तु का सामना करना पड़ रहा है, सूचकांक के साथ एक मामूली अपवाद है। डिबगिंग के दौरान किसी प्रॉपर्टी के लिए वॉच विंडो में अपवादों को देखना भी अप्रत्याशित व्यवहार है।
ब्रैंडन

1
आपको एक संपत्ति पाने वाले में कोड लिखने की आवश्यकता नहीं होनी चाहिए। इसका मतलब यह नहीं है कि आपको किसी अपवाद को छिपाने और निगलने की आवश्यकता होती है जो किसी वस्तु को असमर्थित स्थिति में होने के कारण होता है।
बेन

4
तुम सिर्फ Room.Client.Id क्यों नहीं कर सकते? आप उसे Room.ClientId में क्यों लपेटना चाहते हैं? यदि ग्राहक के लिए इसकी जाँच की जाती है, तो Room.HasClient की तरह कुछ क्यों नहीं मिलता है {return client == null; }} कि अधिक सीधे आगे है और अभी भी लोगों को सामान्य Room.Client.Id करते हैं जब उन्हें वास्तव में आईडी की आवश्यकता होती है?
जेम्स

जवाबों:


25

यह खुशबू आ रही है जैसे आपको Roomउन राज्यों की संख्या को सीमित करना चाहिए जो आपकी कक्षा में हो सकते हैं।

बहुत तथ्य यह है कि आप क्या कर रहे हैं के बारे में पूछ रहे हैं जब Clientअशक्त है एक संकेत है कि Roomराज्य का स्थान बहुत बड़ा है।

चीजों को सरल रखने के लिए मैं Clientकिसी भी Roomउदाहरण की संपत्ति को कभी भी शून्य नहीं होने दूंगा। इसका अर्थ है कि कोड Roomसुरक्षित रूप से मान सकता है कि Clientयह कभी भी अशक्त नहीं है।

भविष्य में किसी कारण से यदि Clientहो जाता है nullसे आग्रह करता हूं कि राज्य का समर्थन करने के विरोध। ऐसा करने से आपके रखरखाव की जटिलता बढ़ जाएगी।

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

ऐसा हो सकता है (एक स्वाभाविक रूप से) एक अनहेल्ड अशक्त संदर्भ अपवाद के परिणामस्वरूप।


2
अच्छा लगा। मुझे लगता है कि "चीजों को सरल रखने के लिए मैं किसी भी कमरे के ग्राहक की संपत्ति को कभी भी शून्य होने की अनुमति नहीं दूंगा।" यह वास्तव में सवाल करने से बेहतर है कि यह शून्य होने पर क्या करना है
मिशेल

@ मिचेल यदि आप बाद में उस आवश्यकता को बदलने का निर्णय लेते हैं, तो आप nullमान को एक NullObject(या यों कहें NullClient) से बदल सकते हैं जो तब भी आपके मौजूदा कोड के साथ काम करेगा और जरूरत पड़ने पर आपको एक गैर-मौजूद ग्राहक के लिए व्यवहार को परिभाषित करने की अनुमति देगा। सवाल यह है कि "कोई ग्राहक" मामला व्यवसाय तर्क का हिस्सा है या नहीं।
null

3
पूरी तरह से सही है। असमर्थित स्थिति का समर्थन करने के लिए कोड का एक भी वर्ण न लिखें। बस इसे एक NullRef फेंकने दें।
बेन

@ बेन असहमत; एमएस दिशानिर्देश स्पष्ट रूप से बताते हैं कि संपत्ति पाने वालों को अपवाद नहीं फेंकना चाहिए। और शून्य रेफ को फेंकने की अनुमति तब दी जाती है जब इसके बजाय एक अधिक सार्थक अपवाद फेंका जा सकता है।
एंडी

1
@ और दिशानिर्देश के कारण को समझने से आपको पता चल जाएगा कि यह कब लागू नहीं होगा।
बेन

21

बस कुछ विचार:

क) क्लाइंटआईड के लिए विशेष रूप से क्लाइंट के लिए पहले से ही पब्लिक गेट्टर होने पर एक गेटर क्यों है? मुझे नहीं पता कि क्‍लाइंट के स्‍क्रीन में क्‍लाइंट आईडी को longपत्‍थर में तराशने की सूचना क्‍यों है ।

ख) दूसरी राय के बारे में आप एक स्थिर परिचय दे सकते हैं Invalid_Client_Id

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


5
+1 के लिए "मुझे यह नहीं दिखाई देता है कि क्‍यों क्‍योंकि क्‍लाइंटआईड एक लंबी सूचना है, पत्‍थर को कमरे के हस्‍ताक्षर में तराशा जाना है"
मिशेल

आपकी अंग्रेजी ठीक है। ;) बस इस जवाब को पढ़ने से, मुझे नहीं पता होगा कि आपकी मूल भाषा अंग्रेजी आपके बिना ऐसा कह रही है।
jpmc26

अंक B & C अच्छे हैं; आरई: ए, इसकी एक सुविधा विधि, और यह तथ्य कि उस कमरे में क्लाइंट का एक संदर्भ है, बस एक कार्यान्वयन विवरण हो सकता है (यह तर्क दिया जा सकता है कि क्लाइंट को सार्वजनिक रूप से दिखाई नहीं देना चाहिए)
एंडी

17

मैं तीनों मतों से असहमत हूं। यदि Clientकभी नहीं हो सकता है null, तो यह भी संभव नहीं है कि यह होnull !

  1. Clientकंस्ट्रक्टर में मान सेट करें
  2. ArgumentNullExceptionकंस्ट्रक्टर में एक फेंको ।

तो आपका कोड कुछ इस तरह होगा:

public class Room
{
    private Client theClient;

    public Room(Client client) {
        if(client == null) throw new ArgumentNullException();
        this.theClient = client;
    }

    public Client Client { 
        get { return theClient; }
        set 
        {
            if (value == null) 
                throw new ArgumentNullException();
            theClient = value;
        }
    }

    public long ClientId
    {
        get
        {
            return theClient.Id;
        }
    }
}
public class Client 
{
    public long Id { get; set; }
}

यह आपके कोड से असंबंधित है, लेकिन आप संभवतः बनाने के एक अपरिवर्तनीय संस्करण का उपयोग करके बेहतर होंगे , और फिर यदि ग्राहक बदलता है, तो एक नया कमरा बना सकता है। यह मेरे उत्तर के अन्य पहलुओं की अशक्त सुरक्षा के अलावा आपके कोड की थ्रेड सुरक्षा में सुधार करेगा। इस चर्चा को उत्परिवर्ती बनाम अपरिवर्तनीय पर देखेंRoomtheClient readonly


1
ये नहीं होना चाहिए ArgumentNullException?
मैथ्यू गुइंडन

4
नहीं। प्रोग्राम कोड को कभी भी फेंकना नहीं चाहिए NullReferenceException, इसे .net VM द्वारा फेंका जाता है "जब एक अशक्त ऑब्जेक्ट को निष्क्रिय करने का प्रयास होता है" । आपको एक फेंक ArgumentNullExceptionदिया जाना चाहिए , जिसे फेंक दिया जाता है "जब एक अशक्त संदर्भ (विजुअल बेसिक में कुछ भी नहीं) एक ऐसी विधि को पारित किया जाता है जो इसे एक वैध तर्क के रूप में स्वीकार नहीं करता है।"
फराप

2
@Pharap मैं एक जावा प्रोग्रामर हूं जो C # कोड लिखने का प्रयास कर रहा है, मुझे लगा कि मैं इस तरह की गलतियां करूंगा।
डुर्रोन 597

@ durron597 मुझे अनुमान लगाना चाहिए था कि 'फाइनल' शब्द के इस्तेमाल से (इस मामले में संबंधित सी # कीवर्ड है readonly)। मैंने अभी-अभी संपादित किया है, लेकिन मुझे लगा कि एक टिप्पणी बेहतर तरीके से समझाएगी कि क्यों (भविष्य के पाठकों के लिए)। यदि आपने पहले ही यह उत्तर नहीं दिया था, तो मैं ठीक उसी तरह का समाधान दूंगा। अपरिहार्यता भी एक अच्छा विचार है, लेकिन यह हमेशा उतना आसान नहीं होता जितना कि एक आशा।
फराप

2
गुणों को आम तौर पर अपवाद नहीं फेंकना चाहिए; एक सेगमेंट के बजाय जो ArgumentNullException को फेंकता है, इसके बजाय एक SetClient विधि प्रदान करें। किसी ने प्रश्न पर इस संबंध में एक उत्तर के लिए लिंक पोस्ट किया है।
एंडी

5

दूसरे और तीसरे विकल्प से बचा जाना चाहिए - गेटक को एक अपवाद के साथ कॉलर को स्मैक नहीं करना चाहिए, जिसका कोई नियंत्रण नहीं है।

आपको यह तय करना चाहिए कि क्या क्लाइंट कभी भी अशक्त हो सकता है। यदि ऐसा है, तो आपको कॉल करने के लिए यह जाँचने का एक तरीका प्रदान करना चाहिए कि क्या यह एक्सेस करने से पहले अशक्त है (उदाहरण के लिए, मुवक्किल ग्राहक संपत्ति)।

यदि आप तय करते हैं कि क्लाइंट कभी भी अशक्त नहीं हो सकता है, तो इसे कंस्ट्रक्टर के लिए एक आवश्यक पैरामीटर बनाएं और अपवाद को वहां फेंक दें यदि कोई नल पास हो।

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

long clientId = room.Client.Id;

यदि ग्राहक अशक्त हो सकता है, तो आप कम से कम कॉलर को जिम्मेदारी देंगे:

if (room.Client != null){
    long clientId = room.Client.Id;
    /* other code follows... */
}

1
मुझे लगता है कि "आप एक ग्राहक को पहले से ही प्रदान की जाने वाली सभी चीज़ों को डुप्लिकेट करते हैं" बोल्ड या कम से कम इटैलिक में होना चाहिए।
छपरा

2

यदि कोई शून्य क्लाइंट गुण समर्थित स्थिति है, तो NullObject का उपयोग करने पर विचार करें ।

लेकिन सबसे अधिक संभावना है कि यह एक असाधारण स्थिति है, इसलिए आपको इसे समाप्त करने के लिए असंभव (या बस बहुत सुविधाजनक नहीं) बनाना चाहिए Client:

public Client Client { get; private set; }

public Room(Client client)
{
    Client = client;
}

public void SetClient(Client client)
{
    if (client == null) throw new ArgumentNullException();
    Client = client;
}

यदि यह हालांकि समर्थित नहीं है, तो आधे बेक्ड समाधान पर समय बर्बाद न करें। इस मामले में:

return Client == null ? 0 : Client.Id;

आपने यहाँ NullReferenceException को 'सॉल्व' कर दिया है, लेकिन बड़ी कीमत पर! यह सभी कॉलर्स को Room.ClientId0 की जाँच करने के लिए बाध्य करेगा :

var aRoom = new Room(aClient);
var clientId = aRoom.ClientId;
if (clientId == 0) RestartRoombookingWizard();
else ContinueRoombooking(clientid);

अन्य समय के साथ अजीब कीड़े अन्य (या अपने आप को!) को किसी भी समय "एररकोड" रिटर्न वैल्यू की जांच करना भूल सकते हैं।
इसे खेलते हैं सुरक्षित एन तेजी से विफल। NullReferenceException को फेंकने की अनुमति दें और "शान से" इसे कॉल करने वाले को और अधिक गड़बड़ करने के लिए कॉल करने की अनुमति देने के बजाय कॉल स्टैक तक कहीं अधिक पकड़ें ...

एक अलग नोट पर, अगर आप को उजागर करने के लिए उत्सुक हैं Clientऔर TellDontAsk केClientId बारे में भी सोचते हैं । यदि आप वस्तुओं से बहुत अधिक पूछते हैं, तो उन्हें यह बताने के बजाय कि आप क्या हासिल करना चाहते हैं, आप सब कुछ एक साथ युग्मित कर सकते हैं, जिससे बाद में और अधिक मुश्किल हो जाता है।


इसका NotSupportedExceptionदुरुपयोग हो रहा है, आपको ArgumentNullExceptionइसके बजाय उपयोग करना चाहिए ।
छपरा

1

C # 6.0 के रूप में, जो अब जारी किया गया है, आपको बस यही करना चाहिए,

public class Room
{
    public Room(Client client)
    {
        this.Client = client;
    }

    public Client Client { get; }
}

public class Client
{
    public Client(long id)
    {
        this.Id = id;
    }

    public long Id { get; }
}

की संपत्ति की परवरिश Clientकरने के लिए Roomकैप्सूलीकरण का एक स्पष्ट उल्लंघन और सूखी सिद्धांत है।

यदि आप Idएक Clientके एक का उपयोग करने की जरूरत है Roomआप कर सकते हैं,

var clientId = room.Client?.Id;

ध्यान दें नल कंडिशनल ऑपरेटर का उपयोग , clientIdएक होगा long?, यदि Clientहै null, तो clientIdहोगा null, अन्यथा clientIdका मान होगा Client.Id


लेकिन clientidएक अशक्त लंबा प्रकार है?
मिशेल

@ मिचेल अच्छी तरह से, यदि एक कमरे में एक क्लाइंट होना चाहिए, तो ctor में एक शून्य जांच डालें। तब var clientId = room.Client.Idदोनों सुरक्षित और होंगे long
जॉडरेल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.