Как стать автором
Обновить
29
Карма
0
Рейтинг

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

Сахарные инжекции в C#

Ради интереса замерил разницу в скороти чтения и записи на реализациях INotifyPropertyChanged с лямбда-выражениями и без них.

Получившиеся результаты:
По скороти доступа (get) классическая реализация выигрывает примерно в 100 раз
По записи (set) классическая лучше примерно в 20 раз

На результаты немного также влияет упаковка-распаковка численных значений. В лямбда-реализациях числа хранятся в виде объектов (упакованы), поэтому сразу может показаться, что это хуже. Однако если вдуматься, UI-привязки работают через рефлексию, поэтому, скорее всего, в классических реализациях упаковка происходит неявно. Тут зависит от конкретного случая, где и как используется свойство чаще, в коде вью-модели или на UI.

Разница в 100 и 20 раз выглядит внушительно, но на деле это оказываются наносекунды (скорость записи, более медленная операция, у меня получилась около 2нс для лямбд, но зависит от производительности устройства). Частота же обносвления экрана в 100 Гц эквивалентна10 мс, поэтому в реальных приложениях на FPS это оказывает ничтожное влияние, если вообще оказывает. Экономятся лишь такты процессора.

В плане красоты разработки и гибкости лямбда-выражения выигрывают, а действительно значимого падения производительности никак не происходит в большинстве обычных случаев, поэтому боятся их уж точно не стоит, это факт.

Сахарные инжекции в C#

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

persons.OrderBy(p=>p.Age).Skip(100).Take(10)

Вообще же в Linq предусмотрены методы на многие случаи из жизни, поэтому прежде чем использовать свои лучше заглянуть в документацию. Хотя свои методы делать тоже полезно для развития мышления, сразу дойти до оптимального решения обычно трудно, а многое уже придумано до нас.

Сахарные инжекции в C#

Всё же решил убрать это утверждение из статьи, поскольку оно спорное.
Не мной оно было придумано, но, к большому сожалению, не удалось найти тот первоисточник, где это изначально встретил.

Сахарные инжекции в C#

Конечно, сложно судить о производительности и затратах памяти ваших методов, не имея перед глазами их полной реализации. Возможно, вы применяете какой-то хитрый алгоритм оптимизированный для работы с вещественными числами, большими или маленькими коллекциями — всякое допустимо.

Но по опыту могу сказать, что зачастую безопаснее использовать стандартные механизмы. Хотя раньше сам решал подобные задачи в лоб, не подозревая даже, что для этого годятся OrderBy, Take и другие методы из этой серии :)

Сахарные инжекции в C#

Пример:
            EventHandler handler1 = (sender, eventArgs) => Console.WriteLine("1");
            var handler2 = handler1;
            handler2 += (sender, eventArgs) => Console.WriteLine("2");

            handler1(null, EventArgs.Empty); // out => 1
            Console.ReadKey();

            handler2(null, EventArgs.Empty); // out => 1 , 2
            Console.ReadKey();

Сахарные инжекции в C#

Сразу прошу извинить меня, если несу чушь, но…
Насколько мне известно, переменные делегатов являются неизменяемыми типами (immutable types), как структуры, то есть во время присваиваивания создаётся новая копия данных, а не просто присваивается ссылка. Вот не знаю только, происходит ли это атомарно.

MSDN
Структуры копируются при присваивании. При присваивании структуры к новой переменной выполняется копирование всех данных, а любое изменение новой копии не влияет на данные в исходной копии. Это важно помнить при работе с коллекциями типов значений, такими как Dictionary<string, myStruct>.

Сахарные инжекции в C#

Мне нравится полиморфизм и вариант с индексатором :)
Стандартных подсказок достаточно, чтобы ни в чём не запутаться. Зато вью-модели получаются такими, что любо глянуть.

Исходный код библиотеки полностью открыт, поэтому при желании можно реализовать всё на свой вкус!

Сахарные инжекции в C#

Долго искал, но, к сожалению, не нашёл того комментария, где когда-то читал о событиях и сборщике мусора. Или мне приснилось?

В общем, проблема, потенциально, может возникнуть в том случае, если операция копирования значения в переменную выполняется не атомарно (var handler = Handler) и поток прервётся не докопировав всё как положено, а за это время кто-то успеет отписаться и вызвать сборщик мусора. После этого управление вернётся исходному потоку и копирование завершиться, но исходный объект будет уничтожен сборщиком мусора, что, очевидно, приведёт к исключению.

Тут всё зависит от диспетчеризации потоков, поэтому вручную такое весьма сложно воспроизвести. И если действительно проблема существует, то она крайне трудноуловима.

Сахарные инжекции в C#

Если использовать стандартные средства, то ваш методы, насколько понимаю, эквивалентены таким конструкциям

 persons.OrderBy(p=>p.Age).First()
 persons.OrderBy(p=>p.Age).Take(10)

Сахарные инжекции в C#

Я ожидаю, что в хорошей литературе тут же будет дан оригинальный английский термин, как в википедии

В том-то и дело, что новый термин может встретиться в любом месте, например, на форуме, и человеку захочется с ним разобраться. Первым делом он начнёт поиски с наиболее предсказуемых вариантов перевода.

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

Например, оригинальное название статьи с MSDN — Declarators and Variable Declarations. Конечно, русский язык богат, и можно подобрать множество синонимов к слову Declaration, но всё-таки, на мой взгляд, хорошо, когда различные термины более-менее универсальны и созвучны на разных языках. Это моё личное мнение, которого придерживаюсь при написании статей.

Сахарные инжекции в C#

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

А какой смысл подписываться на свое событие? Это оверхед на ровном месте, разве нет?
Не совсем понимаю, что вы имеете в виду.

Конструкция
this[() => Name].PropertyChanged += (o, e) => { // do somethig };

эквивалентна
this.PropertyChanged += (o, e) =>
{
    if (e.PropertyName != "Name") return;
    // do something
};

Смысл в том, чтобы избавиться от if-оператора и получить более компактную запись. На практике порой хватает одной-двух строк кода вмето, как минимум, четырёх (при условии стандартного форматирования).

Сахарные инжекции в C#

Паттерн IDataErrorInfo работает через индексатор и это выглядит довольно красиво. На этой почве возникла идея неявных индексных свойств, о которых можно прочитать в документации к библиотеке, где ключом так же является строковое имя свойства (полиморфизм в действии). После этого очень логичным шагом показалась перегрузка индексатора для возможности подписки на изменение значений свойств (INotifyPropertyChanged). Кроме того существует перегрузка индексатора и для команд (ICommand).

На мой взгляд, всё это выглядит очень эстетично и стройно. Во-первых, модификатор this часто опускается и его редко видно, а тут ему нашлось интересное применение, во-вторых же, синтаксис подсказывает, что операция производится с текущим объектом.

если модель составная и описывает список вложенных моделей, то новый индексатор будет семантически конфликтовать с существующим

Не совсем понял, что вы имеете здесь в виду.

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

В текущей реализации для каждого отслеживаемого свойства создаётся отдельный обработчик как снаружи, так и изнутри.

Сахарные инжекции в C#

Будем реалистами :) все эти задержки в большинстве случаев настолько ничтожны, что преимущества от их испольования затмевают собой недостатки. Тем более ничто не запрещает комбинировать разные подходы между собой, где это на самом деле нужно.

А вот подписка на событие с помощью лямды, это почти гарантированная утечка памяти!

Поподробнее, что вы имеете в виду? Да, как и любые другие подписки они могут привести к утечкам памяти.

viewModel[() => viewModel.Name].PropertyChanged += (o, e) => { // do somethig };

this[() => Name].PropertyChanged += (o, e) => { // do somethig };

Соглашусь, что с первым вариантом внешней подписки нужно быть внимательным, но второй замкнутый вполне безопасен.

Сахарные инжекции в C#

По этой теме можно зачитаться материалами и комментариями в статье Потокобезопасные события в C# или Джон Скит против Джеффри Рихтера.

Вызов обработчика у отписавшегося объекта воспроизводится легко (например, достаточно поставить вызов Thread.Sleep() перед handler(o, e)). Экспериментально установлено, что var handler = Handler удерживает оба объекта от сборки мусора даже если других ссылок на объекты не осталось, но, теоретически, как мне думается, операция handler = Handler может произойти не атомарно, и если в этот короткий промежуток времени произойдёт отписка и сборка мусора, то в дальнейшем получится исключение. К сожалению, такое воспроизвести сложно.

Возможно, конечно, и ошибаюсь в чём-то :)

Сахарные инжекции в C#

Да, исходные коды, конечно, есть. Ссылка в окончании статьи. На всякий случай продублирую тут: Aero Framework.

Сахарные инжекции в C#

Спасибо, хорошая рекомендация. Только она подойдёт лишь для Desktop-версии библиотеки. В Portable-варианте перечисление MethodImplOptions включает только два флага NoInlining и NoOptimization. Возможно, компилятор самостоятельно производит Inlining, где это нужно.

Сахарные инжекции в C#

Сделаю ещё небольшое пояснение к сказанному.

Например, начинающий разработчик встретился с понятием внедрение зависимостей и хочет найти английскую литературу по этому вопросу. Для этого нужно сформулировать соответствующий запрос. Но, например, гугл предлагает следующие переводы для слова внедрение: introduction, implantation, plantation, inculcation, intrusion, intercalation, infiltration.

Среди них даже не встречается injection, которое состоит в изначальном понятии Dependency Injections, поэтому в переводах технических терминов иногда проще не уходить далеко от оригинала. Да и это помогает легче запоминать иностранные слова.

Сахарные инжекции в C#

Стараюсь внимательно относиться к терминам.

Инжекции — это инженерный термин, близким переводом которого с английского является внедрение. О смысловых оттенках можно спорить долго, но существует такое понятие как инжектор — механизм или приспособление для внедрения чего-либо. В технической области это слово чаще употребляется, чем, скажем, инъектор или внедритель и звучит благозвучно, а также наиболее созвучно с английским написанием, поэтому выбор пал именно на понятие инжекции.

Декларировать, декларация чего-либо достаточно часто встречаются в документации для разработчиков.

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

Спасибо, что следите за чистотой языка, это тоже важно!

Сахарные инжекции в C#

Смутно припоминаю, что у меня как раз и была ситуация, когда обычный operation.Result не работал, поэтому пришлось создавать новый таск. Хотя в большинстве случаев хватит первого варианта, второй также стоит взять на заметку.

Сахарные инжекции в C#

Интересная ссылка про монады. Правда, непривычный синтаксис иногда получается, хотя в некотрых случаях выглядит красивее обычного.

Ох, перемудрил немного с Await, да, достаточно operation.Result :) Спасибо, что исправили!

Информация

В рейтинге
5,800-й
Зарегистрирован
Активность