यह स्पार्टा है, या यह है?


121

निम्नलिखित एक साक्षात्कार प्रश्न है। मैं एक समाधान के साथ आया था, लेकिन मुझे यकीन नहीं है कि यह क्यों काम करता है।


सवाल:

Spartaकक्षा को संशोधित किए बिना , कुछ कोड लिखें जो MakeItReturnFalseरिटर्न बनाता है false

public class Sparta : Place
{
    public bool MakeItReturnFalse()
    {
        return this is Sparta;
    }
}

मेरा समाधान: (SPOILER)

public class Place
{
public interface Sparta { }
}

लेकिन क्यों करता है Spartaमें MakeItReturnFalse()उल्लेख करने के लिए {namespace}.Place.Spartaके बजाय {namespace}.Sparta?


5
क्या आप कृपया Spoiler Alert या समाधान के लिए कुछ जोड़ सकते हैं, कृपया? मैं इतना निराश हूं कि मुझे इसे अपने दम पर हल करने का मौका नहीं मिला। सवाल वाकई अद्भुत है।
कारोलिस काजेनस

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

1
मुझे इस पोस्ट का शीर्षक नापसंद है; यह codegolf.SE के लिए अधिक फिट है। क्या हम इसे उस चीज़ में बदल सकते हैं जो वास्तव में प्रश्न का वर्णन करती है?
सुपहस्टार

3
प्यारी पहेली। भयानक साक्षात्कार प्रश्न, लेकिन प्यारा पहेली। अब जब आप जानते हैं कि यह कैसे और क्यों काम करता है, तो आपको इस कठिन संस्करण पर एक शॉट लेना चाहिए: stackoverflow.com/q/41319962/88656
एरिक लिपर्ट

जवाबों:


117

लेकिन क्यों करता है Spartaमें MakeItReturnFalse()उल्लेख करने के लिए {namespace}.Place.Spartaके बजाय {namespace}.Sparta?

असल में, क्योंकि यही नाम देखने के नियम कहते हैं। C # 5 विनिर्देश में, संबंधित नामकरण नियम धारा 3.8 ("नाम और प्रकार के नाम") में हैं।

गोलियों की पहली जोड़ी - काट-छाँट और तोड़फोड़ - पढ़ें:

  • यदि नामस्थान-या-प्रकार-नाम फॉर्म का है Iया फॉर्म का I<A1, ..., AK> [तो K = 0 हमारे मामले में] :
    • यदि K शून्य है और नाम-स्थान-या-प्रकार का नाम एक सामान्य विधि घोषणा के भीतर दिखाई देता है [नहीं, कोई सामान्य विधि नहीं]
    • अन्यथा, यदि नाम स्थान-या-प्रकार का नाम एक प्रकार की घोषणा के भीतर दिखाई देता है, तो प्रत्येक उदाहरण के लिए T ()10.3.1), उस प्रकार के उदाहरण प्रकार से शुरू होता है और प्रत्येक संलग्न वर्ग के उदाहरण प्रकार के साथ जारी रहता है या संरचना की घोषणा (यदि कोई हो):
      • यदि Kशून्य है और घोषणा में Tनाम के साथ एक प्रकार का पैरामीटर शामिल है I, तो नामस्थान-या-प्रकार-नाम उस प्रकार के पैरामीटर को संदर्भित करता है। [नहीं]
      • अन्यथा, यदि नाम स्थान-या-प्रकार का नाम प्रकार की घोषणा के शरीर के भीतर प्रकट होता है, और T या उसके किसी भी आधार प्रकार में एक नेस्टेड सुलभ प्रकार होता है जिसमें नाम Iऔर Kप्रकार पैरामीटर होते हैं, तो नाम-स्थान-या-प्रकार-नाम का उल्लेख होता है दिए गए प्रकार के तर्कों के साथ निर्मित प्रकार। [बिंगो!]
  • यदि पिछले चरण असफल रहे थे, तो प्रत्येक नामस्थान के लिए N, उस नामस्थान से शुरू होता है जिसमें नामस्थान-या-प्रकार-नाम होता है, प्रत्येक संलग्न नामस्थान (यदि कोई हो) के साथ जारी रहता है, और वैश्विक स्थान के साथ समाप्त होता है, तो निम्न चरणों का मूल्यांकन किया जाता है। जब तक एक इकाई स्थित है:
    • यदि Kशून्य है और Iएक नामस्थान का नाम है N, तो ... [हां, यह सफल होगा ]

इसलिए कि अंतिम बुलेट बिंदु वह है जो Sparta वर्ग को उठाता है यदि पहली गोली में कुछ भी नहीं मिलता है ... लेकिन जब आधार वर्ग Placeएक इंटरफ़ेस को परिभाषित करता है Sparta, तो यह वर्ग पर विचार करने से पहले हमें मिल जाता है Sparta

ध्यान दें कि यदि आप Place.Spartaइंटरफ़ेस के बजाय नेस्टेड प्रकार को एक वर्ग बनाते हैं , तो यह अभी भी संकलित करता है और वापस लौटता है false- लेकिन संकलक एक चेतावनी जारी करता है क्योंकि यह जानता है कि एक उदाहरण Spartaकभी भी कक्षा का एक उदाहरण नहीं होगा Place.Sparta। इसी तरह यदि आप Place.Spartaएक इंटरफ़ेस रखते हैं लेकिन Spartaक्लास बनाते हैं sealed, तो आपको एक चेतावनी मिलेगी क्योंकि कोई भी Spartaउदाहरण कभी भी इंटरफ़ेस को लागू नहीं कर सकता है।


2
एक और यादृच्छिक अवलोकन: मूल Spartaवर्ग का उपयोग , this is Placeरिटर्न true। हालांकि, जोड़ने public interface Place { }के लिए Spartaवर्ग का कारण बनता है this is Placeवापसी के लिए false। मेरा सिर घूमता है।
बीड़ी

@budi: ठीक है, क्योंकि फिर से, पहले की गोली Placeइंटरफ़ेस के रूप में मिलती है।
जॉन स्कीट

22

जब किसी नाम को उसके मान के लिए हल किया जाता है तो परिभाषा की "निकटता" का उपयोग अस्पष्टताओं को हल करने के लिए किया जाता है। जो भी परिभाषा "निकटतम" है वही चुना जाता है।

इंटरफ़ेस Spartaको एक बेस क्लास के भीतर परिभाषित किया गया है। वर्ग Spartaको नामांकित स्थान में परिभाषित किया गया है। बेस क्लास के भीतर परिभाषित चीजें समान नामस्थान में परिभाषित चीजों की तुलना में "करीब" हैं।


1
और कल्पना करें कि क्या नाम लुकअप इस तरह से काम नहीं करता है। फिर, यदि कोई भी समान नाम के साथ शीर्ष-स्तरीय वर्ग को जोड़ने के लिए हुआ, तो एक आंतरिक वर्ग वाला कार्य कोड टूट जाएगा।
dan04

1
@ dan04: लेकिन इसके बजाय, काम कर कोड है कि नहीं करता है एक नेस्टेड क्लास टूट जाता है, तो किसी को भी एक जोड़ने के लिए होता है नेस्टेड एक शीर्ष स्तर वर्ग के रूप में एक ही नाम के साथ वर्ग। तो यह वास्तव में कुल "जीत" परिदृश्य नहीं है।
जॉन स्कीट

1
@JonSkeet मैं कहूंगा कि ऐसे नेस्टेड क्लास को जोड़ना एक ऐसे क्षेत्र में बदलाव है, जिसमें वर्किंग कोड के प्रभावित होने के उचित कारण हैं, और बदलाव देखने के लिए। पूरी तरह से असंबंधित शीर्ष-स्तरीय वर्ग जोड़ना बहुत दूर हटा दिया गया है।
एंग्यू को अब

2
@JonSkeet क्या यह थोड़ा अलग तरीके से सिर्फ भंगुर बेस क्लास की समस्या नहीं है?
जोओ मेंडेस

1
@ JoãoMendes: हाँ, बहुत ज्यादा।
जॉन स्कीट

1

सुंदर प्रश्न! मैं उन लोगों के लिए थोड़ा लंबा स्पष्टीकरण जोड़ना चाहता हूं जो दैनिक आधार पर सी # नहीं करते हैं ... क्योंकि सवाल सामान्य रूप से नाम समाधान के मुद्दों का एक अच्छा अनुस्मारक है।

मूल कोड लो, निम्नलिखित तरीकों से थोड़ा संशोधित:

  • मूल अभिव्यक्ति (यानी return this is Sparta) के रूप में उनकी तुलना करने के बजाय टाइप नामों को प्रिंट करें ।
  • चलो इंटरफेस को परिभाषित Athenaमें Placeइंटरफेस नाम संकल्प वर्णन करने के लिए सुपर-क्लास।
  • चलो यह भी पता करें thisकि Spartaकक्षा में किस प्रकार का नाम है , बस सब कुछ बहुत स्पष्ट करने के लिए।

कोड इस तरह दिखता है:

public class Place {
    public interface Athena { }
}

public class Sparta : Place
{
    public void printTypeOfThis()
    {
        Console.WriteLine (this.GetType().Name);
    }

    public void printTypeOfSparta()
    {
        Console.WriteLine (typeof(Sparta));
    }

    public void printTypeOfAthena()
    {
        Console.WriteLine (typeof(Athena));
    }
}

अब हम एक Spartaऑब्जेक्ट बनाते हैं और तीन तरीकों को कॉल करते हैं।

public static void Main(string[] args)
    {
        Sparta s = new Sparta();
        s.printTypeOfThis();
        s.printTypeOfSparta();
        s.printTypeOfAthena();
    }
}

हमें जो आउटपुट मिलता है वह है:

Sparta
Athena
Place+Athena

हालाँकि, अगर हम प्लेस क्लास को संशोधित करते हैं और इंटरफ़ेस स्पार्टा को परिभाषित करते हैं:

   public class Place {
        public interface Athena { }
        public interface Sparta { } 
    }

इसके बाद यह है Sparta- इंटरफ़ेस - जो नाम लुकअप मैकेनिज्म के लिए सबसे पहले उपलब्ध होगा और हमारे कोड का आउटपुट बदल जाएगा:

Sparta
Place+Sparta
Place+Athena

इसलिए हमने MakeItReturnFalseसुपरक्लास में स्पार्टा इंटरफेस को परिभाषित करके फ़ंक्शन परिभाषा में टाइप तुलना के साथ प्रभावी रूप से गड़बड़ कर दिया है , जो कि नाम रिज़ॉल्यूशन द्वारा पहले पाया गया है।

लेकिन नाम प्रस्ताव में सुपरक्लास में परिभाषित इंटरफेस को प्राथमिकता देने के लिए C # को क्यों चुना गया? @JonSkeet को पता है! और यदि आप उसका उत्तर पढ़ते हैं तो आपको C # में नाम रिज़ॉल्यूशन प्रोटोकॉल का विवरण मिलेगा।

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