С точки зрения памяти первый способ будет вызывать распределение каждый раз, а второй будет использовать объект кешированного делегата, поскольку он не фиксирует никаких переменных. Компилятор обрабатывает генерацию кэшированного делегата. Нет никакой разницы в первом случае для емкости, равной нулю, поскольку конструктор по умолчанию для List<T>
использует пустой инициализационный массив, то же, что и явная емкость 0.
Что касается инструкций по исполнению, то они являются такими же, когда ключ найден, поскольку второй аргумент не используется. Если ключ не найден, первый способ просто должен прочитать локальную переменную, а второй способ будет иметь слой косвенности для вызова делегата. Кроме того, looking into the source code, похоже, что GetOrAdd с фабрикой сделает дополнительный поиск (через TryGetValue), чтобы избежать вызова фабрики. Делегат также может быть выполнен несколько раз. GetOrAdd просто гарантирует, что вы видите одну запись в словаре, а не то, что фабрика вызывается только один раз.
Таким образом, первый способ может быть более результативным, если ключ обычно не найден, поскольку распределение должно произойти в любом случае, и нет никакой передачи через делегата. Однако, если ключ обычно найден, второй способ более эффективен, поскольку меньше распределений. Для реализации в кеше вы обычно ожидаете, что будет много хитов, поэтому, если это так, я бы порекомендовал второй способ. На практике разница между ними зависит от того, насколько чувствительно общее приложение к распределению в этом коде.
Также любая реализация, использующая это, скорее всего, должна реализовать блокировку вокруг List<T>
, которая возвращается, поскольку она не является потокобезопасной.
В качестве примера я использовал «Список». Фактическая коллекция на своем месте является потокобезопасной. Кроме того, принимая предложение Иосифа, делает '() => Enumerable.Empty ()' помощь с производительностью? –
Hele
Кроме того, он зависит от того, какая версия рамки используется (и было ли реализовано новое поведение), как это было в .NET 4.5, где изменения имели некоторые нежелательные побочные эффекты. Обратитесь к https://basildoncoder.com/blog/concurrentdictionary-getoradd-vs.html – Alex
@Hele Ну, если вы собираетесь использовать 'Enumerable.Empty()', то делегат не нужен.'Enumerable.Empty ()' просто читает статическое поле readonly. Однако я не уверен, почему вы используете интерфейс только для чтения, например 'IEnumerable '. Я подозреваю, что это сделает обновления сложнее, но мне нужно будет увидеть больше кода. –