Есть ли какой-то выигрыш в производительности с "цепочкой" операторов в.NET?
При получении значения кода поиска из таблицы некоторые люди делают это...
Dim dtLookupCode As New LookupCodeDataTable()
Dim taLookupCode AS New LookupCodeTableAdapter()
Dim strDescription As String
dtLookupCode = taLookupCode.GetDataByCodeAndValue("EmpStatus", "FULL")
strDescription = dtLookupCode.Item(0).Meaning
... тем не менее, я также видел вещи, которые были "прикованы", как это...
strDescription = taLookupCode.GetDataByCodeAndValue("EmpStatus", "FULL").Item(0).Meaning
... которая в первую очередь обходит наличие таблицы данных кода поиска, поскольку адаптер таблицы знает, как выглядит структура его набора результатов.
Сохраняет ли использование метода "цепочка" накладные расходы на создание объекта таблицы данных, или он все равно эффективно создается для правильной обработки оператора.Item(0).Meaning?
11 ответов
В отличие от "встроенной" части этого, два набора кода не будут компилироваться в одно и то же. Проблема приходит с:
Dim dtLookupCode As New LookupCodeDataTable()
Dim taLookupCode AS New LookupCodeTableAdapter()
В VB это создаст новые объекты с соответствующими именами ссылок. С последующим:
dtLookupCode = taLookupCode.GetDataByCodeAndValue("EmpStatus", "FULL")
Мы немедленно заменим оригинал dtLookupCode
ссылка на новый объект, который создает мусор для сбора (недоступный объект в оперативной памяти).
Следовательно, в точном, оригинальном сценарии то, что называется "встроенной" техникой, технически более эффективно. (Однако вряд ли вы физически увидите эту разницу в этом небольшом примере.)
Место, где код был бы по существу таким же, если исходный пример читается следующим образом:
Dim taLookupCode AS New LookupCodeTableAdapter
Dim dtLookupCode As LookupCodeDataTable
Dim strDescription As String
dtLookupCode = taLookupCode.GetDataByCodeAndValue("EmpStatus", "FULL")
strDescription = dtLookupCode.Item(0).Meaning
В этом мире у нас есть только существующие ссылки, и мы не создаем ненужные объекты. Я немного переупорядочил утверждения для удобства чтения, но суть та же. Кроме того, вы можете легко инициализировать ссылки одной строкой с помощью чего-то подобного и иметь ту же самую базовую идею:
Dim taLookupCode AS New LookupCodeTableAdapter
Dim dtLookupCode As LookupCodeDataTable = taLookupCode.GetDataByCodeAndValue("EmpStatus", "FULL")
Dim strDescription As String = dtLookupCode.Item(0).Meaning
Эти две строки сводятся к одному и тому же. Я бы выбрал тот, который легче читать. Под "подкладкой" обычно понимается что- то немного другое.
Обычно это просто делает код менее читабельным.
И часто, когда люди используют это "встраивание" (т. Е. Связывание), они повторно обращаются к свойству или полю класса несколько раз вместо того, чтобы получать его один раз и сохранять в локальной переменной. Как правило, это плохая идея, потому что обычно неизвестно, как возвращается это поле или свойство. Например, он может быть рассчитан каждый раз, или он может быть рассчитан один раз и сохранен в классе для частного использования.
Вот две иллюстрации. Следует избегать первого фрагмента:
if (ConfigurationManager.AppSettings("ConnectionString") == null)
{
throw new MissingConfigSettingException("ConnectionString");
}
string connectionString = ConfigurationManager.AppSettings("ConnectionString");
Второе предпочтительнее:
string connectionString = ConfigurationManager.AppSettings("ConnectionString")
if (connectionString == null)
{
throw new MissingConfigSettingException("ConnectionString");
}
Проблема здесь в том, что AppSettings() фактически должен распаковывать коллекцию AppSettings каждый раз, когда извлекается значение:
// Disassembled AppSettings member of ConfigurationManager
public static NameValueCollection AppSettings
{
get
{
object section = GetSection("appSettings");
if ((section == null) || !(section is NameValueCollection))
{
throw new
ConfigurationErrorsException(SR.GetString("Config_appsettings_declaration_invalid"));
}
return (NameValueCollection) section;
}
}
Да, не говорите "встроенный", потому что это означает что-то конкретное на других языках. Скорее всего, разница в производительности равна нулю или настолько мала, что не имеет значения, это просто вопрос предпочтений. Вы хотите записать его в отдельных выражениях, чтобы сделать его более понятным, или написать все это в одной строке, чтобы быстрее его напечатать?
Отладка последнего будет сложнее, если вы хотите увидеть промежуточное состояние и один шаг по этапам.
Я бы пошел на удобочитаемость по количеству экранов, используемых здесь, так как производительность - мойка.
Одной из причин "сцепления" является закон Деметры, который предполагает, что ваш код хрупок перед лицом изменений в LookupCodeDataTable.
Вы должны добавить такую функцию:
function getMeaning( lookupCode as LookupCodeDataTable)
getMeaning=lookupCode.Item(0).Meaning
end function
и назовите это так:
strDescription=getMeaning(taLookupCode.GetDataByCodeAndValue("EmpStatus", "FULL"))
Теперь getMeaning() доступен для вызова во многих других местах, и если LookupCodeDataTable изменится, вам нужно всего лишь изменить getMeaning(), чтобы исправить это.
Я называю это цепочкой.
Вы задаете неправильный вопрос.
Что вам нужно спросить: что является более читабельным?
Если создание цепочек делает код более легким для чтения и понимания, чем делать это.
Однако, если это запутывает, то не надо.
Любые оптимизации производительности не существуют. Не оптимизируйте код, оптимизируйте алгоритмы.
Таким образом, если вы собираетесь вызывать Item(1) и Item(2), то при объединении в цепочку вы будете снова и снова создавать один и тот же объект, что является плохим алгоритмом.
В этом случае первый вариант лучше, так как вам не нужно каждый раз заново создавать адаптер.
Помимо ремонтопригодности, есть еще одна причина избежать цепочки: проверка ошибок.
Да, вы можете заключить все это в try/catch и поймать все исключения, которые может выдать любая часть цепочки.
Но если вы хотите проверить результаты между вызовами без try/catch, вы должны разделить вещи на части. Например:
- Что происходит, когда GetDataByCodeAndValue возвращает ноль?
- Что если он вернет пустой список?
Вы не можете проверить эти значения без try/catch, если вы цепочки.
Это:
dtLookupCode = taLookupCode.GetDataByCodeAndValue("EmpStatus", "FULL")
strDescription = dtLookupCode.Item(0).Meaning
и это:
strDescription = taLookupCode.GetDataByCodeAndValue("EmpStatus", "FULL").Item(0).Meaning
полностью эквивалентны.
В первом примере у вас есть явная временная ссылка (dtLookupTable). Во втором примере временная ссылка неявная. За кулисами компилятор почти наверняка создаст один и тот же код для них обоих. Даже если он не генерирует тот же код, дополнительная временная ссылка очень дешевая.
Тем не менее, я не уверен, если эта строка:
Dim dtLookupCode As New LookupCodeDataTable()
является эффективным. Мне кажется, это создает новый LookupCodeDataTable
который затем отбрасывается при перезаписи переменной в последующем операторе. Я не программирую на VB, но я ожидаю, что эта строка должна быть:
Dim dtLookupCode As LookupCodeDataTable
Ссылка дешевая (вероятно бесплатная), но создание дополнительной справочной таблицы может не быть.
Это то же самое, если вам не нужно обращаться к возвращенным объектам с помощью taLookupCode.GetDataByCodeAndValue("EmpStatus", "FULL") или Item(0) несколько раз. В противном случае вы не знаете, является ли время выполнения этой функции log(n) или n, поэтому для лучшей ставки я бы назначил ссылку на нее.