Анонимный делегат как параметр функции

Я пытаюсь передать параметр, который является анонимным делегатом (без входных параметров, без возвращаемого значения).

Что-то вроде этого:

private function DoSomething(delegate cmd)
{
     cmd();
}

Затем я хочу использовать эту функцию для вызова функции следующим образом:

DoSomething(delegate
{
     Console.WriteLine("Hooah!");
});

Мне нужен этот конкретный способ, потому что он прост в использовании стиль написания.

Возможные?

Ответ 1

Именно для таких целей Microsoft создала Действие и Func оболочки в среде .NET. Оба класса полагаются на анонимные функции. Используйте действие, если вам не нужно возвращать какой-либо результат, просто для выполнения анонимной функции:

private void DoSomething(Action action)
{
    action();
}

Его можно использовать следующим образом:

DoSomething(() => 
{
    Console.WriteLine("test");
});

Термин () => является выражением лямбда и означает что-то вроде input with no parameters is calling .... Подробную информацию см. В документации.

Если вы хотите вернуть результат, используйте делегат Func:

private T DoSomething<T>(Func<T> actionWithResult)
{
    return actionWithResult();
}

Использование:

Console.WriteLine(DoSomething<int>(() => 
{
    return 100;
}));

Обе оболочки имеют переопределения, которые принимают до 8 параметров.

При использовании Func последним параметром всегда является тип возврата:

// returns a string
Func<string> t = () => { return "test string"; };
// first parameter is of type int, result of type string
Func<int, string> toString = (id) => { return id.ToString(); };
// parameters are of type int and float, result is string
Func<int, float, string> sumToString = (n1, n2) => { return (n1 + n2).ToString(); };

Оболочка Func может использоваться непосредственно с типизированным параметром:

Func<string, string> up = text => text.ToUpper();
Console.WriteLine(up("test"));

Я часто использую Func, чтобы создать общий исполнитель, который завернут в блок try/catch и регистрируется, если что-то произойдет. Таким образом я уменьшаю повторяющийся код:

private T safeCallWithLog<T>(Func<T> action)
{
    try
    {           
        return action();
    }
    catch (Exception ex)
    {
        Console.WriteLine(String.Format("Oops ...: {0}", ex.Message));
    }
    // return default type if an error occured
    return default(T);
}

Использование:

var result = safeCallWithLog<DbEntry>(() =>
            {
                return databaseContext.GetEntryWithId(42);
            });

var id = safeCallWithLog<int>(() =>
                    {
                        return databaseContext.GetIdFor("J.D.");
                    }); 

Вы все равно можете использовать оригинальную концепцию делегата . Классы Action и Func представляют собой только обертки вокруг предопределенных общих методов делегатов.

// declare delegate contract
private delegate void output();
// create caller method
private void method(output fun)
{
    fun();
}
// some test functions, that must match exactly the delegate description
// return type and number of arguments
private void test1()
{
    Console.WriteLine("1");
}

private void test2()
{
    Console.WriteLine(DateTime.Now.ToString());
}

// call different methods
method(test1);
method(test2);
// inline call without hard coded method
method(delegate() 
{
    Console.WriteLine("inline");
});

Ответ 2

У .NET есть куча встроенных. Action - это тот, который вы хотите без параметров и типов возврата:

private function DoSomething(Action cmd)
{
    cmd();
}

Также существует общая версия Action, если вы хотите, чтобы делегат имел параметры, но не возвращал типы (например, Action<int, int> для метода, который принимает два int и не имеет возврата).

Func и Predicate (вместе с родовыми версиями для них тоже).

Ответ 3

Конечно, это возможно. Для метода без типа возврата используйте Action иначе Func<>.

public void Function(Action action)
{
    action();
}

и назовите его как

Function(() => System.Console.WriteLine("test"));

Еще приятнее использовать lambdas вместо ключевого слова delegate. Вы даже можете выполнить действие с помощью action.Invoke(), но, на мой взгляд, лучше назвать его как метод, который он на самом деле.

Ответ 4

Однако Pasty описывает наиболее возможные решения, я просто хочу добавить о делете ключевого слова.

  • Первый случай - объявить новый тип:

В этом примере он используется для описания входного параметра

private function DoSomething(delegate cmd)
{ 
     cmd();
}

Но его можно использовать для объявления типа объекта, который используется для указания указателя на функцию:

public delegate *returnParameterType* NewTypeName(*inputParamType1* inputParam1, ...)

а затем использовал это NewTypeName как тип входного параметра:

private function DoSomething(NewTypeName cmd)
    { 
         cmd();  
    }
  • Второй случай ключевого слова usinf "делегат" как в вашем примере - объявить анонимный метод

    делегат() {    Console.WriteLine( "Hooah!" ); }


Однако в такой ситуации такой метод должен быть назначен или подходящему определенному делегату либо для общего делегата, например Action, потому что у Action не должно быть выходных параметров

private void delegate Output();
Output func = delegate(){Console.WriteLine("Hooah!");}

или

Action func1 = delegate(){Console.WriteLine("Hooah!");}