Анонимный делегат как параметр функции
Я пытаюсь передать параметр, который является анонимным делегатом (без входных параметров, без возвращаемого значения), чтобы функционировать.
Что-то вроде этого:
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()
, но, по моему мнению, лучше называть это, как метод, который на самом деле.