Я пытался понять это довольно давно (читая онлайн-блоги и статьи), но пока безуспешно.
Что такое делегаты? Что такое лямбда-выражения? Преимущества и недостатки обоих? Возможная передовая практика использования одного или другого?
Спасибо заранее.
Я пытался понять это довольно давно (читая онлайн-блоги и статьи), но пока безуспешно.
Что такое делегаты? Что такое лямбда-выражения? Преимущества и недостатки обоих? Возможная передовая практика использования одного или другого?
Спасибо заранее.
Делегаты - это методы, которые можно использовать в качестве переменных, например, строки и т.д. Например, вы можете объявить метод делегата с одним аргументом:
delegate void OneArgumentDelegate(string argument);
Он ничего не делает, как интерфейс. Если у вас есть метод в любом классе с одним аргументом вроде этого:
void SomeMethod(string someArgument) {}
It соответствует подпись делегата и, следовательно, может быть назначена переменной его типа:
OneArgumentDelegate ThisIsAVariable = new OneArgumentDelegate(SomeMethod); OneArgumentDelegate ThisIsAlsoAVariable = SomeMethod; // Shorthand works too
Затем они могут передаваться в качестве аргументов методам и вызываться так:
void Main() { DoStuff(PrintString); } void PrintString(string text) { Console.WriteLine(text); } void DoStuff(OneArgumentDelegate action) { action("Hello!"); }
Это приведет к выходу Hello!
.
Лямбда-выражения являются сокращением для DoStuff(PrintString)
, поэтому вам не нужно создавать метод для каждой переменной-делегата, которую вы собираетесь использовать. Вы создаете "временный метод, который передается методу. Он работает следующим образом:
DoStuff(string text => Console.WriteLine(text)); // single line DoStuff(string text => // multi line { Console.WriteLine(text); Console.WriteLine(text); });
Лямбда-выражения - это всего лишь стенография, вы можете создать отдельный метод и передать его. Надеюсь, вы сейчас это понимаете лучше, -)
Делегат - это объект, который содержит ссылку на функцию. Несколько разных делегатов могут указывать на одну и ту же функцию. Тип делегата определяет след функции, на которую он указывает.
Лямбда-выражение - это функция, которая не имеет имени. Единственный способ выполнить эту функцию - иметь делегат, указывающий на функцию. Лямбда-выражения обычно определяются на месте, где вам нужен делегат для функции с заданным размером. Это полезно для того, чтобы сделать код менее подробным и в то же время более наглядным и гибким.
Я бы предположил, что вы используете именованную функцию и делегат для нее всякий раз, когда у вас есть код, который будет вызываться из разных мест. Общим примером является прослушиватель событий, который вы хотите присоединить к нескольким производителям событий.
Еще один момент, чтобы рассмотреть возможность написания отдельной функции - это сложность кода. Это не поможет никому, если вы напишете целую программу внутри выражения лямбда.
С другой стороны, вам часто требуется какая-то тривиальная обработка, которую вы хотите выполнить в обратном вызове. Это то место, где вам могут нравиться лямбда-выражения.
Что очень нравится в лямбда-выражениях, что они наследуют область, в которой они были определены, так что вы можете легко переносить переменные внутри выражения лямбда и, таким образом, передавать много информации внутри. Однако вы должны быть осторожны, см. Раздел "Примечания" эта статья.
Labdas великолепны в сочетании с LINQ.
В заключение я должен процитировать еще один обязательный раздел msdn:
Когда вы используете синтаксис на основе метода для вызова метода Where в классе Enumerable (как и в LINQ to Objects и LINQ to XML), параметр является типом делегирования System.Func. Выражение лямбда является наиболее удобным способом создания этого делегата. Когда вы вызываете тот же метод, например, в классе System.Linq.Queryable(как и в LINQ to SQL), тогда тип параметра является выражением System.Linq.Expressions.Expression, где Func - это любой делегат Func с шестнадцатью входные параметры. Опять же, лямбда-выражение является просто очень кратким способом построения этого дерева выражений. Лямбды позволяют видеть, что вызовы Where выглядят одинаково, хотя на самом деле тип объекта, созданный из лямбда, отличается.
Никто не упомянул анонимных делегатов. Вы можете создавать делегаты "на лету", не объявляя их:
public void Action(Func<int, int> func);
...
Action(delegate(int x) { return x*x; });
Это всего лишь более подробная версия синтаксиса лямбда:
Action(x => x*x);
Также обратите внимание, что синтаксис лямбда имеет более агрессивный тип вывода. Другое отличие состоит в том, что обозначение лямбда может использоваться для объявления деревьев выражений:
public void Action(Expression<Func<int, int>>);
Action(x => x*x);
В этом случае то, что вы получаете, не является функцией, а деревом синтаксического анализа, которое вы можете проверить во время выполнения. Вот как запросы linq строят свой sql, например.
изменить
Чтобы более точно ответить на вопрос о том, когда использовать тот или иной:
Вам редко нужно объявлять новый тип делегата самостоятельно, хотя иногда это полезно. Структура содержит несколько типов Func<>
, а также Action<T>
и Predicate<T>
, которые, как правило, все, что вам нужно.
При создании функции "на лету" нет преимущества использовать синтаксис анонимного делегата вместо синтаксиса лямбда. Поскольку синтаксис лямбда более краткий и тип-вывод, предпочитайте его.
Делегат - это просто указатель на функцию. Его просто как "переменная", где вы можете сохранить адрес в другой функции, которая будет называться
public class test {
Action<int> CallUserCode;
public test(Action<int> proc){
CallUserCode = proc;
}
void foo(){
int someValue = 0;
//do some stuff that needs to call the user procedure
CallUserCode(someValue);
}
}
Lambda Expressions тоже является делегатом, который упростил синтаксис и может "создавать" функции "inline". Таким образом, предыдущий пример будет вызываться с использованием lambda следующим образом.
void bar(){
var t = new test(x => { /* do something with the value i get from foo */});
t.foo(); //here function foo gets called, which will call 'do something' AND call my lambda expression
}
Существует одна важная разница в том, где мы можем использовать lamda, чем делегат.
private delegate int emptymethoddel();
// Delegate for method with no params and returns int
Соответствующий тип делегирования каркаса: Func<int>
Но вы не можете создать новый экземпляр делегата /func из параметризованного метода.
private int TwoArgMethod(int i, int j)
{
return i + j;
}
но с помощью lambda
вы можете получить делегат для вышеуказанного метода.
Func<int> retmethod = () => TwoArgMethod(10, 20);
но для экземпляра делегирования мы не можем сделать, как показано ниже
emptymethoddel retmethod4 = new emptymethoddel(TwoArgMethod(10,20));
// mismatch method signature
С lambda мы можем получить указатели на методы, которые не соответствуют "Func" или любым другим вариантам.
Как говорили другие, lambdas являются синтаксисом для создания делегатов встроенными и анонимными. Единственное, что вы можете сделать с лямбдами, которые невозможно с традиционными функциями, - это закрытие. Таким образом, вы можете создавать функции во время выполнения с информацией о времени выполнения:
string mystring = SomeObject.GetMyString();
AnotherObject.OnSomeEvent += (eventparams =>
{
string newstring = string.Format(eventparams.Message, mystring);
SomeService.PrintEvent(newstring);
}
Таким образом, mystring включается в делегат и может использоваться как переменная.