В вашем первом случае единственное преимущество, которое вы получаете, используя методы лямбда-выражения / анонимные по сравнению с методом, состоит в том, что вы избегаете приведения между object
и типом, который вы хотите использовать;если вы в большой степени полагаетесь на типы значений, то вы будете подвергаться боксу каждый раз, когда вызывается этот метод (с параметром типа object
).
Обычно это не имеет большого значения, но если вашИмея дело с действительно большим количеством обратного вызова, он может начать оказывать влияние.
Лямбды (которые не Expression<T>
) / анонимные методы и делегат, который указывает на метод, не являютсяразные;компилятор C # компилирует лямбда / анонимную функцию в метод класса, который ваш код не видит, а затем подключает делегата до , .
страницы MSDN, которую выссылка на состояния (выделено мной):
лямбда-выражения заменяют анонимные методы как предпочтительный способ написания встроенного кода
, поскольку ониупомянуть «встроенный код», лямбда-выражения обеспечивают наиболее сжатый способ написания кода;поскольку он встроенный, большинство, естественно, хотело бы иметь максимально краткое представление.В конце концов, что более читабельно, это:
var query = myEnumerable.Where(x => x > 2);
Или это?
var query = myEnumerable.Where(delegate(x) { return x > 2; });
Во втором случае это больше, чем в первом, для то же самое .Кроме того, если сигнатура метода, которая принимала делегата, когда-либо изменялась с делегата на Expression<T>
(предположительно, для некоторого анализа лямбды), то код, вызывающий эту лямбду, все еще компилировать, в то время как код, который использует делегат или анонимную функцию, будет не .
Обратите внимание, что для лямбда / анонимного генерируется только один один метод .делегировать.Он не создает несколько методов в зависимости от того, где объявлена переменная.В итоге компилятор создает метод с более широкой областью действия .
Используя ваш (модифицированный) пример:
public static void TestMethod()
{
// Scope of the lambda starts here.
for (int i = 1; i <= 12; i++)
{
// Case A
ThreadPool.QueueUserWorkItem((state) => {
Console.WriteLine(i);
});
}
// And ends here.
}
Метод, который создает компилятор, будетзакройте над циклом, чтобы вы могли получить повторные значения для i
(в зависимости от того, когда ThreadPool
принимает вызов).
Однако, когда вы присваиваете переменную внутри цикла, компилятор достаточно умен, чтобы знать, что ему нужен только код внутри , например:
public static void TestMethod()
{
for (int i = 1; i <= 12; i++)
{
// Scope of the lambda starts here.
// Create copy.
int copy = i;
// Case A
ThreadPool.QueueUserWorkItem((state) => {
Console.WriteLine(copy);
});
// And ends here.
}
}
В приведенном выше примере каждый обратный вызов ThreadPool
выведет другое значение.
Следует отметить, что в C # 5.0, это явное изменение в поведении для оператора foreach
, но не для оператора for
.
Многие разработчики не понимают влияние замыкания на цикл, что является основной причиной изменения.