簡體   English   中英

使用 C# 將方法作為參數傳遞

[英]Pass Method as Parameter using C#

我有幾個方法都具有相同的參數類型和返回值,但名稱和塊不同。 我想將要運行的方法的名稱傳遞給另一個將調用傳遞的方法的方法。

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 代碼,因為我需要定義參數。

您可以使用 .net 3.5 中的 Func 委托作為 RunTheMethod 方法中的參數。 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);
    }
}

您需要使用委托 在這種情況下,您的所有方法都采用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#,您可以從lambda 表達式創建委托,因此您可以通過以下方式調用您的方法:

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類型。

從OP的例子:

 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"));

為了提供一個清晰完整的答案,我將從一開始就開始,然后提出三種可能的解決方案。


簡介

在 CLR(公共語言運行時)之上運行的所有語言,例如 C#、F# 和 Visual Basic,都在運行比機器代碼更高級別代碼的 VM 下工作。 與 JavaScript 和大多數函數式語言不同,方法不是匯編子例程,也不是值。 相反,它們是 CLR 識別的符號。 因此,您不能考慮將方法作為參數傳遞,因為方法本身不會產生任何值,因為它們不是表達式而是語句,它們存儲在生成的程序集中。 此時,您將面對代表。


什么是代表?

委托代表一個方法的句柄(句柄一詞應該優先於指針,因為后者將是一個實現細節)。 由於方法不是值,因此 .NET 中必須有一個特殊的類,即Delegate ,它封裝了任何方法。 它的特別之處在於,它和極少數類一樣,需要CLR自己實現,不能自己實現。

看下面的例子:

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);
}

三種不同的解決方案,相同的基本概念

  • 類型不安全的方式

    直接使用Delegate特殊類的方式與上例相同。 這里的缺點是您的代碼類型不安全,允許動態傳遞參數,沒有約束。

  • 自定義方式

    除了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"); }
  • 標准庫的方式

    或者,您可以使用屬於 .NET 標准的委托:

    • Action封裝了一個無參數的void方法。
    • Action<T1>用一個T1類型的參數包裝了一個void方法。
    • Action<T1, T2>分別用T1T2類型的兩個參數包裝了一個void方法。
    • 等等……
    • Func<TR>包裝了一個帶有TR返回類型的無參數函數。
    • Func<T1, TR>包裝了一個具有TR返回類型和一個T1類型參數的函數。
    • Func<T1, T2, TR>包裝了一個返回類型為TR的函數以及兩個類型分別為T1T2的參數。
    • 等等……

    但是,請記住,通過使用像這樣的預定義委托,參數名稱不會描述它們必須傳遞的內容,委托名稱也不會對它應該做的事情有意義。 因此,在使用這些委托不會影響代碼自我描述的好處時要小心,並避免在其目的不是絕對不言而喻的情況下使用它們。

該解決方案涉及Delegates ,用於存儲要調用的方法。 定義一個將委托作為參數的方法,

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

然后在調用站點上傳遞委托:

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

您應該使用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);
}

我有幾種方法,它們的參數類型和返回值都相同,但名稱和塊卻不同。 我想將要運行的方法的名稱傳遞給另一個方法,該方法將調用傳遞的方法。

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代碼,因為我需要定義參數。

這是一個示例,它可以幫助您更好地理解如何將函數作為參數傳遞。

假設您有頁面並且您想打開一個子彈出窗口。 在父頁面中有一個文本框,應該根據子彈出文本框填充。

在這里,您需要創建一個委托。

Parent.cs // 委托聲明 public delegate void FillName(String FirstName);

現在創建一個將填充您的文本框的函數,並且函數應該映射委托

//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 構造函數中,您需要創建父 //page 的“委托類型”參數

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;
    }

雖然公認的答案是絕對正確的,但我想提供一種額外的方法。

在自己尋找類似問題的解決方案后,我來到了這里。 我正在構建一個插件驅動的框架,作為其中的一部分,我希望人們能夠將菜單項添加到應用程序菜單到一個通用列表而不暴露實際的Menu對象,因為該框架可能部署在其他沒有的平台上Menu UI 對象。 添加有關菜單的一般信息很容易,但是讓插件開發人員有足夠的自由來創建單擊菜單時的回調被證明是一種痛苦。 直到我突然意識到我試圖重新發明輪子和正常的菜單調用並觸發事件的回調!

所以解決方案,一旦你意識到它聽起來很簡單,直到現在我才發現它。

只需為每個當前方法創建單獨的類,如果必須從基類繼承,然后為每個方法添加一個事件處理程序。

如果要將 Method 作為參數傳遞,請使用:

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 it as a parameter");
}

這是一個沒有參數的例子:http://en.csharp-online.net/CSharp_FAQ: _How_call_a_method_using_a_name_string

帶參數: http ://www.daniweb.com/forums/thread98148.html#

您基本上傳入一個對象數組以及方法名稱。 然后將兩者與 Invoke 方法一起使用。

參數 Object[] 參數

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

第二個類是Client,它將使用存儲類。 它有一個創建 PersonDB 實例的 Main 方法,並使用在 Client 類中定義的方法調用該對象的 Process 方法。

class Client
{
  static void Main()
  {
    PersonDB p = new PersonDB();
    p.Process(PrintName);
  }
  static void PrintName(string name)
  {
    System.Console.WriteLine(name);
  }
}

我不知道誰可能需要這個,但如果你不確定如何發送帶有委托的 lambda,當使用委托的函數不需要在其中插入任何參數時,你只需要返回值。

所以你也可以這樣做:

public int DoStuff(string stuff)
{
    Console.WriteLine(stuff);
}

public static bool MethodWithDelegate(Func<int> delegate)
{
    ///do stuff
    int i = delegate();
    return i!=0;
}

public static void Main(String[] args)
{
    var answer = MethodWithDelegate(()=> DoStuff("On This random string that the MethodWithDelegate doesn't know about."));
}

如果傳遞的方法需要接受一個參數並返回一個值,那么Func是最好的方法。 這是一個例子。

public int Method1(string)
{
    // Do something
    return 6;
}

public int Method2(string)
{
    // Do something different
    return 5;
}

public bool RunTheMethod(Func<string, int> myMethodName)
{
    // Do stuff
    int i = myMethodName("My String");
    Console.WriteLine(i); // This is just in place of the "Do more stuff"
    return true;
}

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

此處閱讀文檔

但是,如果作為參數傳遞的方法沒有返回任何內容,您也可以使用Action 對於傳遞的方法,它最多支持 16 個參數。 這是一個例子。 public int MethodToBeCalled(string name, int age) { Console.WriteLine(name + "'s age is" + age); }

public bool RunTheMethod(Action<string, int> myMethodName)
{
    // Do stuff
    myMethodName("bob", 32); // Expected output: "bob's age is 32"
    return true;
}

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

此處閱讀文檔

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM