C # का उपयोग करते हुए पैरामीटर को पास करें


694

मेरे पास एक ही हस्ताक्षर (पैरामीटर और रिटर्न मान) के साथ कई तरीके हैं, लेकिन अलग-अलग नाम और विधियों के आंतरिक भिन्न हैं। मैं किसी अन्य विधि को चलाने के लिए विधि के नाम को पारित करना चाहता हूं जो पारित विधि को लागू करेगा।

public int Method1(string)
{
    ... do something
    return myInt;
}

public int Method2(string)
{
    ... do something different
    return myInt;
}

public bool RunTheMethod([Method Name passed in here] myMethodName)
{
    ... do stuff
    int i = myMethodName("My String");
    ... do more stuff
    return true;
}

public bool Test()
{
    return RunTheMethod(Method1);
}

यह कोड काम नहीं करता है लेकिन यह वही है जो मैं करने की कोशिश कर रहा हूं। मुझे समझ में नहीं आता है कि RunTheMethod कोड को कैसे लिखना है क्योंकि मुझे पैरामीटर को परिभाषित करने की आवश्यकता है।


12
आप विधि के नाम के बजाय एक प्रतिनिधि को पास क्यों नहीं करते?
मार्क बायर्स

जवाबों:


852

आप अपने RunTheMethod विधि में पैरामीटर के रूप में .net 3.5 में Func डेलिगेट का उपयोग कर सकते हैं। फंक प्रतिनिधि आपको एक विधि निर्दिष्ट करने की अनुमति देता है जो एक विशिष्ट प्रकार के कई मापदंडों को लेता है और एक विशिष्ट प्रकार का एक ही तर्क लौटाता है। यहाँ एक उदाहरण है कि काम करना चाहिए:

public class Class1
{
    public int Method1(string input)
    {
        //... do something
        return 0;
    }

    public int Method2(string input)
    {
        //... do something different
        return 1;
    }

    public bool RunTheMethod(Func<string, int> myMethodName)
    {
        //... do stuff
        int i = myMethodName("My String");
        //... do more stuff
        return true;
    }

    public bool Test()
    {
        return RunTheMethod(Method1);
    }
}

51
यदि विधि शून्य वापस करने के हस्ताक्षर के रूप में है और कोई पैरामीटर नहीं है, तो फंक कॉल कैसे बदल जाएगा? मैं काम करने के लिए वाक्यविन्यास प्राप्त करने के लिए प्रतीत नहीं कर सकता।
user31673

211
@unknown: उस मामले में इसके Actionबजाय होगा Func<string, int>
जॉन स्कीट

12
लेकिन अब क्या होगा अगर आप विधि में तर्क करना चाहते हैं ??
जॉन ktejik

40
@ user396483 उदाहरण के लिए, Action<int,string>एक विधि से मेल खाती है जो 2 पैरामीटर (इंट और स्ट्रिंग) ले रही है और शून्य लौट रही है।
सर्दार

24
@NoelWidmer Func<double,string,int>एक ऐसे तरीके से मेल खाता है जो 2 पैरामीटर ( doubleऔर string) लेता है और वापस लौटता है int। अंतिम निर्दिष्ट प्रकार रिटर्न प्रकार है। आप इस प्रतिनिधि का उपयोग 16 मापदंडों तक कर सकते हैं। यदि आपको किसी तरह की आवश्यकता है, तो अपने स्वयं के प्रतिनिधि के रूप में लिखें public delegate TResult Func<in T1, in T2, (as many arguments as you want), in Tn, out TResult>(T1 arg1, T2 arg2, ..., Tn argn);। अगर मुझे गलत समझा तो कृपया मुझे सुधार दें।
सेदार

356

आपको एक प्रतिनिधि का उपयोग करने की आवश्यकता है । इस मामले में आपके सभी तरीके एक stringपैरामीटर लेते हैं और एक वापसी करते हैं int- यह सबसे सरल रूप से Func<string, int>प्रतिनिधि 1 द्वारा दर्शाया गया है । तो आपका कोड इस तरह से सरल बदलाव के साथ सही हो सकता है:

public bool RunTheMethod(Func<string, int> myMethodName)
{
    // ... do stuff
    int i = myMethodName("My String");
    // ... do more stuff
    return true;
}

डेलीगेट्स के पास इससे कहीं ज्यादा ताकत है, भर्ती तौर पर। उदाहरण के लिए, C # के साथ आप लैम्ब्डा एक्सप्रेशन से एक डेलीगेट बना सकते हैं , इसलिए आप अपने तरीके को इस तरह से लागू कर सकते हैं:

RunTheMethod(x => x.Length);

यह इस तरह एक अनाम समारोह बनाएगा:

// The <> in the name make it "unspeakable" - you can't refer to this method directly
// in your own code.
private static int <>_HiddenMethod_<>(string x)
{
    return x.Length;
}

और फिर उस प्रतिनिधि को RunTheMethodविधि में पास करें।

आप ईवेंट सब्सक्रिप्शन, अतुल्यकालिक निष्पादन, कॉलबैक - सभी प्रकार की चीजों के लिए प्रतिनिधियों का उपयोग कर सकते हैं। यह उन पर पढ़ने लायक है, खासकर यदि आप LINQ का उपयोग करना चाहते हैं। मेरे पास एक लेख है जो ज्यादातर प्रतिनिधियों और घटनाओं के बीच अंतर के बारे में है, लेकिन आप इसे वैसे भी उपयोगी पा सकते हैं।


1 यह सिर्फ Func<T, TResult>फ्रेमवर्क में जेनेरिक डेलीगेट के प्रकार पर आधारित है; आप आसानी से अपनी घोषणा कर सकते हैं:

public delegate int MyDelegateType(string value)

और फिर MyDelegateTypeइसके बदले पैरामीटर टाइप का बनाएं ।


59
+1 यह वास्तव में दो मिनट में दूर करने के लिए एक अद्भुत जवाब है।
डेविड हॉल

3
जब आप प्रतिनिधियों का उपयोग करके फ़ंक्शन पास कर सकते हैं, तो रणनीति पैटर्न का उपयोग करने के लिए एक अधिक पारंपरिक ओओ दृष्टिकोण होगा।
पाओलो

21
@ पाओलो: प्रतिनिधि रणनीति पैटर्न का सिर्फ एक बहुत सुविधाजनक कार्यान्वयन है जहां प्रश्न में रणनीति को केवल एक ही विधि की आवश्यकता होती है। ऐसा नहीं है कि यह रणनीति पैटर्न के खिलाफ जा रहा है - लेकिन यह इंटरफेस का उपयोग करके पैटर्न को लागू करने की तुलना में बहुत अधिक सुविधाजनक है।
जॉन स्कीट

5
"क्लासिक" प्रतिनिधियों (जैसा कि .NET 1/2 से जाना जाता है) अभी भी उपयोगी हैं, या वे फंक / एक्शन के कारण पूरी तरह से अप्रचलित हैं? इसके अलावा, क्या आपके उदाहरण में एक प्रतिनिधि कीवर्ड गायब नहीं है public **delegate** int MyDelegateType(string value)?
M4N

1
@ मरीन: दोह! ठीक करने के लिए धन्यवाद। संपादित। अपने स्वयं के प्रतिनिधियों की घोषणा करने के लिए - यह टाइप नाम को कुछ अर्थ देने के लिए उपयोगी हो सकता है, लेकिन मैंने शायद ही कभी अपने स्वयं के प्रतिनिधि प्रकार को .NET 3.5 के लिए बनाया हो।
जॉन स्कीट

112

ओपी के उदाहरण से:

 public static int Method1(string mystring)
 {
      return 1;
 }

 public static int Method2(string mystring)
 {
     return 2;
 }

आप एक्शन डेलिगेट आज़मा सकते हैं! और फिर अपनी विधि का उपयोग करके कॉल करें

 public bool RunTheMethod(Action myMethodName)
 {
      myMethodName();   // note: the return value got discarded
      return true;
 }

RunTheMethod(() => Method1("MyString1"));

या

public static object InvokeMethod(Delegate method, params object[] args)
{
     return method.DynamicInvoke(args);
}

फिर बस कॉल विधि

Console.WriteLine(InvokeMethod(new Func<string,int>(Method1), "MyString1"));

Console.WriteLine(InvokeMethod(new Func<string, int>(Method2), "MyString2"));

4
धन्यवाद, यह मुझे मिला जहां मैं जाना चाहता था क्योंकि मैं एक अधिक सामान्य "RunTheMethod" विधि चाहता था जो कई मापदंडों के लिए अनुमति देगा। Btw अपने पहले InvokeMethodlambda कॉल RunTheMethodबजाय होना चाहिए
जॉन

1
जॉन की तरह, इसने वास्तव में मुझे एक चाल सामान्य विधि की मदद की। धन्यवाद!
.555533

2
आपने मेरा दिन;) चयनित उत्तर IMO की तुलना में वास्तव में उपयोग करने के लिए सरल और बहुत अधिक लचीला है।
सिद्दिविंदर 94

क्या RunTheMethod () => Method1 ("MyString1")) पर विस्तार करने का एक तरीका है; वापसी मान प्राप्त करने के लिए? आदर्श रूप में एक सामान्य?
Jay

यदि आप मापदंडों से अवगत कराना चाहते हैं: stackoverflow.com/a/5414539/2736039
Ultimo_m

31
public static T Runner<T>(Func<T> funcToRun)
{
    //Do stuff before running function as normal
    return funcToRun();
}

उपयोग:

var ReturnValue = Runner(() => GetUser(99));

6
यह बहुत उपयोगकर्ता है। इस तरह से, एक या कई मापदंडों का उपयोग कर सकते हैं। मुझे लगता है, सबसे अद्यतन उत्तर यह है।
बाफसर

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

@ImantsVolkovs मेरा मानना ​​है कि आप फंक के बजाय एक्शन का उपयोग करने के लिए इसे संशोधित करने में सक्षम हो सकते हैं, और हस्ताक्षर को शून्य में बदल सकते हैं। हालांकि 100% निश्चित नहीं है।
शोकेव

2
क्या किसी फ़ंक्शन को पैरामीटर को पास करने का कोई तरीका है जिसे कहा जाता है?
जिमी

17

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


एक संक्षिप्त परिचय

सीएलआर ( कॉमन लैंग्वेज रनटाइम ) के शीर्ष पर चलने वाली सभी भाषाएँ , जैसे C #, F # और विज़ुअल बेसिक, एक VM के तहत काम करती हैं, जो कि C और C ++ जैसी देशी भाषाओं की तुलना में उच्च स्तर पर कोड चलाता है (जो सीधे मशीन को संकलित करता है। कोड)। यह इस प्रकार है कि विधियाँ किसी भी प्रकार के संकलित ब्लॉक नहीं हैं, लेकिन वे केवल संरचित तत्व हैं जिन्हें सीएलआर पहचानता है। इस प्रकार, आप एक विधि को एक पैरामीटर के रूप में पारित करने के लिए नहीं सोच सकते हैं, क्योंकि विधियां स्वयं किसी भी मूल्य का उत्पादन नहीं करती हैं, क्योंकि वे अभिव्यक्ति नहीं हैं! बल्कि, वे कथन हैं, जो उत्पन्न CIL कोड में परिभाषित हैं। तो, आप प्रतिनिधि अवधारणा का सामना करेंगे।


एक प्रतिनिधि क्या है?

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

निम्नलिखित उदाहरण देखें:

static void MyMethod()
{
    Console.WriteLine("I was called by the Delegate special class!");
}

static void CallAnyMethod(Delegate yourMethod)
{
    yourMethod.DynamicInvoke(new object[] { /*Array of arguments to pass*/ });
}

static void Main()
{
    CallAnyMethod(MyMethod);
}

तीन अलग-अलग तरीके, एक ही अवधारणा के पीछे:

  • तरीका 1 ऊपर दिए गए उदाहरण के रूप में सीधे विशेष वर्ग का
    उपयोग करें Delegate। इस समाधान की समस्या यह है कि आपका कोड अनियंत्रित हो जाएगा क्योंकि आप विधिवत रूप से अपने तर्कों को विधि परिभाषा में उन लोगों के प्रकारों तक सीमित किए बिना पास कर देंगे।

  • वे 2 विशेष वर्ग के
    अलावा Delegate, प्रतिनिधि अवधारणा कस्टम प्रतिनिधियों को फैलती है, जो कि पूर्व की विधियों की परिभाषा हैdelegate कीवर्ड , और वे सामान्य तरीकों के समान व्यवहार करते हैं। वे इस प्रकार जांचे जाते हैं, इसलिए आप त्रुटिपूर्ण सुरक्षित कोड के साथ आएंगे।

    यहाँ एक उदाहरण है:

    delegate void PrintDelegate(string prompt);
    
    static void PrintSomewhere(PrintDelegate print, string prompt)
    {
        print(prompt);
    }
    
    static void PrintOnConsole(string prompt)
    {
        Console.WriteLine(prompt);
    }
    
    static void PrintOnScreen(string prompt)
    {
        MessageBox.Show(prompt);
    }
    
    static void Main()
    {
        PrintSomewhere(PrintOnConsole, "Press a key to get a message");
        Console.Read();
        PrintSomewhere(PrintOnScreen, "Hello world");
    }
  • वे 3
    वैकल्पिक रूप से, आप एक प्रतिनिधि का उपयोग कर सकते हैं जो पहले से ही .NET मानक में परिभाषित किया गया है:

    • Actionvoidबिना किसी तर्क के लपेटता है।
    • Action<T1>voidएक तर्क के साथ लपेटता है।
    • Action<T1, T2>voidदो तर्कों के साथ लपेटता है।
    • और इसी तरह...
    • Func<TR>एक फ़ंक्शन को TRरिटर्न प्रकार के साथ लपेटता है और बिना किसी तर्क के।
    • Func<T1, TR>TRवापसी प्रकार के साथ और एक तर्क के साथ एक फ़ंक्शन को लपेटता है।
    • Func<T1, T2, TR> के साथ एक समारोह लपेटता है TR रिटर्न प्रकार और दो तर्कों के साथ है।
    • और इसी तरह...

(बाद वाला समाधान पोस्ट किए गए अधिकांश लोगों का है।)


1
एक फ़ंक <टी> का रिटर्न प्रकार अंतिम नहीं होना चाहिए? Func<T1,T2,TR>
sanmcp

13

आपको एक Func<string, int>प्रतिनिधि का उपयोग करना चाहिए , जो एक stringतर्क के रूप में एक फ़ंक्शन का प्रतिनिधित्व करता है और एक लौटाता है int:

public bool RunTheMethod(Func<string, int> myMethod) {
    // do stuff
    myMethod.Invoke("My String");
    // do stuff
    return true;
}

फिर इसका उपयोग करें:

public bool Test() {
    return RunTheMethod(Method1);
}

3
यह संकलन नहीं होगा। Testविधि होना चाहिएreturn RunTheMethod(Method1);
pswg

7

यदि आप चाहते हैं कि किस विधि को चलाने के समय कहा जाता है तो मैं एक प्रतिनिधि का उपयोग करने की सलाह दूंगा: http://www.codeproject.com/KB/cs/delegates_step1.aspx

यह आपको कॉल करने के तरीके को स्टोर करने के लिए एक ऑब्जेक्ट बनाने की अनुमति देगा और जब आप इसकी आवश्यकता हो तो इसे अपने अन्य तरीकों से पास कर सकते हैं।


2

जबकि स्वीकृत उत्तर बिल्कुल सही है, मैं एक अतिरिक्त तरीका प्रदान करना चाहूंगा।

इसी तरह के प्रश्न के हल की खोज करने के बाद मैं यहाँ समाप्त हुआ। मैं एक प्लगइन संचालित फ्रेमवर्क का निर्माण कर रहा हूं, और इसके हिस्से के रूप में मैं चाहता था कि लोग वास्तविक Menuवस्तु को उजागर किए बिना एक सामान्य सूची में एप्लिकेशन मेनू में मेनू आइटम जोड़ने में सक्षम हों क्योंकि फ्रेमवर्क अन्य प्लेटफार्मों पर तैनात हो सकता है जो कि नहीं हैMenu यूआई वस्तुओं। मेनू के बारे में सामान्य जानकारी जोड़ना काफी आसान है, लेकिन जब मेनू पर क्लिक किया जाता है तो दर्द पैदा करने के लिए प्लगइन डेवलपर को कॉलबैक बनाने के लिए पर्याप्त स्वतंत्रता की अनुमति देता है। जब तक यह मुझ पर नहीं चढ़ा कि मैं पहिया और सामान्य मेनू कॉल का फिर से आविष्कार करने और घटनाओं से कॉलबैक को ट्रिगर करने की कोशिश कर रहा था!

तो यह समाधान, जितना आसान लगता है एक बार जब आप इसे महसूस करते हैं, तो मुझे अब तक हटा दिया गया है।

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


1

यहां एक उदाहरण दिया गया है जो आपको यह समझने में मदद कर सकता है कि एक पैरामीटर के रूप में फ़ंक्शन कैसे पारित किया जाए।

मान लीजिए कि आपके पास पेरेंट पेज है और आप चाइल्ड पॉपअप विंडो खोलना चाहते हैं। मूल पृष्ठ में एक टेक्स्टबॉक्स होता है जिसे चाइल्ड पॉपअप टेक्स्टबॉक्स पर आधारित होना चाहिए।

यहां आपको एक प्रतिनिधि बनाने की आवश्यकता है।

जनप्रतिनिधि // प्रतिनिधियों की घोषणा जनप्रतिनिधि शून्य शून्य नाम (स्ट्रिंग फर्स्टनाम);

अब एक फंक्शन बनाएं जो आपके टेक्स्टबॉक्स को भरे और फंक्शन को डेलिगेट्स को मैप करना चाहिए

//parameters
public void Getname(String ThisName)
{
     txtname.Text=ThisName;
}

अब बटन पर क्लिक करने पर आपको चाइल्ड पॉपअप विंडो खोलने की जरूरत है।

  private void button1_Click(object sender, RoutedEventArgs e)
  {
        ChildPopUp p = new ChildPopUp (Getname) //pass function name in its constructor

         p.Show();

    }

चाइल्डपॉप कंस्ट्रक्टर में आपको पेरेंट // पेज के 'डेलिगेट टाइप' का पैरामीटर बनाना होगा

ChildPopUp.cs

    public  Parent.FillName obj;
    public PopUp(Parent.FillName objTMP)//parameter as deligate type
    {
        obj = objTMP;
        InitializeComponent();
    }



   private void OKButton_Click(object sender, RoutedEventArgs e)
    {


        obj(txtFirstName.Text); 
        // Getname() function will call automatically here
        this.DialogResult = true;
    }

संपादित लेकिन इस उत्तर की गुणवत्ता में अभी भी सुधार किया जा सकता है।
स्टेकऑवरफ्लो

1

यदि आप विधि को पैरामीटर के रूप में पास करना चाहते हैं, तो उपयोग करें:

using System;

public void Method1()
{
    CallingMethod(CalledMethod);
}

public void CallingMethod(Action method)
{
    method();   // This will call the method that has been passed as parameter
}

public void CalledMethod()
{
    Console.WriteLine("This method is called by passing parameter");
}

0

यहाँ एक पैरामीटर के बिना एक उदाहरण है: http://en.csharp-online.net/CSharp_FAQ:_How_call_a_method_using_a_name_string

params के साथ: http://www.daniweb.com/forums/thread98148.html#

आप मूल रूप से विधि के नाम के साथ वस्तुओं की एक सरणी में गुजरते हैं। फिर आप इनवोक विधि से दोनों का उपयोग करें।

पैराम्स ऑब्जेक्ट [] पैरामीटर


ध्यान दें कि विधि का नाम स्ट्रिंग में नहीं है - यह वास्तव में एक विधि समूह के रूप में है। डेलिगेट्स यहां सबसे अच्छा जवाब हैं, प्रतिबिंब नहीं।
जॉन स्कीट

@ विधि: विधि आह्वान में, तर्क के रूप में प्रयुक्त अभिव्यक्ति एक विधि समूह है ; यह एक विधि का नाम है जिसे संकलन-समय पर जाना जाता है, और संकलक इसे एक प्रतिनिधि में बदल सकता है। यह उस स्थिति से बहुत अलग है जहां नाम केवल निष्पादन के समय ही जाना जाता है।
जॉन स्कीट

0
class PersonDB
{
  string[] list = { "John", "Sam", "Dave" };
  public void Process(ProcessPersonDelegate f)
  {
    foreach(string s in list) f(s);
  }
}

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

class Client
{
  static void Main()
  {
    PersonDB p = new PersonDB();
    p.Process(PrintName);
  }
  static void PrintName(string name)
  {
    System.Console.WriteLine(name);
  }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.