C # में प्रतिनिधियों का उपयोग करना


79

C # भाषा और .NET फ्रेमवर्क में, क्या आप प्रतिनिधियों को समझने में मेरी मदद कर सकते हैं? मैं कुछ कोड की जाँच करने की कोशिश कर रहा था, और मैंने पाया कि मेरे द्वारा प्राप्त परिणाम मेरे लिए अप्रत्याशित थे। यह रहा:

class Program
{
    public static int I = 0;

    static Func<string> del = new Func<string>(I.ToString);

    static void Main(string[] args)
    {
        I = 10;
        Console.WriteLine("{0}", del());
    }
}

जवाब 0 था, लेकिन 10 नहीं। क्यों?


12
@ रॉटेम: नहीं, उसने नहीं किया।
डैनियल हिल्गारथ

3
@ रॉटेम - यह एक प्रतिनिधि घोषणा है। जोड़ने ()से आह्वान होगा ToString
Oded

1
क्षमा करें, कभी इस्तेमाल नहीं किया Funcगया था, एक अनुमान था :)
रोटेम

2
एक अच्छा सवाल के लिए +1, अच्छी तरह से पूछा गया। यह प्रतीत होता है कि सरल-सहज प्रश्न कैसे / भाषा / मंच के एक खराब समझ वाले क्षेत्र को उजागर कर सकता है।
मार्टिन

5
ए (यूनिकास्ट) प्रतिनिधि उदाहरण या तो एक इंस्टेंस विधि या एक staticविधि को इंगित कर सकता है । जब यह एक आवृत्ति विधि का प्रतिनिधित्व करता है, तो प्रतिनिधि दोनों "लक्ष्य" ऑब्जेक्ट को रखता है, जिस पर विधि, और विधि की जानकारी प्राप्त करना है। इसलिए जब आप कहते हैं del = I.ToString;, तो delवह वस्तु धारण करेगा Iजो यहाँ है Int32(अपरिवर्तनीय मूल्य प्रकार)। जब आप एक अनाम फ़ंक्शन का उपयोग करते हैं del = () => I.ToString();, तो संकलक एक विधि बनाता है static string xxx() { return I.ToString(); }और delऑब्जेक्ट उस उत्पन्न विधि को रखता है।
जेपी स्टिग नीलसन

जवाबों:


79

कारण निम्नलिखित है:

जिस तरह से आप प्रतिनिधि को घोषित करते हैं वह सीधे ToStringस्थिर अंतर की विधि की ओर इशारा करता है। यह निर्माण के समय पर कब्जा कर लिया है।

जैसा कि फ्लिंडबर्ग नीचे टिप्पणी में बताते हैं, प्रत्येक प्रतिनिधि के पास एक लक्ष्य होता है और लक्ष्य पर निष्पादित होने के लिए एक विधि होती है।

इस मामले में, निष्पादित की जाने वाली ToStringविधि स्पष्ट रूप से विधि है। दिलचस्प हिस्सा वह उदाहरण है जिस पद्धति को निष्पादित किया गया है: यह Iनिर्माण के समय का उदाहरण है , जिसका अर्थ है कि प्रतिनिधि उपयोग Iकरने के लिए इंस्टेंस प्राप्त करने के लिए उपयोग नहीं कर रहा है, लेकिन यह उदाहरण के लिए संदर्भ को संग्रहीत करता है।

बाद में आप Iएक अलग मूल्य में बदल जाते हैं, मूल रूप से इसे एक नया उदाहरण प्रदान करते हैं। यह जादुई रूप से आपके प्रतिनिधि में कैप्चर किए गए उदाहरण को नहीं बदलता है, ऐसा क्यों होना चाहिए?

अपेक्षित परिणाम प्राप्त करने के लिए, आपको प्रतिनिधि को इसे बदलना होगा:

static Func<string> del = new Func<string>(() => I.ToString());

इस तरह, प्रतिनिधि एक अनाम विधि की ओर इशारा करता है जो प्रतिनिधि के निष्पादन के समय ToStringकरंट Iपर निष्पादित होता है ।

इस मामले में, निष्पादित की जाने वाली विधि एक अनाम विधि है जिसे कक्षा में बनाया गया है जिसमें प्रतिनिधि को घोषित किया गया है। उदाहरण शून्य है क्योंकि यह एक स्थिर विधि है।

उस कोड पर एक नज़र डालें जो कंपाइलर प्रतिनिधि के दूसरे संस्करण के लिए बनाता है:

private static Func<string> del = new Func<string>(UserQuery.<.cctor>b__0);
private static string cctor>b__0()
{
    return UserQuery.I.ToString();
}

जैसा कि आप देख सकते हैं, यह एक सामान्य तरीका है जो कुछ करता है । हमारे मामले में यह ToStringवर्तमान उदाहरण पर कॉल करने का परिणाम देता है I


1
@flindeberg: आप इंट के बजाय अपनी कक्षा का भी उपयोग कर सकते हैं। यह अभी भी एक ही व्यवहार करेगा, क्योंकि अंतर्निहित कारण नहीं बदलता है: प्रतिनिधि विशिष्ट वस्तु पर ToString के विशिष्ट अवतार को इंगित करता है। इससे कोई फर्क नहीं पड़ता कि यह एक संदर्भ प्रकार है या एक मूल्य प्रकार है।
डैनियल हिल्गर्थ

3
@ user1859587: एक प्रतिनिधि के पास एक विधि और एक लक्ष्य (उदाहरण) होता है, यह मायने रखता है कि यह आपके उदाहरण को कैप्चर करता है या लैंबडा फ़ंक्शन के उदाहरण के साथ उदाहरण में संदर्भ होता है।
फ्लिनबर्ग

1
@ user1859587: आपका स्वागत है। BTW: मैंने इसे थोड़ा स्पष्ट करने के लिए उत्तर को अपडेट करने की कोशिश की कि यहां क्या चल रहा है। आप इसे फिर से पढ़ना चाह सकते हैं :-)
डैनियल हिल्गरर्थ

3
डैनियल, फ्लिंडबर्ग की टिप्पणी की पुष्टि करने के लिए: आपका उत्तर सही है लेकिन मुक्केबाजी के संबंध में आपकी टिप्पणी नहीं है। user1859587 सही है: देखा गया व्यवहार इस तथ्य का परिणाम है कि प्रतिनिधि कॉल के रिसीवर को पकड़ लेता है। हालाँकि int पर ToString के लिए कॉल का रिसीवर इंट वैरिएबल का संदर्भ होगा, लेकिन डेलीगेट के पास ढेर पर इंट इंट्रैक्शन के संदर्भ को रखने का कोई तरीका नहीं है; चर के संदर्भ केवल अस्थायी भंडारण में जा सकते हैं। तो यह अगली सबसे अच्छी बात करता है: यह इंट को बॉक्स करता है और उस ढेर स्थान का संदर्भ बनाता है।
एरिक लिपर्ट

10
इस तथ्य का एक दिलचस्प परिणाम यह है कि रिसीवर बॉक्सिंग है, यह एक अशक्त int पर GetValueOrDefault () के लिए एक प्रतिनिधि बनाना असंभव है, क्योंकि एक अशक्त int बॉक्सिंग बॉक्सिंग int पैदा करता है, न कि एक बॉक्सिंग nullable int, और एक बॉक्सिंग int। कोई GetValueOrDefault () विधि।
एरिक लिपर्ट

4

आपको Iअपने फ़ंक्शन को पास करने की आवश्यकता है ताकि I.ToString()उचित समय पर निष्पादित किया जा सके (इसके बजाय समय फ़ंक्शन बनाया जाता है)।

class Program
{
    public static int I = 0;

    static Func<int, string> del = num => num.ToString();

    static void Main(string[] args)
    {
        I = 10;
        Console.WriteLine("{0}", del(I));
    }
}

1

यहां बताया गया है कि यह कैसे किया जाना चाहिए:

using System;

namespace ConsoleApplication1
{

    class Program
    {
        public static int I = 0;

        static Func<string> del = new Func<string>(() => {
            return I.ToString();
        });

        static void Main(string[] args)
        {
            I = 10;
            Console.WriteLine("{0}", del());
        }
    }
}

0

C # डेलिगेट एक ऑब्जेक्ट और इंस्टेंस और एक विधि दोनों को इनकैप्सुलेट करता है। एक प्रतिनिधि घोषणा एक वर्ग को परिभाषित करती है जो वर्ग System.Delegate से ली गई है। एक प्रतिनिधि उदाहरण एक इनवोकेशन सूची को एन्क्रिप्ट करता है, जो एक सूची एक या अधिक विधि है, जिनमें से प्रत्येक को कॉल करने योग्य इकाई के रूप में संदर्भित किया जाता है।

और रूप सीखें

http://asp-net-by-parijat.blogspot.in/2015/08/what-is-delegates-in-c-how-to-declare.html


-2

मेरा अनुमान है क्योंकि इंट को मानों द्वारा नहीं संदर्भों द्वारा पारित किया जाता है, और उस कारण से जब प्रतिनिधि बनाते हैं, तो यह "I" (0) के वर्तमान मूल्य के ToString की विधि का एक प्रतिनिधि है।


2
आपका अनुमान सही नहीं है। इसका मूल्य प्रकार बनाम संदर्भ प्रकारों से कोई लेना-देना नहीं है। ठीक यही बात संदर्भ प्रकारों के साथ भी होगी।
डैनियल हिल्गर्थ

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

फंक <string> () (> = I.ToString ()) के रूप में अच्छी तरह से काम करना चाहिए क्योंकि हम विधि के आह्वान तक "I" का उपयोग नहीं कर रहे हैं।
यशय

लेकिन जो यहां चल रहा है उसके बराबर नहीं है। यदि आप Fooइसके बजाय कक्षा का उपयोग करेंगे intऔर लाइन I = 10को बदल दिया हैI = new Foo(10) आप वर्तमान कोड के साथ के रूप में ठीक उसी परिणाम प्राप्त होगा। I.Value = 10पूरी तरह से कुछ और है। यह एक नया उदाहरण प्रदान नहीं करता है I। लेकिन एक नया उदाहरण प्रदान करना Iयहां महत्वपूर्ण बिंदु है।
डैनियल हिल्गरर्थ

2
ठीक है, तो समस्या "मैं" को पुन: सौंपने की है, अगर "मैं" आपस में मिलते-जुलते थे और हम वस्तु को पुन: असाइन किए बिना बदल देते हैं, तो यह काम करेगा। इस उदाहरण में, हम ऐसा नहीं कर सकते क्योंकि मैं int (अपरिवर्तनीय) हूं।
यशय
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.