C #을 사용하여 메소드를 매개 변수로 전달


695

동일한 서명 (매개 변수 및 반환 값)을 가진 여러 가지 방법이 있지만 이름과 방법의 내부가 다릅니다. 전달 된 메소드를 호출하는 다른 메소드로 실행할 메소드 이름을 전달하고 싶습니다.

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

답변:


853

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

51
메소드가 void를 리턴하는 서명으로 매개 변수가없는 경우 Func 호출은 어떻게 변경됩니까? 구문이 작동하지 않는 것 같습니다.
user31673

211
@unknown :이 경우 Action대신입니다 Func<string, int>.
Jon Skeet

12
그러나 이제 메소드에 인수를 전달하려면 어떻게해야합니까 ??
john ktejik

40
@ user396483 예를 들어, Action<int,string>2 개의 매개 변수 (int 및 string)를 사용하고 void를 리턴하는 메소드에 해당합니다.
serdar

24
@NoelWidmer Using Func<double,string,int>는 2 개의 매개 변수 ( doublestring) 를 가져 와서 반환 하는 메서드에 해당합니다 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

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 이것은 2 분 안에 딸랑이에 대한 놀라운 답변입니다.
David Hall

3
델리게이트를 사용하여 함수를 전달할 수 있지만보다 전통적인 OO 방식은 전략 패턴을 사용하는 것입니다.
Paolo

21
@Paolo : 대리인은 문제의 전략에 단일 방법 만 필요한 전략 패턴을 매우 편리하게 구현합니다. 이것이 전략 패턴 과 반대 되는 것은 아니지만 인터페이스를 사용하여 패턴을 구현하는 것보다 훨씬 편리합니다.
Jon Skeet

5
"클래식"델리게이트 (.NET 1/2에서 알려진)가 여전히 유용합니까, 아니면 Func / Action 때문에 완전히 폐기 되었습니까? 또한 귀하의 예에 대리인 키워드가 public **delegate** int MyDelegateType(string value)없습니까?
M4N

1
@ 마틴 :도! 수정 해 주셔서 감사합니다. 편집했습니다. 자신의 대리인을 선언하는 경우 형식 이름에 의미를 부여하는 것이 유용 할 수 있지만 .NET 3.5 이후로 내 자신의 대리인 유형을 거의 만들지 않았습니다.
Jon Skeet

112

OP의 예에서 :

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

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

Action Delegate를 시도 할 수 있습니다! 그런 다음 사용하여 메소드를 호출하십시오.

 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
John과 마찬가지로 이것은 일반적인 방법을 사용하는 데 실제로 도움이되었습니다. 감사!
ean5533

2
당신은 내 하루를 만들었습니다.) 선택한 답변 IMO보다 사용하기가 훨씬 쉽고 유연합니다.
Sidewinder94

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
매우 사용자 친화적입니다. 이 방법으로 하나 이상의 매개 변수를 사용할 수 있습니다. 가장 업데이트 된 답변은 이것입니다.
bafsar

이 구현에 대해 한 가지를 추가하고 싶습니다. 전달하려는 메소드에 void 유형의 void가 있으면이 솔루션을 사용할 수 없습니다.
Imants Volkovs

@ImantsVolkovs 나는 Func 대신 Action을 사용하도록 이것을 수정하고 서명을 void로 변경할 수 있다고 생각합니다. 100 % 확실하지 않습니다.
충격파

2
호출 된 함수에 매개 변수를 전달하는 방법이 있습니까?
Jimmy

17

가능한 한 완전한 솔루션을 공유하기 위해 세 가지 다른 방법을 제시하지만 이제는 가장 기본적인 원칙부터 시작하겠습니다.


간단한 소개

C #, F # 및 Visual Basic과 같은 CLR ( 공용 언어 런타임 )에서 실행되는 모든 언어 는 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);
}

세 가지 다른 방법, 같은 개념 뒤에 :

  • 방법 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 <T>의 리턴 타입이 마지막이 아니어야합니까? 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


2

수락 된 답변은 절대적으로 정확하지만 추가 방법을 제공하고 싶습니다.

나는 비슷한 질문에 대한 해결책을 스스로 찾은 후에 여기에 갔다. 플러그인 기반 프레임 워크를 구축 중이며 사람들 Menu이 프레임 워크가 MenuUI 가없는 다른 플랫폼에 배포 될 수 있기 때문에 실제 객체 를 노출시키지 않고 응용 프로그램 메뉴에 일반 메뉴에 메뉴 항목을 추가 할 수 있기를 원했습니다. 사물. 메뉴에 대한 일반적인 정보를 추가하는 것은 쉽지만, 플러그인 개발자가 메뉴를 클릭 할 때 콜백을 생성 할 수있는 충분한 자유를 허용하는 것은 고통 스러웠습니다. 내가 바퀴와 일반 메뉴 호출을 다시 발명하려고 시도하고 이벤트에서 콜백을 트리거하려고한다는 생각이들 때까지!

그래서 일단 당신이 그것을 깨닫고 들리는 것처럼 간단한 해결책은 지금까지 나를 피했습니다.

필요한 경우 기본에서 상속 된 현재 메소드 각각에 대해 별도의 클래스를 만들고 각 이벤트 핸들러를 추가하십시오.


1

다음은 함수를 매개 변수로 전달하는 방법을 더 잘 이해하는 데 도움이되는 예입니다.

부모 페이지가 있고 자식 팝업 창을 열려고 한다고 가정 하십시오. 부모 페이지에는 자식 팝업 텍스트 상자를 기준으로 채워 져야하는 텍스트 상자가 있습니다.

여기에서 대리인을 만들어야합니다.

Parent.cs // 델리게이트 선언 public delegate void FillName (String FirstName);

이제 텍스트 상자를 채우는 함수를 만들고 함수는 대리자를 매핑해야합니다.

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

이제 버튼을 클릭하면 Child 팝업 창을 열어야합니다.

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

         p.Show();

    }

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

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

두 번째 클래스는 클라이언트이며 스토리지 클래스를 사용합니다. 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.