0

У меня есть ConcurrentDictionary<Guid, Foo> _dic = new();

Класс Foo внутри себя имеет несколько полей. Мне нужно только одно поле.

В плане реализации - можно использовать _dic.Values.Select(x => x.needProperty).ToList(), НО сама property Values под капотом создаёт новый List. Как-нибудь можно без двойного преобразования к List получить лист нужных property из класса Foo? Нужно получить только одно конкретное поле needProperty.

С помощью каста тоже не получится, т.к. IEnumerable к IReadOnlyList не кастится ((IReadOnlyCollection)_dic.Values.Select(x => x.needProperty) - будет ошибка)

https://github.com/microsoft/referencesource/blob/master/mscorlib/system/collections/Concurrent/ConcurrentDictionary.cs введите сюда описание изображения

https://source.dot.net/#System.Linq/System/Linq/Where.SpeedOpt.cs,6e33b46aba3510c8 введите сюда описание изображения

Frehzy
  • 1,316
  • Пробегитесь в цикле по словарю и отфильтруйте. Заодно и лишнего лока не будет. – vitidev Feb 14 '22 at 08:17
  • @vitidev его и так не будет, если брать его собственный энумератор, а не коллекцию значений. – aepot Feb 14 '22 at 08:39

1 Answers1

2

У вас разработка под .NET Framework 4.x? Если нет, то вы не в те исходники смотрите. Для .NET 6 исходники можно посмотреть тут или тут.

_dic.Select(x => x.Value.needProperty).ToArray()

Не используйте списки там где не требуется модификация коллекции после создания. Дополнительно убедитесь в том, что словарь не изменяется из другого потока во время чтения, а то может получиться так, что пока вы его читаете, либо успеете подхватить изменения, либо нет, как повезет.

В плане потокобезопасности, лучше использовать _dic.Values, оно снимет снапшот словаря на момент запроса, залочив его при формировании списка.

aepot
  • 49,560
  • Этот список нужно только для чтения, поэтому он как раз таки ReadOnly. – Frehzy Feb 14 '22 at 08:53
  • Разве использование ToArray() не создаёт под капотом ещё одного массива/коллекции? Я как раз таки хочу избежать двойного создания. Есть уже идея - создать ещё одну коллекцию, в которую добавлять тот же элемент, но уже конкретно needProperty (с таким же guid). Что-то вроде ConcurrentDictionary<Guid, needProperty>. И Tryremove из обоих словарей сразу. Конечно, выглядит это не очень, т.к. уже 2 поля используются, но вариантов другой реализации пока что не вижу – Frehzy Feb 14 '22 at 08:55
  • 1
    @Frehzy создает, точно так же как ToList() создает список, но массив создается быстрее и легче списка. Я вам показал, как исправить Linq запрос, чтобы было только одно создание, вы только ToArray увидели чтоли? Посмотрите в мой код еще раз, внимательнее. – aepot Feb 14 '22 at 09:20
  • 1
    Да, разобрался. Покопался в документации и всё встало на свои места. Спасибо – Frehzy Feb 14 '22 at 11:16
  • Я вот заглянул сейчас в код - удивительно, почему array работает быстрее. Ведь по факту, они всё равно сходятся по итогу к одному и тому же – Frehzy Feb 14 '22 at 11:38
  • 1
    @Frehzy потому что List, это обертка над массивом. А каждая обертка - это дополнительный код. Плюс вот это еще. – aepot Feb 14 '22 at 11:40
  • 1
    Теперь совершенно всё встало на свои места. Спасибо, ещё раз – Frehzy Feb 14 '22 at 11:48