Это, конечно, интересный момент, но по беглому изучению кода выглядит так, что entries при добавлении новых элементов может лишь увеличиваться в размере, а перекладка элементов в новый массив происходит без смешивания, через Array.Copy, поэтому даже старый индекс будет валиден в случае нового массива, вопрос остаётся открытым…
Обменяться-то они могут, но по хэш-коду вычисляется номер «корзины» (связного списка), а поиск в списке уже идёт по строгой эквивалентности ключа, поэтому в худшем случае элемент может не найтись, хотя он в словаре присутствует (и-то мне видится это крайне маловероятным событием, если вообще возможным).
Заглядывал когда-то давно. Конечно, могу ошибаться, но на вскидку такое маловероятно, поскольку если не найдено соответствия по ключу, то с чего бы возвращать дугое значение. Теоретически, может быть такое, что ключ уже попал в словарь, а значение пока ещё не присвоилось, но тогда хотя бы дефолтный null вернуться должен.
Я прямо говорю, что мной выбрано такое решение в целях эксперимента и обсуждения, поскольку есть подозрение, что оно рабочее.
lock и Dictionary уже готовые, поэтому использую их по максимуму. :)
За надёжность не ручаюсь, но выглядит работоспособно, мне было бы интересно словить ошибку в такой комбинации, если она возможна.
Для большей уверенности, конечно, можно использовать ConcurrentDictionary, но его производительность я не измерял собственноручно.
Внешняя ссылка на экземпляр класса RipeType может появится только после выполнения конструктора, в каком бы потоке мы ни выполняли оператор new. Исключение составляет лишь случай вроде
class AnyClass
{
public static AnyClass Instance;
public AnyClass()
{
Instance = this;
/* ... */
}
}
Но это не наша ситуация, поэтому вариант с недоинициализированным RipeType отпадает.
По логике вещей, при чтении структура словаря не может быть нарушена, даже если оно идёт из разных потоков. При параллельной записи тоже, поскольку есть lock. Остаётся лишь случай чтения в момент записи… Мне думается, что словарь не бросит исключение от такого, а если вдруг чтение произошло до момента вставки только что созданного экземпляра и вернулся null, то мы направляемся в lock и дожидаемся завершения вставки, после чего повторяем чтение и получаем уже созданный экземпляр.
Насколько понял из беглого ознакомления с type traits, идеи в основе схожие, но раньше мне не попадалось подобного рода оптимизаций на C#, разве что кэширование в переменную встречал (вместо многократного повторения вызова typeof(T).GetSomething()).
Статический же TypeOf позволяет несколько обобщить подход, например, реализовать быстрый доступ к информации о типе из разных частей приложения, что довольно удобно, на мой взгляд.
Можно и ConcurrentDictionary использовать, если нужно.
По началу у меня самого были подозрения насчёт такого решения, но при более детальном анализе я пришёл к выводу, что оно довольно безопасное. Буду признателен, если вы всё же укажете на возможный сценарий, приводящий к ошибке… Мне самому интересно о нём узнать, если он существует.
Да и завязываться на то что компилятор закеширует передаваемый в Lock.Invoke делегат тоже не стоит...
Если в разных потоках создать два делегата от одного метода, то они будут равны, поэтому lock сработает корректно.
Вот еще заметил. Судя по бенчмарку, GetRipeType всегда медленнее чем простой GetType. Так зачем оно нужно?
Зависит от сценария использования. Если получается единожды закэшировать информацию о типе объекта, то потом быстрее её брать из RipeType, чем из Type
static RipeType AnyRipeType = anyObject.GetRipeType();
static void AnyPerformanceCriticalMethod()
{
/* ... using of AnyRipeType ... */
}
В каждом конкретном случае нужно выбирать более оптимальное решение, чтобы достичь максимальной производительности, поскольку есть различия даже на разных CLR.
Нет, я не считаю это лучшим синтаксисом, он просто доступен мне уже сейчас в качестве альтернативы. Я только считаю, что is var / case var реализованы некрасиво без явной на то необходимости, вот и всё.
Когда у вас этот матчинг воткнут внутрь какого-нибудь актора, который должен очень быстро обрабатывать сообщения, и матч-кейсов много — да, это кошмар.
Тут уже возникает вопрос, а стоит ли вообще применять стандартный матчинг в swich с его подводными упаковками…
А если у вас несколько матч-кейсов?
Не понял вас, то одним делегатом нужно, то уже двумя.
Я прямо говорю, что мной выбрано такое решение в целях эксперимента и обсуждения, поскольку есть подозрение, что оно рабочее.
С непривычки да, но как бы должно работать, поскольку компилятор обеспечивает однозначный и потокобезопасный контекст блокировки.
За надёжность не ручаюсь, но выглядит работоспособно, мне было бы интересно словить ошибку в такой комбинации, если она возможна.
Для большей уверенности, конечно, можно использовать ConcurrentDictionary, но его производительность я не измерял собственноручно.
Но это не наша ситуация, поэтому вариант с недоинициализированным RipeType отпадает.
По логике вещей, при чтении структура словаря не может быть нарушена, даже если оно идёт из разных потоков. При параллельной записи тоже, поскольку есть lock. Остаётся лишь случай чтения в момент записи… Мне думается, что словарь не бросит исключение от такого, а если вдруг чтение произошло до момента вставки только что созданного экземпляра и вернулся null, то мы направляемся в lock и дожидаемся завершения вставки, после чего повторяем чтение и получаем уже созданный экземпляр.
typeof(T).GetSomething())
.Статический же TypeOf позволяет несколько обобщить подход, например, реализовать быстрый доступ к информации о типе из разных частей приложения, что довольно удобно, на мой взгляд.
ConcurrentDictionary
использовать, если нужно.По началу у меня самого были подозрения насчёт такого решения, но при более детальном анализе я пришёл к выводу, что оно довольно безопасное. Буду признателен, если вы всё же укажете на возможный сценарий, приводящий к ошибке… Мне самому интересно о нём узнать, если он существует.
Если в разных потоках создать два делегата от одного метода, то они будут равны, поэтому
lock
сработает корректно.Зависит от сценария использования. Если получается единожды закэшировать информацию о типе объекта, то потом быстрее её брать из RipeType, чем из Type
В каждом конкретном случае нужно выбирать более оптимальное решение, чтобы достичь максимальной производительности, поскольку есть различия даже на разных CLR.
И да, я считаю, что
Выглядит сейчас получше, чем обычный switch.
и повторяйте все ваши манипуляции с ним…
case var
case object
Так себе аргумент с визуальной сложностью.
В-третьих, добавилось неочевидное поведение с null.
Не понял вас, то одним делегатом нужно, то уже двумя.