खोज…


टिप्पणियों

सारांश

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

इन-बिल्ट प्रतिनिधि प्रकार: Action<...> , Predicate<T> और Func<...,TResult>

System नामस्थान में Action<...> , Predicate<T> और Func<...,TResult> डेलीगेट्स शामिल हैं, जहां "..." 0 और 16 जेनेरिक प्रकार के मापदंडों (0 मापदंडों के लिए, Action नॉन- के बीच का प्रतिनिधित्व करता है) सामान्य)।

Func एक वापसी प्रकार मिलान के साथ तरीकों का प्रतिनिधित्व करता है TResult , और Action की वापसी मूल्य (शून्य) के बिना तरीकों का प्रतिनिधित्व करता है। दोनों मामलों में, अतिरिक्त सामान्य प्रकार के पैरामीटर मेल खाते हैं, क्रम में, विधि पैरामीटर।

Predicate बूलियन रिटर्न प्रकार के साथ विधि का प्रतिनिधित्व करता है, T इनपुट पैरामीटर है।

कस्टम प्रतिनिधि प्रकार

नामांकित प्रतिनिधि प्रकार को delegate कीवर्ड का उपयोग करके घोषित किया जा सकता है।

प्रतिनिधियों को आमंत्रित करना

प्रतिनिधि के रूप में समान सिंटैक्स का उपयोग करके प्रतिनिधि भेजे जा सकते हैं: प्रतिनिधि उदाहरण का नाम, इसके बाद कोष्ठक जिसमें कोई भी पैरामीटर हो।

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

प्रतिनिधियों को निम्नलिखित तरीकों से सौंपा जा सकता है:

  • एक नामित विधि सौंपना
  • एक लैंबडा का उपयोग करके एक अनाम विधि असाइन करना
  • delegate कीवर्ड का उपयोग करके एक नामित विधि निर्दिष्ट करना।

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

एकाधिक प्रतिनिधि वस्तुओं को + ऑपरेटर का उपयोग करके एक प्रतिनिधि उदाहरण को सौंपा जा सकता है। - ऑपरेटर एक और प्रतिनिधि से एक घटक प्रतिनिधि निकालने के लिए किया जा सकता है।

नामित विधि प्रतिनिधियों के संदर्भों को समझना

प्रतिनिधियों को नामित तरीके प्रदान करते समय, वे उसी अंतर्निहित वस्तु का उल्लेख करेंगे यदि:

  • वे एक ही उदाहरण विधि हैं, एक वर्ग के एक ही उदाहरण पर

  • वे एक वर्ग पर एक ही स्थिर विधि हैं

    public class Greeter
    {
        public void WriteInstance()
        {
            Console.WriteLine("Instance");
        }
    
        public static void WriteStatic()
        {
            Console.WriteLine("Static");
        }
    }
    
    // ...
    
    Greeter greeter1 = new Greeter();
    Greeter greeter2 = new Greeter();
    
    Action instance1 = greeter1.WriteInstance;
    Action instance2 = greeter2.WriteInstance;
    Action instance1Again = greeter1.WriteInstance;
    
    Console.WriteLine(instance1.Equals(instance2)); // False
    Console.WriteLine(instance1.Equals(instance1Again)); // True
    
    Action @static = Greeter.WriteStatic;
    Action staticAgain = Greeter.WriteStatic;
    
    Console.WriteLine(@static.Equals(staticAgain)); // True
    

एक प्रतिनिधि प्रकार की घोषणा

निम्न सिंटैक्स, NumberInOutDelegate नाम के साथ एक delegate प्रकार बनाता है, एक विधि का प्रतिनिधित्व करता है जो एक int लेता है और एक int रिटर्न देता है।

public delegate int NumberInOutDelegate(int input);

इसका उपयोग इस प्रकार किया जा सकता है:

public static class Program
{
    static void Main()
    {
        NumberInOutDelegate square = MathDelegates.Square;
        int answer1 = square(4); 
        Console.WriteLine(answer1); // Will output 16

        NumberInOutDelegate cube = MathDelegates.Cube;
        int answer2 = cube(4);
        Console.WriteLine(answer2); // Will output 64            
    }
}

public static class MathDelegates
{
    static int Square (int x)
    {
        return x*x;
    }

    static int Cube (int x)
    {
        return x*x*x;
    }
}

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


आप एक सामान्य प्रतिनिधि प्रकार की घोषणा कर सकते हैं, और उस स्थिति में आप निर्दिष्ट कर सकते हैं कि प्रकार कुछ प्रकार के तर्कों में सहसंयोजक ( out ) या कंट्रावेरिएंट ( in ) है। उदाहरण के लिए:

public delegate TTo Converter<in TFrom, out TTo>(TFrom input);

अन्य जेनेरिक प्रकारों की तरह, जेनेरिक डेलीगेट प्रकारों में अड़चनें हो सकती हैं, जैसे where TFrom : struct, IConvertible where TTo : new()

प्रतिनिधि प्रकारों के लिए सह- और विरोधाभासी से बचें जो बहुस्तरीय प्रतिनिधियों के लिए उपयोग किए जाने वाले हैं, जैसे कि घटना हैंडलर प्रकार। यह इसलिए है क्योंकि यदि रन-टाइम प्रकार विचरण के कारण संकलन-समय प्रकार से भिन्न होता है, तो संघनन ( + ) विफल हो सकता है। उदाहरण के लिए, बचें:

public delegate void EventHandler<in TEventArgs>(object sender, TEventArgs e);

इसके बजाय, एक अपरिवर्तनीय सामान्य प्रकार का उपयोग करें:

public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);

समर्थित भी प्रतिनिधि हैं जहां कुछ पैरामीटर ref या out द्वारा संशोधित किए out , जैसे:

public delegate bool TryParser<T>(string input, out T result);

(नमूना उपयोग TryParser<decimal> example = decimal.TryParse; ), या प्रतिनिधियों जहां पिछले पैरामीटर है params संशोधक। प्रतिनिधि प्रकार में वैकल्पिक पैरामीटर (आपूर्ति डिफ़ॉल्ट मान) हो सकते हैं। प्रतिनिधि प्रकार अपने हस्ताक्षर या रिटर्न प्रकार ( unsafe कीवर्ड का उपयोग करें) में int* या char* जैसे सूचक प्रकारों का उपयोग कर सकते हैं। एक प्रतिनिधि प्रकार और इसके पैरामीटर कस्टम विशेषताओं को ले जा सकते हैं।

द फंक , क्रिया और समर्पित करें प्रतिनिधि प्रकार

सिस्टम नामस्थान में Func<..., TResult> प्रतिनिधि प्रकार हैं जिनके बीच 0 और 15 जेनेरिक पैरामीटर हैं, जो प्रकार TResult

private void UseFunc(Func<string> func)
{
    string output = func(); // Func with a single generic type parameter returns that type
    Console.WriteLine(output);
}

private void UseFunc(Func<int, int, string> func)
{
    string output = func(4, 2); // Func with multiple generic type parameters takes all but the first as parameters of that type
    Console.WriteLine(output);
}

सिस्टम नेमस्पेस में Action<...> विभिन्न प्रकार के जेनेरिक मापदंडों के साथ प्रतिनिधि (0 से 16 तक) शामिल हैं। यह Func<T1, .., Tn> , लेकिन यह हमेशा void

private void UseAction(Action action)
{
    action(); // The non-generic Action has no parameters
}

private void UseAction(Action<int, string> action)
{
    action(4, "two"); // The generic action is invoked with parameters matching its type arguments
}

Predicate<T> भी Func का एक रूप है लेकिन यह हमेशा bool लौटाएगा। एक विधेय एक कस्टम मानदंडों को निर्दिष्ट करने का एक तरीका है। इनपुट के मूल्य और विधेय के भीतर परिभाषित तर्क के आधार पर, यह या तो true या false वापस आएगा। Predicate<T> इसलिए Func<T, bool> के समान व्यवहार करता है और दोनों को एक ही तरीके से आरंभ और उपयोग किया जा सकता है।

Predicate<string> predicate = s => s.StartsWith("a");
Func<string, bool> func = s => s.StartsWith("a");

// Both of these return true
var predicateReturnsTrue = predicate("abc");
var funcReturnsTrue = func("abc");

// Both of these return false
var predicateReturnsFalse = predicate("xyz");
var funcReturnsFalse = func("xyz");

Predicate<T> या Func<T, bool> का उपयोग करने का विकल्प वास्तव में राय का विषय है। Predicate<T> लेखक के इरादे के बारे में अधिक स्पष्ट है, जबकि Func<T, bool> के C # डेवलपर्स के एक बड़े अनुपात से परिचित होने की संभावना है।

इसके अलावा, कुछ मामले ऐसे होते हैं, जिनमें से केवल एक विकल्प उपलब्ध होता है, खासकर जब दूसरे एपीआई के साथ बातचीत करते हैं। उदाहरण के लिए List<T> और Array<T> आमतौर पर अपने तरीकों के लिए Predicate<T> लेते हैं, जबकि अधिकांश LINQ एक्सटेंशन केवल Func<T, bool> स्वीकार करते हैं।

एक प्रतिनिधि को एक नामित विधि सौंपना

नामित हस्ताक्षर के साथ प्रतिनिधियों को नामित तरीके सौंपे जा सकते हैं:

public static class Example
{
    public static int AddOne(int input)
    {
        return input + 1;
    }
}


Func<int,int> addOne = Example.AddOne

Example.AddOne एक लेता है int और एक रिटर्न int , उसके हस्ताक्षर प्रतिनिधि से मेल खाता है Func<int,int>Example.AddOne को सीधे addOne को सौंपा जा सकता है क्योंकि उनके पास मिलान हस्ताक्षर हैं।

प्रतिनिधि समानता

कॉलिंग .Equals() एक प्रतिनिधि पर संदर्भ समानता द्वारा तुलना:

Action action1 = () => Console.WriteLine("Hello delegates");
Action action2 = () => Console.WriteLine("Hello delegates");
Action action1Again = action1;

Console.WriteLine(action1.Equals(action1)) // True
Console.WriteLine(action1.Equals(action2)) // False
Console.WriteLine(action1Again.Equals(action1)) // True

मल्टीकास्ट डेलीगेट पर += या -= करते समय ये नियम लागू होते हैं, उदाहरण के लिए जब घटनाओं से सदस्यता लेना और सदस्यता समाप्त करना।

लैम्ब्डा द्वारा एक प्रतिनिधि को सौंपना

एक प्रतिनिधि को सौंपने के लिए अनाम विधियों का उपयोग करने के लिए लैम्ब्डा का उपयोग किया जा सकता है:

Func<int,int> addOne = x => x+1;

ध्यान दें कि चर बनाते समय इस प्रकार की स्पष्ट घोषणा आवश्यक है:

var addOne = x => x+1; // Does not work

मापदंडों के रूप में प्रतिनिधियों को पास करना

डेलिगेट्स का उपयोग टाइप फंक्शन पॉइंटर्स के रूप में किया जा सकता है:

class FuncAsParameters
{
  public void Run()
  {
    DoSomething(ErrorHandler1);
    DoSomething(ErrorHandler2);
  }

  public bool ErrorHandler1(string message)
  {
    Console.WriteLine(message);
    var shouldWeContinue = ...  
    return shouldWeContinue;
  }

  public bool ErrorHandler2(string message)
  {
    // ...Write message to file...
    var shouldWeContinue = ...  
    return shouldWeContinue;
  }

  public void DoSomething(Func<string, bool> errorHandler)
  {
    // In here, we don't care what handler we got passed!
    ...
    if (...error...)
    {
      if (!errorHandler("Some error occurred!"))
      {
        // The handler decided we can't continue
        return;
      }
    }
  }
}

संयुक्त प्रतिनिधि (बहुस्तरीय प्रतिनिधि)

जोड़ + और घटाव - संचालन का उपयोग प्रतिनिधि उदाहरणों को संयोजित करने के लिए किया जा सकता है। प्रतिनिधि में असाइन किए गए प्रतिनिधियों की एक सूची होती है।

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace DelegatesExample {
    class MainClass {
        private delegate void MyDelegate(int a);

        private static void PrintInt(int a) {
            Console.WriteLine(a);
        }

        private static void PrintType<T>(T a) {
            Console.WriteLine(a.GetType());
        }

        public static void Main (string[] args)
        {
            MyDelegate d1 = PrintInt;
            MyDelegate d2 = PrintType;

            // Output:
            // 1
            d1(1);

            // Output:
            // System.Int32
            d2(1);

            MyDelegate d3 = d1 + d2;
            // Output:
            // 1
            // System.Int32
            d3(1);

            MyDelegate d4 = d3 - d2;
            // Output:
            // 1
            d4(1);

            // Output:
            // True
            Console.WriteLine(d1 == d4);
        }
    }
}

इस उदाहरण में d3 d1 और d2 प्रतिनिधियों का एक संयोजन है, इसलिए जब प्रोग्राम को 1 और System.Int32 स्ट्रिंग्स को आउटपुट किया जाता है।


गैर शून्य रिटर्न प्रकारों के साथ प्रतिनिधियों को मिलाना:

यदि मल्टीकास्ट प्रतिनिधि के पास nonvoid रिटर्न प्रकार होता है, तो कॉल करने वाले को अंतिम विधि से रिटर्न वैल्यू प्राप्त होती है। पूर्ववर्ती तरीकों को अभी भी कहा जाता है, लेकिन उनके रिटर्न मानों को छोड़ दिया जाता है।

    class Program
    {
        public delegate int Transformer(int x);

        static void Main(string[] args)
        {
            Transformer t = Square;
            t += Cube;
            Console.WriteLine(t(2));  // O/P 8 
        }

        static int Square(int x) { return x * x; }

        static int Cube(int x) { return x*x*x; }
    }

t(2) पहले Square और फिर Cube । स्क्वायर का रिटर्न वैल्यू खारिज कर दिया गया है और अंतिम विधि यानी Cube का रिटर्न वैल्यू बरकरार रखा गया है।

सुरक्षित इनवॉइस मल्टीकास्ट प्रतिनिधि

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

public static class DelegateExtensions
{
    public static void SafeInvoke(this Delegate del,params object[] args)
    {
        var exceptions = new List<Exception>();

        foreach (var handler in del.GetInvocationList())
        {
            try
            {
                handler.Method.Invoke(handler.Target, args);
            }
            catch (Exception ex)
            {
                exceptions.Add(ex);
            }
        }

        if(exceptions.Any())
        {
            throw new AggregateException(exceptions);
        }
    }
}

public class Test
{
    public delegate void SampleDelegate();

    public void Run()
    {
        SampleDelegate delegateInstance = this.Target2;
        delegateInstance += this.Target1;

        try
        {
            delegateInstance.SafeInvoke();
        } 
        catch(AggregateException ex)
        {
            // Do any exception handling here
        }
    }

    private void Target1()
    {
        Console.WriteLine("Target 1 executed");
    }

    private void Target2()
    {
        Console.WriteLine("Target 2 executed");
        throw new Exception();
    }
}

यह आउटपुट:

Target 2 executed
Target 1 executed

SaveInvoke बिना, सीधे SaveInvoke , केवल टारगेट 2 को निष्पादित करेगा।

एक प्रतिनिधि के अंदर बंद

क्लोजर इनलाइन अनाम विधियाँ हैं जिनमें Parent विधि चर और अन्य अनाम विधियों का उपयोग करने की क्षमता होती है जो माता-पिता के दायरे में परिभाषित होते हैं।

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

delegate int testDel();
static void Main(string[] args)
{
    int foo = 4;
    testDel myClosure = delegate()
    {
        return foo;
    };
    int bar = myClosure();

}

.NET में क्लोजर से लिया गया उदाहरण।

फन में परिवर्तन बदलना

public class MyObject{
    public DateTime? TestDate { get; set; }

    public Func<MyObject, bool> DateIsValid = myObject => myObject.TestDate.HasValue && myObject.TestDate > DateTime.Now;

    public void DoSomething(){
        //We can do this:
        if(this.TestDate.HasValue && this.TestDate > DateTime.Now){
            CallAnotherMethod();
        }

        //or this:
        if(DateIsValid(this)){
            CallAnotherMethod();
        }
    }
}

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

public void CheckForIntegrity(){
    if(ShipDateIsValid(this) && TestResultsHaveBeenIssued(this) && !TestResultsFail(this)){
        SendPassingTestNotification();
    }
}


Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow