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

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

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

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

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

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

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

Возможный?

4 ответа

Решение

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

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

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

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

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

Func а также Predicate (наряду с общими версиями для них тоже) также существуют.

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

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

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

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

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

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

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

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

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


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

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

или же

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

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

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

и называть это как

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

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

Другие вопросы по тегам