c# - प्रतिनिधियों का उपयोग कब और क्यों करें?




.net delegates (6)

प्रतिनिधियों का अवलोकन

प्रतिनिधियों के पास निम्नलिखित गुण हैं:

  • प्रतिनिधि C ++ फ़ंक्शन पॉइंटर्स के समान होते हैं, लेकिन सुरक्षित प्रकार होते हैं।
  • प्रतिनिधि पैरामीटर के रूप में विधियों को पारित करने की अनुमति देते हैं।
  • कॉलबैक विधियों को परिभाषित करने के लिए प्रतिनिधियों का उपयोग किया जा सकता है।
  • प्रतिनिधियों को एक साथ बंधे जा सकते हैं; उदाहरण के लिए, एक ही घटना पर कई विधियों को बुलाया जा सकता है।
  • विधियों को प्रतिनिधि हस्ताक्षर से मिलान करने की आवश्यकता नहीं है। अधिक जानकारी के लिए, कोविरेन्स और कॉन्ट्रा भिन्नता देखें।
  • सी # संस्करण 2.0 बेनामी तरीके की अवधारणा को प्रस्तुत करता है, जो कोड ब्लॉक को अलग-अलग परिभाषित विधि के स्थान पर पैरामीटर के रूप में पारित करने की अनुमति देता है।

इस प्रश्न का उत्तर यहां दिया गया है:

मैं सी # में अपेक्षाकृत नया हूं, और मैं सोच रहा हूं कि प्रतिनिधि का उचित उपयोग कब करें । वे घटनाओं की घोषणा में व्यापक रूप से उपयोग किए जाते हैं, लेकिन मुझे अपने कोड में उनका उपयोग कब करना चाहिए और वे उपयोगी क्यों हैं? कुछ और क्यों नहीं उपयोग करें?

मैं यह भी सोच रहा हूं कि मुझे प्रतिनिधियों का उपयोग करना होगा और मेरे पास कोई अन्य विकल्प नहीं है

सहायता के लिए धनयवाद!

संपादित करें: मुझे लगता है कि मुझे here प्रतिनिधियों here एक आवश्यक उपयोग मिला here


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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MyLibrary;

namespace DelegateApp {

  /// <summary>
  /// A class to define a person
  /// </summary>
  public class Person {
    public string Name { get; set; }
    public int Age { get; set; }
  }

  class Program {
    //Our delegate
    public delegate bool FilterDelegate(Person p);

    static void Main(string[] args) {

      //Create 4 Person objects
      Person p1 = new Person() { Name = "John", Age = 41 };
      Person p2 = new Person() { Name = "Jane", Age = 69 };
      Person p3 = new Person() { Name = "Jake", Age = 12 };
      Person p4 = new Person() { Name = "Jessie", Age = 25 };

      //Create a list of Person objects and fill it
      List<Person> people = new List<Person>() { p1, p2, p3, p4 };

      //Invoke DisplayPeople using appropriate delegate
      DisplayPeople("Children:", people, IsChild);
      DisplayPeople("Adults:", people, IsAdult);
      DisplayPeople("Seniors:", people, IsSenior);

      Console.Read();
    }

    /// <summary>
    /// A method to filter out the people you need
    /// </summary>
    /// <param name="people">A list of people</param>
    /// <param name="filter">A filter</param>
    /// <returns>A filtered list</returns>
    static void DisplayPeople(string title, List<Person> people, FilterDelegate filter) {
      Console.WriteLine(title);

      foreach (Person p in people) {
        if (filter(p)) {
          Console.WriteLine("{0}, {1} years old", p.Name, p.Age);
        }
      }

      Console.Write("\n\n");
    }

    //==========FILTERS===================
    static bool IsChild(Person p) {
      return p.Age < 18;
    }

    static bool IsAdult(Person p) {
      return p.Age >= 18;
    }

    static bool IsSenior(Person p) {
      return p.Age >= 65;
    }
  }
}

जब आप पास करना चाहते हैं तो उस ब्लॉक के ब्लॉक को घोषित करना चाहते हैं जब प्रतिनिधि बहुत उपयोगी होते हैं। उदाहरण के लिए एक जेनेरिक रीट्री तंत्र का उपयोग करते समय।

छद्म:

function Retry(Delegate func, int numberOfTimes)
    try
    {
       func.Invoke();
    }
    catch { if(numberOfTimes blabla) func.Invoke(); etc. etc. }

या जब आप कोड ब्लॉक के देर से मूल्यांकन करना चाहते हैं, जैसे एक फ़ंक्शन जहां आपके पास कुछ Transform एक्शन है, और एक पहले BeforeTransform और एक AfterTransform एक्शन BeforeTransform चाहते हैं, जिसे आप अपने ट्रांसफॉर्म फ़ंक्शन के भीतर मूल्यांकन कर सकते हैं, यह जानने के बिना कि BeginTransform भर गया है या नहीं , या इसे बदलने के लिए क्या है।

और निश्चित रूप से घटना हैंडलर बनाते समय। आप अब कोड का मूल्यांकन नहीं करना चाहते हैं, लेकिन केवल जब आवश्यक हो, तो आप एक प्रतिनिधि को पंजीकृत करते हैं जिसे ईवेंट होने पर बुलाया जा सकता है।


मान लें कि आप कुछ अंतराल पर कुछ वास्तविक मूल्यवान फ़ंक्शन f ( x ) को एकीकृत करने के लिए एक प्रक्रिया लिखना चाहते हैं [ए, बी]। मान लें कि हम ऐसा करने के लिए 3-प्वाइंट गॉसियन विधि का उपयोग करना चाहते हैं (निश्चित रूप से कोई भी करेगा)।

आदर्श रूप में हम कुछ फ़ंक्शन चाहते हैं जो इस तरह दिखते हैं:

// 'f' is the integrand we want to integrate over [a, b] with 'n' subintervals.
static double Gauss3(Integrand f, double a, double b, int n) {
  double res = 0;

  // compute result
  // ...

  return res;
}

तो हम किसी भी Integrand , एफ , और बंद अंतराल पर अपने निश्चित अभिन्न अंग प्राप्त कर सकते हैं।

Integrand किस प्रकार होना चाहिए?

प्रतिनिधियों के बिना

खैर, प्रतिनिधियों के बिना, हमें एक विधि के साथ कुछ प्रकार के इंटरफ़ेस की आवश्यकता होगी, निम्नानुसार घोषित घोषित करें:

// Interface describing real-valued functions of one variable.
interface Integrand {
  double eval(double x);
}

फिर हमें इस इंटरफेस को लागू करने वाले वर्गों का पूरा समूह बनाना होगा, जैसा कि निम्नानुसार है:

// Some function
class MyFunc1 : Integrand {
  public double eval(double x) {
    return /* some_result */ ;
  }
}

// Some other function
class MyFunc2 : Integrand {
  public double eval(double x) {
    return /* some_result */ ;
  }
}

// etc

फिर उन्हें हमारे Gauss3 विधि में उपयोग करने के लिए, हमें इसे निम्नानुसार आमंत्रित करने की आवश्यकता है:

double res1 = Gauss3(new MyFunc1(), -1, 1, 16);
double res2 = Gauss3(new MyFunc2(), 0, Math.PI, 16);

और Gauss3 को निम्न की तरह दिखने की जरूरत है:

static double Gauss3(Integrand f, double a, double b, int n) {
  // Use the integrand passed in:
  f.eval(x);
}

तो हमें Guass3 में हमारे मनमाने ढंग से कार्यों का उपयोग करने के लिए बस इतना करना है।

प्रतिनिधियों के साथ

public delegate double Integrand(double x);

अब हम उस प्रोटोटाइप का पालन करने वाले कुछ स्थिर (या नहीं) कार्यों को परिभाषित कर सकते हैं:

class Program {
   public delegate double Integrand(double x);   
   // Define implementations to above delegate 
   // with similar input and output types
   static double MyFunc1(double x) { /* ... */ }
   static double MyFunc2(double x) { /* ... */ }
   // ... etc ...

   public static double Gauss3(Integrand f, ...) { 
      // Now just call the function naturally, no f.eval() stuff.
      double a = f(x); 
      // ...
   }

   // Let's use it
   static void Main() {
     // Just pass the function in naturally (well, its reference).
     double res = Gauss3(MyFunc1, a, b, n);
     double res = Gauss3(MyFunc2, a, b, n);    
   }
}

एक साधारण कार्य के लिए, कोई इंटरफ़ेस नहीं, कोई गुंजाइश नहीं है। कोई सामान नहीं, कोई ऑब्जेक्ट इंस्टेंटेशन नहीं, उपयोग की तरह सरल फ़ंक्शन-पॉइंटर।

निस्संदेह, प्रतिनिधि हूड के नीचे फ़ंक्शन पॉइंटर्स से अधिक हैं, लेकिन यह एक अलग मुद्दा है (फ़ंक्शन चेनिंग और ईवेंट)।


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


मैं बस इनके चारों ओर अपना सिर चला गया हूं, और इसलिए मैं एक उदाहरण साझा करूंगा क्योंकि आपके पास पहले से ही विवरण हैं लेकिन फिलहाल एक फायदा यह है कि सर्कुलर रेफरेंस स्टाइल चेतावनियों के आसपास जाना है जहां आपके पास 2 परियोजनाओं को संदर्भित नहीं किया जा सकता है अन्य।

आइए मान लें कि एप्लिकेशन एक एक्सएमएल डाउनलोड करता है, और उसके बाद एक्सएमएल को डेटाबेस में सहेजता है।

मेरे पास 2 परियोजनाएं हैं जो मेरा समाधान बनाती हैं: एफ़टीपी और एक सेवडेटाबेस।

इसलिए, हमारा एप्लिकेशन किसी भी डाउनलोड को डाउनलोड करके और फ़ाइल डाउनलोड करने से शुरू होता है, फिर यह SaveDatabase प्रोजेक्ट को कॉल करता है।

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

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

देखें कि यह कच्चा उदाहरण मदद करता है (छद्म कोड)। हम यह भी मान लेंगे कि एप्लिकेशन एफ़टीपी कक्षा की आरंभ () विधि से शुरू होता है।

class FTP
{
    public void Begin()
    {
        string filePath = DownloadFileFromFtpAndReturnPathName();

        SaveDatabase sd = new SaveDatabase();
        sd.Begin(filePath, NotifyFtpComplete());
    }

    private void NotifyFtpComplete()
    {
        //Code to send file to FTP site
    }
}


class SaveDatabase
{
    private void Begin(string filePath, delegateType NotifyJobComplete())
    {
        SaveToTheDatabase(filePath);

        //InvokeTheDelegate - here we can execute the NotifyJobComplete method at our preferred moment in the application, despite the method being private and belonging to a different class. 
        NotifyJobComplete.Invoke();
    }
}

तो, इसके साथ समझाया गया है, हम अब इस कंसोल एप्लिकेशन के साथ सी #

using System;

namespace ConsoleApplication1
{
    //I've made this class private to demonstrate that the SaveToDatabase cannot have any knowledge of this Program class.
    class Program
    {
        static void Main(string[] args)
        {
            //Note, this NotifyDelegate type is defined in the SaveToDatabase project
            NotifyDelegate nofityDelegate = new NotifyDelegate(NotifyIfComplete);

            SaveToDatabase sd = new SaveToDatabase();            
            sd.Start(nofityDelegate);
            Console.ReadKey();
        }

        //this is the method which will be delegated - the only thing it has in common with the NofityDelegate is that it takes 0 parameters and that it returns void. However, it is these 2 which are essential. It is really important to notice that it writes a variable which, due to no constructor, has not yet been called (so _notice is not initialized yet). 
    private static void NotifyIfComplete()
    {
        Console.WriteLine(_notice);
    }

    private static string _notice = "Notified";
    }


    public class SaveToDatabase
    {
        public void Start(NotifyDelegate nd)
        {
            Console.WriteLine("Yes, I shouldn't write to the console from here, it's just to demonstrate the code executed.");
            Console.WriteLine("SaveToDatabase Complete");
            Console.WriteLine(" ");
            nd.Invoke();
        }
    }
    public delegate void NotifyDelegate();
}

मेरा सुझाव है कि आप कोड के माध्यम से कदम उठाएं और देखें कि कब नोटिस कहा जाता है और जब विधि (प्रतिनिधि) को इस के रूप में बुलाया जाता है, तो मुझे उम्मीद है कि चीजें बहुत स्पष्ट हो जाएंगी।

हालांकि, आखिरकार, हम पैरामीटर को शामिल करने के लिए प्रतिनिधि प्रकार को बदलकर इसे और अधिक उपयोगी बना सकते हैं।

using System.Text;

namespace ConsoleApplication1
{
    //I've made this class private to demonstrate that the SaveToDatabase cannot have any knowledge of this Program class.
    class Program
    {
        static void Main(string[] args)
        {
            SaveToDatabase sd = new SaveToDatabase();

//Please note, that although NotifyIfComplete() takes a string parameter, we do not declare it - all we want to do is tell C# where the method is so it can be referenced later - we will pass the paramater later.
            NotifyDelegateWithMessage nofityDelegateWithMessage = new NotifyDelegateWithMessage(NotifyIfComplete);

            sd.Start(nofityDelegateWithMessage);

            Console.ReadKey();
        }

        private static void NotifyIfComplete(string message)
        {
            Console.WriteLine(message);
        }
    }


    public class SaveToDatabase
    {
        public void Start(NotifyDelegateWithMessage nd)
        {
            //To simulate a saving fail or success, I'm just going to check the current time (well, the seconds) and store the value as variable.
            string message = string.Empty;
            if (DateTime.Now.Second > 30)
                message = "Saved";
            else
                message = "Failed";

            //It is at this point we pass the parameter to our method.
            nd.Invoke(message);
        }
    }

    public delegate void NotifyDelegateWithMessage(string message);
}




delegates