All streams
Search
Write a publication
Pull to refresh
56
0

Пользователь

Send message
Это, конечно, интересный момент, но по беглому изучению кода выглядит так, что 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.
Druu
И да, я считаю, что
public static double CalculateSquare(
    this Shape shape) => shape.Match
(
    (Line _) => 0,
    (Circle c) => Math.PI * c.Radius * c.Radius,
    (Rectangle r) => r.Width * r.Height,
    () => double.NaN
);

Выглядит сейчас получше, чем обычный switch.
Мой код соответствует моим понятиям о математической красоте.
А для меня это и есть то самое начало, за которое мне нравится программирование, поэтому к ней стремлюсь в любом языке.
Тогда уж вводите оператор правостороннего присваивания для полной математической красоты
var x = SomeFunction(); //pure
SomeFunction() to var x; //pure

и повторяйте все ваши манипуляции с ним…
default var
case var
case object
Так себе аргумент с визуальной сложностью.

Во-вторых, вывод типа никуда не делся.
В-третьих, добавилось неочевидное поведение с null.
Нет, я не считаю это лучшим синтаксисом, он просто доступен мне уже сейчас в качестве альтернативы. Я только считаю, что is var / case var реализованы некрасиво без явной на то необходимости, вот и всё.
Когда у вас этот матчинг воткнут внутрь какого-нибудь актора, который должен очень быстро обрабатывать сообщения, и матч-кейсов много — да, это кошмар.
Тут уже возникает вопрос, а стоит ли вообще применять стандартный матчинг в swich с его подводными упаковками…

А если у вас несколько матч-кейсов?
Не понял вас, то одним делегатом нужно, то уже двумя.
Подождите, где я убрал var x?

var x = SomeFunction(); //pure
Do(x);
Other(x);

полностью семантически эквивалентно

Do(SomeFunction());
Other(SomeFunction());

Information

Rating
Does not participate
Registered
Activity