Передача общего метода в качестве параметра в С#

Я пытаюсь написать функцию, которая принимает другую функцию в качестве параметра (и его вызовы позже), я знаю, что ее выполнимость, если я знаю подпись функции заранее, но возможно ли это, если я не? (например, передача функции в качестве параметра в JavaScript)

Например:

// this should accept a function with any kind of signature and return value
void DelayedCall(?? functionToCallLater, float delayInSecs, params object[] values)
{
    //here do something like (just as an example)
    functionToCallLater.Invoke(values);
}

EDIT: я не хочу ничего говорить о functionToCallLater Он может выглядеть так:

    int MyFunc()
    void MyFunc2()
    void MyFunc3(int someParam) 
    string MyFunc4(int someParam, MyClass someOtherParam) 
    void MyFunc5<T>(params T[] values) 

Ответ 1

Вы имеете в виду это?:

void DelayedCall<T>(Action<T> functionToCallLater, 
  float delayInSecs, params T[] values)
{
    //here do something like (just as an example)
    functionToCallLater(values);
}

У вас есть объект [] как ваши значения, но я предполагаю, что вы хотите также указать их тип, используя общий. Кроме того, поскольку он не выглядел так, как ваш метод, который вы вызываете, не имеет типа возврата, я использовал Action<T> вместо Func<T>.

Основываясь на приведенном ниже комментарии,

Если вы хотите принимать разные подписи, у вас действительно есть 2 разных варианта. Первый, как сказал Слакс, - использовать отражение. Это может стать чрезвычайно сложным, поскольку вам нужно будет определить положение и типы параметров в функцииToCallLater и выровнять их с входящими параметрами из родительской функции. Есть много семантики, которые действительно делают это трудным (хотя это возможно) и действительно больше проблем, чем это хуже, когда проблема обобщается для обработки большого числа случаев.

Второй и более проверяемый способ обработки этого сценария (хотя и не обязательно меньше работы) заключается в создании перегрузок для рассматриваемого метода:

void DelayedCall<T>(Func<T> functionToCallLater, 
      float delayInSecs, params T[] values)

void DelayedCall<T,R>(Func<T,R> functionToCallLater, 
      float delayInSecs, params T[] values)

void DelayedCall<T>(Func<T,X,R> functionToCallLater, 
      float delayInSecs, params T[] values)

etc.

В зависимости от того, насколько сложным является метод перегрузки, это может быть немного утомительным почти невозможным, но оно будет работать. Реальный вопрос, который вы хотите задать себе в этом сценарии, заключается в следующем: "Это оптимальный способ справиться с этим подходом?" Например, если вы не нуждаетесь в возвращаемом значении в вызывающем методе, зачем вообще его разрешать? Вы всегда можете заставить вызывающего объекта обернуть функцию другим методом, который придерживается подписи метода следующим образом:

void DelayedCall<T>(Action<T> functionToCallLater, 
      float delayInSecs, params T[] values)
....
 DelayledCallAction<int>(
    (i) => MyMethodWhichReturnsSomething(i), 
    floadDelayInSecs, 1,2,3);

Это устраняет некоторые сценарии и функциональные возможности, которые должен обрабатывать метод. Путем сокращения возможных параметров, которые должна обрабатывать подпись метода, проблема становится более простой и управляемой.

Ответ 2

Если вы ничего не знаете о функции, вам нужно будет использовать Reflection (и заплатить штраф за производительность).

Примите Delegate, затем вызовите .DynamicInvoke().

Ответ 3

Я думаю, что это было бы довольно многословно в С#:/

Вот решение, которое может принимать функцию, которая возвращает или не возвращает значение и принимает 0 - 2 параметра:

    // signature: void MyFunc()
    void DelayedCall(Action sdf, float delayInSecs)
    {
    }

    // signature: SomeClass MyFunc()
    void DelayedCall<T>(Func<T> sdf, float delayInSecs)
    {
    }

    // signature: void MyFunc(SomeClass param1)
    void DelayedCall<T>(Action<T> sdf, float delayInSecs, T values)
    {
    }

    // signature: SomeClass MyFunc(SomeClass param1)
    void DelayedCall<T, K>(Func<T, K> sdf, float delayInSecs, T values)
    {
    }

    // signature: void MyFunc(SomeClass param1, SomeClass2 param2)
    void DelayedCall<T, K>(Action<T, K> sdf, float delayInSecs, T values, K values2)
    {
    }

    // signature: SomeClass MyFunc(SomeClass param1, SomeClass2 param2)
    void DelayedCall<T, K, L>(Func<T, K, L> sdf, float delayInSecs, T values, K values2)
    {
    }

Они принимают, например, следующее:

    void MyFunc1() {  }
    int MyFunc2() { return 6; }
    void MyFunc3(int someParam) { }
    string MyFunc4(string someParam) { return "";  }
    void MyFunc5(int someParam, string someparam2) { }
    string MyFunc6(string someParam, int someparam2) { return "";  }