C # स्विच स्टेटमेंट सीमाएँ - क्यों?


140

स्विच स्टेटमेंट लिखते समय, केस स्टेटमेंट में आप क्या स्विच कर सकते हैं, इसकी दो सीमाएँ हैं।

उदाहरण के लिए (और हाँ, मुझे पता है, अगर आप इस तरह की बात कर रहे हैं तो इसका मतलब है कि आपका ऑब्जेक्ट ओरिएंटेड (OO) आर्किटेक्चर iffy है - यह सिर्फ एक कंट्रोल्ड उदाहरण है!)।

  Type t = typeof(int);

  switch (t) {

    case typeof(int):
      Console.WriteLine("int!");
      break;

    case typeof(string):
      Console.WriteLine("string!");
      break;

    default:
      Console.WriteLine("unknown!");
      break;
  }

यहां स्विच () स्टेटमेंट 'एक अभिन्न प्रकार की उम्मीद का मूल्य' के साथ विफल हो जाता है और 'एक निरंतर मूल्य अपेक्षित है' के साथ केस स्टेटमेंट विफल हो जाते हैं।

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



अंतर्निहित प्रकारों पर स्विच करने के लिए एक अन्य विकल्प TypeCode Enum का उपयोग करना है ।
एरिक फिलिप्स

बस, एक ENUM बनाएं और स्विच मामले में NameOf का उपयोग करें। यह एक गतिशील चर पर स्थिर के रूप में काम करेगा।
वैभव।

जवाबों:


98

यह मेरी मूल पोस्ट है, जिसने कुछ बहस छेड़ दी ... क्योंकि यह गलत है :

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


वास्तव में, सी # स्विच बयान है नहीं हमेशा एक निरंतर समय शाखा।

कुछ मामलों में कंपाइलर एक CIL स्विच स्टेटमेंट का उपयोग करेगा जो वास्तव में एक जंप टेबल का उपयोग करके एक निरंतर समय शाखा है। हालांकि, इवान हैमिल्टन द्वारा बताए गए विरल मामलों में कंपाइलर पूरी तरह से कुछ और उत्पन्न कर सकता है।

यह वास्तव में विभिन्न सी # स्विच स्टेटमेंट, कुछ विरल, कुछ सघन, और ildasm.exe उपकरण के साथ परिणामी CIL को देखकर सत्यापित करने के लिए काफी आसान है।


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

कृपया मेरे पोस्ट को नीचे देखें, जहां मैं अपनी राय में निर्णायक रूप से दिखाता हूं, कि स्विच स्टेटमेंट एक निरंतर समय शाखा करता है।
बजे ब्रायन एसेन्क

आपके जवाब के लिए बहुत बहुत धन्यवाद, ब्रायन। कृपया इवान हैमिल्टन का उत्तर देखें ((48259) [ beta.stackoverflow.com/questions/44905/#48259] )। संक्षेप में: आप उस switch निर्देश (CIL) के बारे में बात कर रहे हैं जो switchC # के कथन के समान नहीं है।
mweerden

मैं नहीं मानता कि कंपाइलर स्ट्रिंग्स पर स्विच करने पर लगातार-टाइम ब्रांचिंग करता है।
ड्रू नोक

क्या यह अभी भी C # 7.0 में स्विच केस स्टेटमेंट में पैटर्न मिलान के साथ लागू है?
बी। डैरेन ओल्सन

114

CIL स्विच निर्देश के साथ C # स्विच स्टेटमेंट को भ्रमित न करना महत्वपूर्ण है।

CIL स्विच एक जम्प टेबल है, जिसमें जम्प पतों के एक इंडेक्स की आवश्यकता होती है।

यह तभी उपयोगी है जब C # स्विच के मामले आसन्न हों:

case 3: blah; break;
case 4: blah; break;
case 5: blah; break;

लेकिन बहुत कम उपयोग अगर वे नहीं हैं:

case 10: blah; break;
case 200: blah; break;
case 3000: blah; break;

(आपको केवल 3 स्लॉट के साथ आकार में तालिका ~ 3000 प्रविष्टियों की आवश्यकता होगी)

गैर-आसन्न अभिव्यक्तियों के साथ, कंपाइलर रैखिक प्रदर्शन करना शुरू कर सकता है अगर-और-अगर-तो-और चेक।

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

आसन्न वस्तुओं के थक्कों वाले अभिव्यक्ति सेट के साथ, कंपाइलर बाइनरी ट्री सर्च कर सकता है, और अंत में एक CIL स्विच कर सकता है।

यह "मेस" और "मैट्स" से भरा है, और यह कंपाइलर (मोनो या रोटर के साथ भिन्न हो सकता है) पर निर्भर है।

मैंने अपने मशीन पर आसन्न मामलों का उपयोग करके आपके परिणामों को दोहराया:

10 रास्ता स्विच निष्पादित करने के लिए कुल समय, 10000 पुनरावृत्तियों (एमएस): 25.1383
अनुमानित 10 प्रति स्विच स्विच (एमएस): 0.00251383

50 रास्ता स्विच, 10000 पुनरावृत्तियों (एमएस) को निष्पादित करने का कुल समय: 26.593
अनुमानित 50 प्रति स्विच स्विच (एमएस): 0.0026593

5000 रास्ता स्विच को निष्पादित करने का कुल समय, 10000 पुनरावृत्तियों (एमएस): 23.7094
अनुमानित समय 5000 प्रति स्विच (एमएस): 0.00237094

50000 रास्ता स्विच, 10000 पुनरावृत्तियों (एमएस) को निष्पादित करने का कुल समय: 20.0933
प्रति 50000 स्विच (एमएस) प्रति अनुमानित समय: 0.00200933

फिर मैंने गैर-आसन्न केस एक्सप्रेशन का उपयोग भी किया:

10 रास्ता स्विच, 10000 पुनरावृत्तियों (एमएस) को निष्पादित करने का कुल समय: 19.6189
अनुमानित 10 प्रति स्विच स्विच (एमएस): 0.00196189

500 रास्ता स्विच, 10000 पुनरावृत्तियों (एमएस) को निष्पादित करने का कुल समय: 19.1664
अनुमानित समय 500 प्रति स्विच (एमएस): 0.00191664

5000 रास्ता स्विच को निष्पादित करने का कुल समय, 10000 पुनरावृत्तियों (एमएस): 19.5871
अनुमानित समय 5000 प्रति स्विच (एमएस): 0.00195871

एक गैर-आसन्न 50,000 केस स्विच स्टेटमेंट संकलित नहीं होगा।
"'ConsoleApplication1.Program.Main (string []) के पास संकलन करने के लिए एक अभिव्यक्ति बहुत लंबी या जटिल है

यहाँ क्या मजेदार है, यह है कि बाइनरी ट्री सर्च सीआईएल स्विच इंस्ट्रक्शन की तुलना में थोड़ा (शायद सांख्यिकीय रूप से नहीं) तेज दिखाई देता है।

ब्रायन, आपने " निरंतर " शब्द का उपयोग किया है , जिसका कम्प्यूटेशनल जटिलता सिद्धांत के दृष्टिकोण से बहुत ही निश्चित अर्थ है। यद्यपि सरलीकृत समीपवर्ती पूर्णांक उदाहरण CIL का उत्पादन कर सकता है जिसे O (1) (स्थिर) माना जाता है, एक विरल उदाहरण O (log n) (लघुगणक) है, संकुल उदाहरण बीच में कहीं झूठ बोलते हैं और छोटे उदाहरण O (n) (रैखिक) )।

यह स्ट्रिंग स्थिति को भी संबोधित नहीं करता है, जिसमें एक स्थैतिक Generic.Dictionary<string,int32>बनाया जा सकता है, और पहले उपयोग पर निश्चित ओवरहेड भुगतना होगा। यहां का प्रदर्शन प्रदर्शन पर निर्भर करेगा Generic.Dictionary

यदि आप C # Language Specification (CIL स्पेक नहीं) की जांच करते हैं, तो आपको "15.7.2 मिलेगा। स्विच स्टेटमेंट" में "निरंतर समय" का कोई उल्लेख नहीं है या यह कि अंतर्निहित कार्यान्वयन CIL स्विच इंस्ट्रक्शन का उपयोग भी करता है (मानने में बहुत सावधानी बरतें) ऐसी बातें)।

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


बेशक ये समय मशीनों और स्थितियों पर निर्भर करेगा। मैं इन समय परीक्षणों पर ध्यान नहीं दूंगा, हम जिस माइक्रोसेकंड अवधि की बात कर रहे हैं, वह किसी भी "वास्तविक" कोड द्वारा चलाया जा रहा है (और आपको कुछ "वास्तविक कोड" शामिल करने होंगे अन्यथा कंपाइलर शाखा को अनुकूलित कर देगा), या प्रणाली में घबराना। मेरे उत्तर C # संकलक द्वारा निर्मित CIL की जांच करने के लिए IL DASM का उपयोग करने पर आधारित हैं । बेशक, यह अंतिम नहीं है, क्योंकि वास्तविक निर्देश CPU रन तब JIT द्वारा बनाए जाते हैं।

मैं वास्तव में मेरी x86 मशीन पर निष्पादित अंतिम सीपीयू निर्देशों की जांच कर चुका हूं, और कुछ ऐसा करते हुए एक साधारण आसन्न सेट स्विच की पुष्टि कर सकता हूं:

  jmp     ds:300025F0[eax*4]

बाइनरी ट्री खोज कहाँ से भरी है:

  cmp     ebx, 79Eh
  jg      3000352B
  cmp     ebx, 654h
  jg      300032BB
  
  cmp     ebx, 0F82h
  jz      30005EEE

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

इस तरह के एक छोटे से ऑपरेशन के साथ समय को सही ढंग से शांत करना मुश्किल है। हमने कोड, या परीक्षण प्रक्रियाओं को साझा नहीं किया। मैं यह नहीं देख सकता कि आस-पास के मामलों के लिए उसका समय क्यों बढ़ना चाहिए। मेरा 10x तेज था, इसलिए वातावरण और परीक्षण कोड बहुत भिन्न हो सकते हैं।
इवान हैमिल्टन

23

पहला कारण जो मन में आता है वह ऐतिहासिक है :

चूंकि अधिकांश C, C ++, और Java प्रोग्रामर इस तरह की स्वतंत्रता के आदी नहीं हैं, इसलिए वे उनकी मांग नहीं करते हैं।

एक और, अधिक मान्य, कारण यह है कि भाषा की जटिलता बढ़ जाएगी :

सबसे पहले, ऑब्जेक्ट .Equals()को ==ऑपरेटर के साथ या उसके साथ तुलना की जानी चाहिए ? दोनों कुछ मामलों में मान्य हैं। क्या हमें ऐसा करने के लिए नया सिंटैक्स पेश करना चाहिए? क्या हमें प्रोग्रामर को अपनी तुलना विधि पेश करने की अनुमति देनी चाहिए?

इसके अलावा, वस्तुओं पर स्विच करने की अनुमति स्विच स्टेटमेंट के बारे में अंतर्निहित धारणाओं को तोड़ देगी । स्विच स्टेटमेंट को नियंत्रित करने वाले दो नियम हैं जो संकलक लागू नहीं कर पाएंगे यदि ऑब्जेक्ट्स को स्विच करने की अनुमति दी गई है ( सी # संस्करण 3.0 भाषा विनिर्देश , .78.7.2 देखें):

  • स्विच लेबल के मूल्य स्थिर हैं
  • स्विच लेबल के मूल्य अलग - अलग हैं (ताकि किसी दिए गए स्विच-अभिव्यक्ति के लिए केवल एक स्विच ब्लॉक का चयन किया जा सके)

काल्पनिक मामले में इस कोड उदाहरण पर विचार करें कि गैर-स्थिर मामले मानों की अनुमति दी गई थी:

void DoIt()
{
    String foo = "bar";
    Switch(foo, foo);
}

void Switch(String val1, String val2)
{
    switch ("bar")
    {
        // The compiler will not know that val1 and val2 are not distinct
        case val1:
            // Is this case block selected?
            break;
        case val2:
            // Or this one?
            break;
        case "bar":
            // Or perhaps this one?
            break;
    }
}

कोड क्या करेगा? क्या होगा अगर केस स्टेटमेंट को फिर से व्यवस्थित किया जाए? दरअसल, C # बने स्विच फॉल-थ्रू अवैध होने का एक कारण यह है कि स्विच स्टेटमेंट में मनमाने तरीके से फेरबदल किया जा सकता है।

ये नियम एक कारण के लिए हैं - ताकि प्रोग्रामर एक केस ब्लॉक को देखकर निश्चित सटीक स्थिति जान सके जिसके तहत ब्लॉक में प्रवेश किया जाता है। जब उपर्युक्त स्विच स्टेटमेंट 100 लाइनों या अधिक (और यह होगा) में बढ़ता है, तो ऐसा ज्ञान अमूल्य है।


2
स्विच के पुनरावर्तन पर ध्यान दें। यदि केस में कोई कोड नहीं है, तो गिरना कानूनी है। जैसे, केस 1: केस 2: कंसोल। क्राइटलाइन ("हाय"); टूटना;
जोएल मैकबेथ

10

वैसे, VB, जिसमें एक ही अंतर्निहित वास्तुकला है, बहुत अधिक लचीले Select Caseबयानों (उपरोक्त कोड VB में काम करेगा) और अभी भी कुशल कोड का उत्पादन करता है, जहां यह संभव है, इसलिए तकनीकी बाधा द्वारा तर्क को ध्यान से विचार करना होगा।


1
Select Caseएन वीबी बहुत लचीला और एक सुपर समय की बचत होती है। मुझे इसकी बहुत याद आती है।
एडुआर्डो मोल्टेनि

@EduardoMolteni F # पर स्विच करें। यह पास्कल बनाता है और वीबी के स्विच तुलना में बेवकूफ बच्चों की तरह लगते हैं।
लूवन

10

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

संकलक चुन सकता है (और करता है):

  • एक बड़ा अगर-और बयान बनाएँ
  • MSIL स्विच निर्देश (जंप टेबल) का उपयोग करें
  • एक जेनेरिक.बार्डर बनाने के लिए <string, int32>, इसे पहले इस्तेमाल पर पॉप्युलेट करें, और एक जेसीआई (MSD) स्विच इंस्ट्रक्शन (जंप टेबल) पास करने के लिए जेनेरिक.वार्ड <> :: TryGetValue () इंडेक्स के लिए कॉल करें
  • अगर-एलस और एमएसआईएल "स्विच" कूद के संयोजन का उपयोग करें

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

स्ट्रिंग मामले को संभालने के लिए, कंपाइलर (कुछ बिंदु पर) a.Equals (b) (और संभवतः a.GetHashCode ()) का उपयोग करके समाप्त हो जाएगा। मुझे लगता है कि संकलक के लिए इन बाधाओं को संतुष्ट करने वाली किसी भी वस्तु का उपयोग करना कठिन होगा।

स्थैतिक मामले के अभिव्यक्तियों की आवश्यकता के लिए ... उन आशावादों में से कुछ (हैशिंग, कैशिंग, इत्यादि) उपलब्ध नहीं होंगे यदि केस भाव निर्धारक नहीं थे। लेकिन हम पहले ही देख चुके हैं कि कभी-कभी कंपाइलर सिर्फ सरलीकृत अगर-और-अगर-कहीं और सड़क को चुनता है ...

संपादित करें: lomaxx - " टाइपोफ़ " ऑपरेटर की आपकी समझ सही नहीं है। "टाइपोफ़" ऑपरेटर का उपयोग एक प्रकार के लिए System.Type ऑब्जेक्ट को प्राप्त करने के लिए किया जाता है (इसके सुपरटेप या इंटरफेस के साथ कुछ भी नहीं करना है)। किसी दिए गए प्रकार के साथ किसी ऑब्जेक्ट की रन-टाइम संगतता की जाँच करना "ऑपरेटर" का काम है। किसी वस्तु को व्यक्त करने के लिए यहाँ "टाइपोफ़" का उपयोग अप्रासंगिक है।


6

जेफ एटवुड के अनुसार, इस विषय पर, स्विच स्टेटमेंट एक प्रोग्रामिंग अत्याचार है । उन्हें संयम से उपयोग करें।

आप अक्सर तालिका का उपयोग करके एक ही कार्य पूरा कर सकते हैं। उदाहरण के लिए:

var table = new Dictionary<Type, string>()
{
   { typeof(int), "it's an int!" }
   { typeof(string), "it's a string!" }
};

Type someType = typeof(int);
Console.WriteLine(table[someType]);

7
आप गंभीरता से किसी के कफ़ ट्विटर पोस्ट को बिना किसी सबूत के उद्धृत कर रहे हैं? कम से कम एक विश्वसनीय स्रोत से लिंक करें।
इवान हैमिल्टन

4
यह एक विश्वसनीय स्रोत से है; सवाल का ट्विटर पोस्ट जेफ एटवुड का है, जिस साइट को आप देख रहे हैं, उसके लेखक हैं। :-) यदि आप जिज्ञासु हैं तो जेफ के पास इस विषय पर कुछ मुट्ठी भर ब्लॉग पोस्ट हैं।
जुडाह गेब्रियल हिमंगो

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

@JonathonReinhart हाँ, मुझे लगता है कि यह बात है - switchबयान का उपयोग करने की तुलना में बहुरूपिक कोड को संभालने के लिए बेहतर तरीके हैं । वह यह नहीं कह रहा है कि आपको राज्य मशीनों को नहीं लिखना चाहिए, बस आप अच्छे विशिष्ट प्रकारों का उपयोग करके एक ही काम कर सकते हैं। बेशक, एफ # जैसी भाषाओं में यह बहुत आसान है, जिसमें ऐसे प्रकार हैं जो आसानी से काफी जटिल राज्यों को कवर कर सकते हैं। अपने उदाहरण के लिए, आप विभेदित यूनियनों का उपयोग कर सकते हैं जहां राज्य प्रकार का हिस्सा बन जाता है, और switchपैटर्न मिलान के साथ बदल देता है । या उदाहरण के लिए इंटरफेस का उपयोग करें।
लुआण

पुराना उत्तर / प्रश्न, लेकिन मैंने सोचा होगा कि (अगर मैं गलत हूं तो मुझे सुधारें) Dictionaryएक अनुकूलित switchकथन की तुलना में काफी धीमा हो गया होगा ...?
पॉल

6

मुझे कोई कारण नहीं दिखता कि स्विच स्टेटमेंट को केवल स्टैटिक एनालिसिस के लिए ही सुसाइड करना पड़े

सच है, यह करने के लिए नहीं है , और कई भाषाओं वास्तव में गतिशील स्विच बयान का उपयोग करें। इसका मतलब यह है कि "मामला" खंड को फिर से व्यवस्थित करने से कोड का व्यवहार बदल सकता है।

यहाँ "स्विच" में जाने वाले डिज़ाइन निर्णयों के पीछे कुछ दिलचस्प जानकारी है: C # स्विच स्टेटमेंट को फॉल-थ्रू की अनुमति नहीं देने के लिए क्यों बनाया गया है, लेकिन फिर भी एक ब्रेक की आवश्यकता है?

डायनामिक केस एक्‍सप्रेशनों के चलते इस PHP कोड जैसी विकृति हो सकती है:

switch (true) {
    case a == 5:
        ...
        break;
    case b == 10:
        ...
        break;
}

जो स्पष्ट रूप से सिर्फ if-elseबयान का उपयोग करना चाहिए ।


1
यही कारण है कि मैं PHP के बारे में प्यार करता हूं (अब जैसा कि मैं C # के लिए संक्रमण कर रहा हूं), यह स्वतंत्रता है। इसके साथ बुरा कोड लिखने की स्वतंत्रता
मिलती है

5

माइक्रोसॉफ्ट ने आखिरकार आपको सुना!

अब C # 7 के साथ आप कर सकते हैं:

switch(shape)
{
case Circle c:
    WriteLine($"circle with radius {c.Radius}");
    break;
case Rectangle s when (s.Length == s.Height):
    WriteLine($"{s.Length} x {s.Height} square");
    break;
case Rectangle r:
    WriteLine($"{r.Length} x {r.Height} rectangle");
    break;
default:
    WriteLine("<unknown shape>");
    break;
case null:
    throw new ArgumentNullException(nameof(shape));
}

3

यह एक कारण नहीं है, लेकिन C # विनिर्देश खंड 8.7.2 निम्नलिखित बताता है:

स्विच स्टेटमेंट का संचालन प्रकार स्विच अभिव्यक्ति द्वारा स्थापित किया जाता है। यदि स्विच अभिव्यक्ति का प्रकार sbyte, byte, short, ushort, int, uint, long, ulong, char, string, या enum-type है, तो वह स्विच स्टेटमेंट का शासी प्रकार है। अन्यथा, बिल्कुल एक उपयोगकर्ता-परिभाषित निहित रूपांतरण ()6.4) स्विच अभिव्यक्ति के प्रकार से निम्न संभव गवर्निंग प्रकारों में से किसी एक में मौजूद होना चाहिए: sbyte, बाइट, शॉर्ट, ushort, int, uint, लॉन्ग, ulong, char, string । यदि ऐसा कोई अंतर्निहित रूपांतरण मौजूद नहीं है, या यदि एक से अधिक ऐसे अंतर्निहित रूपांतरण मौजूद हैं, तो संकलन-समय त्रुटि उत्पन्न होती है।

C # 3.0 विनिर्देश यहां स्थित है: http://download.microsoft.com/download/3/8/8/388e7205-bc10-4226-b2a8-75351c669b09/CSharp/20Language%20Specification.doc


3

ऊपर यहूदा के उत्तर ने मुझे एक विचार दिया। आप ओपी के स्विच व्यवहार का उपयोग "नकली" कर सकते हैं Dictionary<Type, Func<T>:

Dictionary<Type, Func<object, string,  string>> typeTable = new Dictionary<Type, Func<object, string, string>>();
typeTable.Add(typeof(int), (o, s) =>
                    {
                        return string.Format("{0}: {1}", s, o.ToString());
                    });

यह आपको एक प्रकार के साथ व्यवहार को स्विच स्टेटमेंट के रूप में संबद्ध करने की अनुमति देता है। मेरा मानना ​​है कि आईएल के लिए संकलित किए जाने पर स्विच-स्टाइल जंप टेबल के बजाय इसे बंद करने का अतिरिक्त लाभ है।


0

मुझे लगता है कि ऐसा कोई मौलिक कारण नहीं है कि कंपाइलर आपके स्विच स्टेटमेंट को स्वचालित रूप से ट्रांसलेट न कर सके:

if (t == typeof(int))
{
...
}
elseif (t == typeof(string))
{
...
}
...

लेकिन इससे बहुत फायदा नहीं हुआ है।

अभिन्न प्रकार पर एक केस स्टेटमेंट कंपाइलर को कई अनुकूलन करने की अनुमति देता है:

  1. कोई दोहराव नहीं है (जब तक कि आप केस लेबल की नकल नहीं करते हैं, जो कंपाइलर पता लगाता है)। आपके उदाहरण में वंशानुक्रम के कारण t कई प्रकारों से मेल खा सकता है। क्या पहले मैच को निष्पादित किया जाना चाहिए? उन सभी को?

  2. कंपाइलर सभी तुलनाओं से बचने के लिए जंप टेबल द्वारा एक अभिन्न प्रकार पर एक स्विच स्टेटमेंट को लागू करने का विकल्प चुन सकता है। यदि आप ऐसे एन्यूमरेशन पर स्विच कर रहे हैं जिसमें पूर्णांक मान 0 से 100 हैं तो यह इसमें 100 पॉइंटर्स के साथ एक सरणी बनाता है, प्रत्येक स्विच स्टेटमेंट के लिए एक। रनटाइम में यह केवल पूर्णांक मान के आधार पर सरणी से पता दिखता है जिस पर स्विच किया जा रहा है। यह 100 तुलनात्मक प्रदर्शन करने की तुलना में बहुत बेहतर रनटाइम प्रदर्शन करता है।


1
यहां ध्यान देने की एक महत्वपूर्ण जटिलता यह है कि .NET मेमोरी मॉडल की कुछ मजबूत गारंटीएं हैं जो आपके छद्मकोड को सटीक रूप से (काल्पनिक, अमान्य C # ) के बराबर नहीं बनाती हैं switch (t) { case typeof(int): ... }क्योंकि आपके अनुवाद का अर्थ है कि चर t को दो बार मेमोरी से प्राप्त किया जाना चाहिए t != typeof(int), अगर बाद में (putatively) हमेशा t ठीक एक बार के मूल्य को पढ़ें । यह अंतर समवर्ती कोड की शुद्धता को तोड़ सकता है जो उन उत्कृष्ट गारंटियों पर निर्भर करता है। इस पर अधिक जानकारी के लिए, विंडोज पर
ग्लेन स्लेडेन

0

स्विच स्टेटमेंट डॉक्यूमेंटेशन के अनुसार यदि वस्तु को अभिन्न प्रकार से अभिन्न रूप में परिवर्तित करने का एक अस्पष्ट तरीका है, तो उसे अनुमति दी जाएगी। मुझे लगता है कि आप एक व्यवहार की उम्मीद कर रहे हैं, जहां प्रत्येक मामले के बयान के साथ इसे बदल दिया जाएगा if (t == typeof(int)), लेकिन जब आप उस ऑपरेटर को ओवरलोड करने के लिए कीड़े की पूरी कैन खोलेंगे। यदि आपने अपना == ओवरराइड गलत लिखा है, तो स्विच स्टेटमेंट के लिए कार्यान्वयन विवरण बदल जाने पर व्यवहार बदल जाएगा। अभिन्न प्रकारों और स्ट्रिंग की तुलना को कम करके और उन चीजों को जो अभिन्न प्रकारों तक कम किया जा सकता है (और इरादा है) वे संभावित मुद्दों से बचते हैं।


0

लिखा था:

"स्विच स्टेटमेंट आपके पास कितने मामलों की परवाह किए बिना एक निरंतर समय शाखा करता है।"

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

@mweerden - आह मैं देख रहा हूँ। धन्यवाद।

मुझे C # और .NET में बहुत अनुभव नहीं है, लेकिन ऐसा लगता है कि भाषा डिजाइनर संकीर्ण परिस्थितियों को छोड़कर टाइप सिस्टम के लिए स्थिर पहुंच की अनुमति नहीं देते हैं। Typeof कीवर्ड एक वस्तु देता है तो यह रन-टाइम केवल पर पहुँचा जा सकता है।


0

मुझे लगता है कि हेन्क ने इसे "टाइप सिस्टम के लिए कोई स्थिर पहुंच" वाली चीज के साथ नहीं मिला

एक अन्य विकल्प यह है कि जहाँ संख्यात्मक और तार हो सकते हैं वहाँ कोई क्रम नहीं है। इस प्रकार एक प्रकार का स्विच एक बाइनरी खोज ट्री का निर्माण नहीं कर सकता है, बस एक रैखिक खोज।


0

मैं इस टिप्पणी से सहमत हूं कि टेबल संचालित दृष्टिकोण का उपयोग करना अक्सर बेहतर होता है।

C # 1.0 में यह संभव नहीं था क्योंकि इसमें जेनरिक और अनाम प्रतिनिधि नहीं थे। C # के नए संस्करणों में यह काम करने के लिए मचान है। वस्तु शाब्दिकों के लिए एक संकेतन होने से भी मदद मिलती है।


0

मुझे लगभग C # का कोई ज्ञान नहीं है, लेकिन मुझे संदेह है कि या तो स्विच को बस लिया गया था, क्योंकि यह अन्य भाषाओं में होता है, इसे अधिक सामान्य बनाने के बारे में सोचने के बिना या डेवलपर ने फैसला किया कि इसका विस्तार करना इसके लायक नहीं था।

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

हालाँकि, मैं कल्पना कर सकता हूँ कि कोई व्यक्ति लोमैक्स ( 44918 ) द्वारा दिए गए कारणों जैसे प्रकारों को छोड़ना चाहता है ।

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


0

C # 8 आपको स्विच की अभिव्यक्ति का उपयोग करके सुरुचिपूर्ण ढंग से और कॉम्पैक्ट रूप से इस समस्या को हल करने की अनुमति देता है:

public string GetTypeName(object obj)
{
    return obj switch
    {
        int i => "Int32",
        string s => "String",
        { } => "Unknown",
        _ => throw new ArgumentNullException(nameof(obj))
    };
}

नतीजतन, आप प्राप्त करते हैं:

Console.WriteLine(GetTypeName(obj: 1));           // Int32
Console.WriteLine(GetTypeName(obj: "string"));    // String
Console.WriteLine(GetTypeName(obj: 1.2));         // Unknown
Console.WriteLine(GetTypeName(obj: null));        // System.ArgumentNullException

आप यहां नई सुविधा के बारे में अधिक पढ़ सकते हैं ।

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