'आउट' कीवर्ड का उपयोग दो प्रतीत होने वाले संदर्भों में क्यों किया जाता है?


11

C # में, outकीवर्ड को दो अलग-अलग तरीकों से उपयोग किया जा सकता है।

  1. एक पैरामीटर संशोधक के रूप में जिसमें एक तर्क को संदर्भ द्वारा पारित किया जाता है

    class OutExample
    {
        static void Method(out int i)
        {
            i = 44;
        }
        static void Main()
        {
            int value;
            Method(out value);
            // value is now 44
        }
    }
  2. सहसंयोजक निर्दिष्ट करने के लिए एक प्रकार के पैरामीटर संशोधक के रूप में

    // Covariant interface. 
    interface ICovariant<out R> { }
    
    // Extending covariant interface. 
    interface IExtCovariant<out R> : ICovariant<R> { }
    
    // Implementing covariant interface. 
    class Sample<R> : ICovariant<R> { }
    
    class Program
    {
        static void Test()
        {
            ICovariant<Object> iobj = new Sample<Object>();
            ICovariant<String> istr = new Sample<String>();
    
            // You can assign istr to iobj because 
            // the ICovariant interface is covariant.
            iobj = istr;
        }
    }

मेरा सवाल है: क्यों?

एक शुरुआत के लिए, दोनों के बीच संबंध सहज नहीं लगता है । संदर्भ के साथ गुजरने से जेनेरिक के उपयोग का कोई लेना देना नहीं है।

मैंने पहली बार सीखा कि outसंदर्भ से तर्क पारित करने के संबंध में क्या था, और इसने जेनेरिक के साथ सहसंयोजक को परिभाषित करने के उपयोग की मेरी समझ को बाधित किया।

क्या इन उपयोगों के बीच कोई संबंध है जो मुझे याद आ रहा है?


5
यदि आप System.Func<in T, out TResult>प्रतिनिधि में सहसंयोजक और विरोधाभासी उपयोग को देखते हैं तो कनेक्शन थोड़ा और अधिक समझने योग्य है ।
रवांग

4
इसके अलावा, अधिकांश भाषा डिजाइनर कीवर्ड की संख्या को कम करने की कोशिश करते हैं, और एक बड़ी कोडबेस के साथ कुछ मौजूदा भाषा में एक नया कीवर्ड जोड़ना दर्दनाक है (एक नाम के रूप में उस शब्द का उपयोग करके कुछ मौजूदा कोड के साथ संभावित संघर्ष)
बेसिल स्टायरनेविच

जवाबों:


20

एक कनेक्शन है, हालांकि यह थोड़ा ढीला है। C # कीवर्ड में standin´ और #out their के रूप में उनका नाम सुझाव इनपुट और आउटपुट के लिए खड़ा है। यह आउटपुट मापदंडों के मामले में बहुत स्पष्ट है, लेकिन टेम्पलेट मापदंडों के साथ क्या करना है, यह कम साफ है।

आइए Liskov प्रतिस्थापन सिद्धांत पर एक नज़र डालें :

...

लिस्कोव का सिद्धांत हस्ताक्षर पर कुछ मानक आवश्यकताओं को लागू करता है जो नई वस्तु-उन्मुख प्रोग्रामिंग भाषाओं में अपनाई गई हैं (आमतौर पर प्रकारों के बजाय कक्षाओं के स्तर पर; अंतर के लिए नाममात्र बनाम संरचनात्मक उपप्रकार देखें):

  • उपप्रकार में विधि तर्कों का विपरीत।
  • उपप्रकार में वापसी प्रकार के सहसंयोजक।

...

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

interface I<out T>
{
  void func(T t); //Invalid variance: The type parameter 'T' must be
                  //contravariantly valid on 'I<T>.func(T)'.
                  //'T' is covariant.

}

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

interface I<in T>
{
  T func(); //Invalid variance: The type parameter 'T' must
            //be covariantly valid on 'I<T>.func()'. 
            //'T' is contravariant.

}

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

System.Funcयह भी एक अच्छा उदाहरण है कि रवांग ने अपनी टिप्पणी में क्या उल्लेख किया है। में System.Funcसभी इनपुट पैरामीटर के साथ flaged गिरफ्तारी in, और आउटपुट पैरामीटर के साथ flaged है out। कारण वही है जो मैंने वर्णित किया है।


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

10

@ गैबोर ने पहले ही कनेक्शन (सभी के लिए विरोधाभासी "जो" में "चला गया है, सभी के लिए सहसंयोजक" बाहर "चला जाता है) समझाया है, लेकिन सभी पर फिर से कीवर्ड का उपयोग क्यों करें?

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

किसी भाषा में कीवर्ड जोड़ना और भी महंगा है। यह मूल रूप से सभी कोड बनाता है जो इस कीवर्ड को एक पहचानकर्ता के रूप में उपयोग करता है, जो सभी जगह पीछे की ओर-संगतता को तोड़ता है।

inऔर outखोजशब्द पहले से ही अस्तित्व में है तो वे सिर्फ पुन: उपयोग किया जा सकता है।

वे प्रासंगिक कीवर्ड जोड़ सकते थे , जो केवल टाइप पैरामीटर सूची के संदर्भ में कीवर्ड होते हैं, लेकिन उन्होंने कौन से कीवर्ड चुने होंगे? covariantऔर contravariant? +और -(जैसे स्काला ने किया, उदाहरण के लिए) superऔर extendsजैसे जावा ने किया? क्या आप अपने सिर के ऊपर से याद कर सकते हैं कि कौन से पैरामीटर सहसंयोजक और विपरीत हैं?

वर्तमान समाधान के साथ, एक अच्छा mnemonic है: आउटपुट प्रकार मापदंडों को outकीवर्ड मिलता है , इनपुट प्रकार के मापदंडों को inकीवर्ड मिलता है । विधि मापदंडों के साथ अच्छी समरूपता पर ध्यान दें: आउटपुट पैरामीटर outकीवर्ड प्राप्त करते हैं , इनपुट पैरामीटर inकीवर्ड प्राप्त करते हैं (अच्छी तरह से, वास्तव में, कोई भी कीवर्ड बिल्कुल नहीं है, क्योंकि इनपुट डिफ़ॉल्ट है, लेकिन आपको विचार मिलता है)।

[नोट: यदि आप संपादन इतिहास को देखते हैं, तो आप देखेंगे कि मैंने वास्तव में अपने परिचयात्मक वाक्य में दोनों को मूल रूप से बदल दिया है। और मैं भी उस समय के दौरान उठ गया! यह सिर्फ आपको यह दिखाने के लिए जाता है कि वास्तव में यह कितना महत्वपूर्ण है।


सह-बनाम संलयन-विचरण को याद रखने का तरीका यह है कि यदि किसी इंटरफ़ेस में कोई फ़ंक्शन सामान्य इंटरफ़ेस प्रकार के पैरामीटर को लेता है तो क्या होता है। यदि किसी के पास एक है interface Accepter<in T> { void Accept(T it);};, तो एक इनपुट पैरामीटर के रूप में Accepter<Foo<T>>स्वीकार करेगा Tयदि Foo<T>इसे आउटपुट पैरामीटर के रूप में स्वीकार करता है, और इसके विपरीत। इस प्रकार, विपरीत -variance। इसके विपरीत, interface ISupplier<out T> { T get();};एक Supplier<Foo<T>>जो कुछ भी विचरण की तरह होगा Fooइस प्रकार - है सह -variance।
सुपरकैट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.