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
メソッドの名前の代わりにデリゲートを渡してみませんか?
Mark Byers、2010年

回答:


852

RunTheMethodメソッドのパラメーターとして、.net 3.5のFuncデリゲートを使用できます。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
メソッドがvoidを返し、パラメータを返さないというシグネチャを持っている場合、Func呼び出しはどのように変化しますか?構文が機能しないようです。
user31673 2010年

210
@unknown:その場合はのAction代わりになりFunc<string, int>ます。
Jon Skeet、2010年

12
しかし、メソッドに引数を渡したい場合はどうでしょうか?
john ktejik 2014

40
@ user396483たとえば、Action<int,string>2つのパラメーター(intおよびstring)を取り、voidを返すメソッドに対応します。
serdar 2014年

24
@NoelWidmer Using 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);。誤解した場合は訂正してください。
serdar 2014

356

デリゲートを使用する必要があります。この場合、すべてのメソッドはstringパラメーターを取り、anを返します。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これは本当に2分でガタガタ音を立てる素晴らしい答えです。
David Hall、

3
デリゲートを使用して関数を渡すことができますが、より伝統的なオブジェクト指向のアプローチは戦略パターンを使用することです。
Paolo

20
@Paolo:デリゲートは、問題の戦略が単一のメソッドのみを必要とする戦略パターンの非常に便利な実装です。これが戦略パターンに反するようなものではありませんが、インターフェースを使用してパターンを実装するよりもはるかに便利です。
Jon Skeet

5
「クラシック」デリゲート(.NET 1/2から知られている)はまだ有用ですか、それともFunc / Actionのために完全に廃止されましたか?また、あなたの例にはデリゲートキーワードがありませんpublic **delegate** int MyDelegateType(string value)か?
M4N 2010年

1
@マーティン:ドー!修正をありがとう。編集。独自のデリゲートを宣言する場合と同様に、型名に何らかの意味を付けると便利ですが、.NET 3.5以降、独自のデリゲート型を作成することはほとんどありません。
Jon Skeet、2010年

112

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

4
おかげで、複数のパラメーターを使用できるより一般的な「RunTheMethod」メソッドが必要だったので、これで行きたいところに行きました。ところで、最初のInvokeMethodラムダコールはRunTheMethod代わりに
John

1
ジョンのように、これは私がmoveジェネリックメソッドを持つのを本当に助けました。ありがとう!
ean5533

2
あなたは私の一日を作りました;)本当に使いやすく、選択した回答IMOよりもはるかに柔軟です。
Sidewinder94 2014年

RunTheMethod(()=> Method1( "MyString1"));を展開する方法はありますか?戻り値を取得するには?理想的にはジェネリック?
ジェイ

パラメータを渡したい場合は、このことに注意してください: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
それは非常に使い勝手が良いです。この方法では、1つまたは複数のパラメーターを使用できます。おそらく、最新の答えはこれです。
bafsar 2015年

この実装について1つ追加したいと思います。渡そうとしているメソッドの戻り型がvoidの場合、このソリューションは使用できません。
イマンツヴォルコフス2016年

@ImantsVolkovsこれを変更して、Funcの代わりにActionを使用し、署名をvoidに変更できると思います。100%確かではありません。
Shockwave 2017年

2
呼び出される関数にパラメーターを渡す方法はありますか?
ジミー

16

可能な限り完全なソリューションを共有するために、3つの異なる方法を提示することになりますが、ここでは、最も基本的な原則から始めます。


簡単な紹介

C#、F#、Visual Basicなど、CLR(共通言語ランタイム)上で実行されるすべての言語はVMの下で動作し、VMの下で動作し、CやC ++(直接マシンにコンパイルされる)のようなネイティブ言語よりも高いレベルでコードを実行しますコード)。つまり、メソッドはコンパイルされたブロックではなく、CLRが認識する構造化された要素にすぎません。したがって、メソッドは式ではないため、メソッド自体は値を生成しないため、メソッドをパラメーターとして渡すことはできません。むしろ、それらは生成されたCILコードで定義されたステートメントです。したがって、デリゲートの概念に直面します。


デリゲートとは何ですか?

デリゲートは、メソッドへのポインタを表します。上で述べたように、メソッドは値ではないため、CLR言語には特別なクラスがあり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);
}

3つの異なる方法、背後にある同じ概念:

  • 方法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引数なしでa をラップします。
    • Action<T1>voidを1つの引数でラップします。
    • Action<T1, T2>a voidを2つの引数でラップします。
    • 等々...
    • Func<TR>TR戻り値の型があり、引数のない関数をラップします。
    • Func<T1, TR>TR戻り値の型と1つの引数を持つ関数をラップします。
    • Func<T1, T2, TR>TR戻り値の型と2つの引数を持つ関数をラップします。
    • 等々...

(後者のソリューションは、ほとんどの人が投稿したものです。)


1
Func <T>の戻り値の型は最後でなければなりませんか? Func<T1,T2,TR>
sanmcp

13

Func<string, int>デリゲートを使用する必要があります。デリゲートは、a 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


2

受け入れられた答えは完全に正しいですが、私は追加の方法を提供したいと思います。

私は同様の質問の解決策を自分で探した後、ここに行きました。私はプラグイン主導のフレームワークを構築しています。その一部として、UI Menuを持たない他のプラットフォームにフレームワークがデプロイされる可能性があるため、実際のオブジェクトを公​​開することなく、メニュー項目をアプリケーションメニューの一般的なリストに追加できるようにしたかったのです。Menuオブジェクト。メニューに関する一般的な情報を追加するのは簡単ですが、メニューがクリックされたときにコールバックを作成するための十分な自由をプラグイン開発者に許可するのは面倒であることが判明しました。ホイールと通常のメニューの呼び出しを再発明し、イベントからのコールバックをトリガーしようとしていることが、私に気づかされるまで!

したがって、ソリューションは、あなたがそれを理解するとすぐに聞こえるのと同じくらい簡単なものでしたが、今まで私を避けていました。

現在のメソッドごとに個別のクラスを作成し、必要に応じてベースから継承し、それぞれにイベントハンドラーを追加するだけです。


1

以下は、関数をパラメーターとして渡す方法を理解するのに役立つ例です。

あなたが持っていると仮定し、親ページを、あなたが子供のポップアップウィンドウを開くようにしたいです。親ページには、子ポップアップテキストボックスに基づいて入力するテキストボックスがあります。

ここで、デリゲートを作成する必要があります。

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

    }

親//ページの「デリゲートタイプ」のパラメーターを作成する必要があるIN ChildPopUpコンストラクター

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

編集されましたが、この回答の品質はまだ改善される可能性があります。
SteakOverflow、2016年

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

パラメータあり:http : //www.daniweb.com/forums/thread98148.html#

基本的には、メソッドの名前とともにオブジェクトの配列を渡します。次に、両方をInvokeメソッドで使用します。

params Object []パラメータ


メソッドの名前は文字列に含まれていないことに注意してください-実際にはメソッドグループです。ここでは代理人が最良の答えであり、反省ではありません。
Jon Skeet

@Lette:メソッド呼び出しでは、引数として使用される式はメソッドグループです。これは、コンパイル時に認識されるメソッドの名前であり、コンパイラはこれをデリゲートに変換できます。これは、実行時にのみ名前がわかる状況とは大きく異なります。
Jon Skeet

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

2番目のクラスはクライアントで、ストレージクラスを使用します。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);
  }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.