Я прочитал ответ на вопрос о переполнении стека, который содержал следующий предложенный код:
Action<Exception> logAndEat = ex =>
{
// Log Error and eat it
};
try
{
// Call to a WebService
}
catch (SoapException ex)
{
logAndEat(ex);
}
catch (HttpException ex)
{
logAndEat(ex);
}
catch (WebException ex)
{
logAndEat(ex);
}
У меня вопрос: в чем преимущество (если оно есть) использования лямбда-выражения для LogAndEat, в отличие от (на мой взгляд, более простого и более очевидного) частного метода, который выглядит следующим образом:
private void LogAndEat(Exception ex)
{
// Log Error and eat it
}
Редактировать: Спасибо за ответы на этот вопрос, но просто более четко сформулирую мой фундаментальный вопрос: какой подход лучше / будет лучше Вы рекомендуете в этом случае? Лямбда-выражение или закрытый метод?
Спасибо всем за отличные ответы, за которые я проголосовал, но я решил обобщить их, чтобы попытаться отразить плюсы и минусы в одном ответе.
Плюсы использования лямбда-выражения (LE) вместо закрытого метода:
Минусы использования лямбда-выражения вместо приватного метода:
Существует также более субъективная проблема удобства сопровождения, и можно утверждать, что LE не так хорошо понимается большинством разработчиков, как частный метод, и поэтому его несколько сложнее поддерживать.Можно также утверждать, что LE повышает удобство сопровождения, поскольку он инкапсулирован в методе, в котором он вызывается, а не в частном методе, который виден всему классу.
Думаю, вы могли бы подумать, что лямбда-выражение в этом примере немного похоже на вложенные функции Паскаля в том смысле, что это код, который может быть только выполнен методом, в котором он объявлен.
Частный метод может быть вызван из любого метода в том же классе, в то время как лямбда, подобная этой, является локальной для текущего метода и поэтому явно используется только в этом контексте.
Это действительно единственное преимущество, о котором я могу думать, - ясность с точки зрения ожидаемого использования.
Переменные, захваченные logAndEat
, в противном случае были бы параметрами для метода LogAndEat
. Вы можете считать это формой карри.
LogAndEat может ссылаться на частные поля внутри функции, в которой он определен. Итак:
private bool caughtException;
Action<Exception> logAndEat = ex =>
{
caughtException = true;
};
try
{
// Call to a WebService
}
catch (SoapException ex)
{
logAndEat(ex);
}
catch (HttpException ex)
{
logAndEat(ex);
}
catch (WebException ex)
{
logAndEat(ex);
}
if (caughtException)
{
Console.Writeline("Ma, I caught an exception!");
}
Это банальный пример (!), Но потенциально это может быть намного аккуратнее, чем передача кучи параметров частному методу.
IMO Мне не нравятся маленькие приватные функции, которые используются только в другом приватном методе, бродящем по моим классам, я просто нахожу их уродливыми, вот почему я бы использовал лямбду в этом примере
Я не думаю, что использование лямбды вместо функции повлияет на производительность
.Многое зависит от личных предпочтений, никто не может сказать, что единственный способ - это правильный путь или неправильный путь.
Лямбда-выражения ведут себя как замыкания в других языках, но самое замечательное в них то, что они могут обращаться к переменным, привязанным к методу, в котором они объявлены. Это добавляет гибкости тому, что вы можете делать в своем коде, но дает ценой невозможности изменить код в этом методе во время отладки.
По этой причине, если вы собираетесь регистрировать ошибки, в какой-то момент вы можете оказаться в отладчике этого метода. Если вы используете лямбда-выражение, вы не сможете изменять какой-либо код во время выполнения, поэтому по этой причине я предпочитаю использовать отдельный частный метод, который принимает исключение в качестве своего параметра.
Чтобы понять, лучше ли использовать лямбда-выражение (как я сделал в исходном ответе ) или использовать «правильный» метод, нам нужно принять решение на основе о том, насколько многоразовым является код обработки общих ошибок.
Если общий код обработки ошибок полностью повторно используется другими функциями в разных классах приложения, то, вероятно, это должна быть внутренняя или публичная функция, которая может быть в другом классе, если это делает организацию кода более разумной.
Если общий код обработки ошибок может быть повторно использован другой функцией только в том же классе, то, вероятно, это должна быть частная функция в том же классе.
Если общий код обработки ошибок не может быть повторно использован какой-либо другой функцией и должен использоваться только этой конкретной функцией, тогда у вас есть два варианта. Первый - использовать делегат, чтобы инкапсулировать его в пределах функции, гарантируя, что функция не утекает каких-либо деталей реализации. Второй - использовать частную функцию в том же классе с комментарием, который должен вызываться только той функцией, для которой она предназначена.
В третьем случае не существует четкого «наилучшего способа» сделать это, поскольку оба являются совершенно законными способами достижения одной и той же цели. Я склонен использовать делегат, когда общий код невелик и / или вам нужно какое-то поведение делегатов, например, захват переменных.
Теперь, возвращаясь к вопросу, почему я написал код с использованием делегата? У меня не было информации о том, можно ли повторно использовать код обработки ошибок, поэтому я предположил, что это не так, и решил использовать делегата, поскольку я полагал, что людям будет легко перейти от этого к пониманию того, что это может быть `` правильный '' метод (как и у вас), тогда как если бы я показал использование «правильной» функции, люди могли бы не осознавать, что делегат был бы альтернативой.