Коллеги возник вопрос. Решил немного расширить свою квалификацию и начать изучать C#, потому что видел это требование во многих вакансиях для программистов PLC. Прошёл базовый курс по языку от Microsoft , но дальше не знаю что делать. Все остальные найденные курсы в основном по web программированию, а это, как мне кажется, не сильно мне подходит. Может кто-нибудь посоветовать материалы на данную тему? Может есть какие-нибудь курсы/книги/видео? Спасибо 🙂
Всем привет! Знаю, таких постов уже было много, но попытаю удачу. Прошу помощи в поисках курсов и материалов по работе с Юнити. Планирую вкатываться с С++ (джун) в С#. Что касается Юнити, то больше интересует "техническая" сторона в плане:
Почему и зачем для каждого создается отдельный класс
Иерархия, что от чего наследуется (например канвас -> UI и тд)
Виды скриптов, куда их помещать, если хочу их использовать глобально (например нажатие той же кнопки Esc или Tab, если я хочу использовать их в любой сцене)
И другие подобные вещи. Поскольку совсем непонятны именно принципы работы с движком. Везде все не то - по типу "как заставить персонажа двигаться" и куча непонятного (как минимум в плане иерархии) кода, который работает, но почему - непонятно. Вряд ли без понимания далеко уеду, методом тыка не очень хочется учиться. Заранее всем спасибо!
Всем привет! Знаю, таких постов уже было много, но попытаю удачу. Прошу помощи в поисках курсов и материалов по работе с Юнити. Планирую вкатываться с С++ (джун) в С#. Что касается Юнити, то больше интересует "техническая" сторона в плане:
Представьте, что вы разрабатываете библиотеку, которой будут пользоваться тысячи людей 😮. Чтобы убедиться в стабильности — нужно всё хорошенько покрыть тестами. Все мы любим инкапсуляцию, верно (я надеюсь)? Поэтому мы не разрешаем использовать всё подряд из нашей сборки, а с умом используем модификаторы доступа и позволяем использовать только public классы и методы.
В C#, есть 7 модификаторов доступа, основные:
- private — доступ только внутри текущего класса - protected — доступ внутри текущего и дочерних классов - public — классы и методы доступны где угодно, также из сборок, использующих текущую - internal — публичный API, внутри текущей сборки. Как public, но нет доступа из сборок использующих текущую - остальные можно посмотреть тут
Но, C# — не JavaScript, и для тестов создаётся отдельная сборка, а internal методы в ней не доступны.
Чтобы тестировать internal функциональность, нужно использовать атрибут InternalsVisibleToAttribute, и в качестве параметра указать имя тестовой сборки. Тогда все internal методы и классы будут доступны для тестирования.
Одним из самых популярных на собеседованиях C# разработчиков является вопрос о работе сборщика мусора в .NET. Мне также приходилось рассказывать о нём интервьюерам на моих собесах. Сегодня я хотел бы кратко рассказать о сборщике мусора, заодно и сам вспомню.
🔍 Зачем нужен сборщик мусора? Сборщик мусора автоматически обеспечивает освобождение памяти, занятой объектами, которые больше не используются. Такой подход предотвращает утечки памяти и снимает с разработчика бремя ручного освобождения памяти (привет С++), что позволяет сосредотачиваться на более важных аспектах программирования.
⏰ Когда вызывается сборщик мусора? Сборщик мусора активируется автоматически, например, когда одно из поколений заполнено (о поколениях позже). Если вам требуется инициировать сборку мусора вручную, используйте GC.Collect().
🔗 Как определяется, какие объекты больше не используются? Сборщик мусора опирается на концепцию "GC Roots" (корни сборки мусора):
1. Локальные переменные: Объекты, на которые существуют ссылки в локальных переменных. 2. Статические переменные: Объекты, на которые ссылаются статические переменные классов. Эти корни живут в течение всего времени выполнения приложения. 3. Активные элементы стека: Если во время выполнения функции происходит сборка мусора, то локальная переменная внутри данной функции не будет удалена сборщиком мусора. Такая переменная считается активным корнем, до тех пор пока кадр стека метода не будет разрушен.
Сборщик мусора создаёт граф (фаза маркировки), который содержит все объекты, достижимые из GC Roots. Объекты, на которые нет ссылок из GC Roots, считаются недостижимыми и готовыми для удаления.
🔄 Как работает сборщик мусора? Сборщик мусора использует концепцию поколений (поколения 0, 1 и 2) для эффективной работы с объектами различной "старости".
- При создании объекты помещаются в поколение 0. Если объект слишком большой (по умолчанию объекты размером больше 85 000 байт), то он будет помещён в Large Object Heap (очищается вместе с поколением 2). - Когда поколение 0 заполнено — запускается сборка мусора. Неиспользуемые объекты удаляются (недостижимые из GC Roots), оставшиеся перемещаются в поколение 1. - Аналогично происходит сборка мусора, когда заполнено поколение 1. Все выжившие объекты перемещаются в поколение 2. Затем происходит сборка мусора в поколении 0. - Когда заполнено поколение 2, происходит полная сборка мусора. Сперва очищается поколение 2, а затем 1 и 0. Если после этого недостаточно места для новых объектов — происходит исключение OutOfMemory. - В самом конце происходит фаза сжатия, в которой сборщик мусора перемещает живые объекты так, чтобы они располагались в памяти непосредственно друг за другом.
🔄 Для чего нужно разделение на несколько поколений? - Сборка мусора поколения 0 выполняется чаще и занимается удалением объектов, которые быстро выходят из области видимости. - С увеличением поколения редкость запуска сборки позволяет более эффективно использовать ресурсы. Поколение 0 очищается чаще всего, поколение 2 очищается реже всего.
Для того, чтобы плагин загружался при запуске Revit - для него необходимо подготовить файл-манифест.
Файл-манифест - это XML-файл, сохраненный с расширением .addin и содержащий основную информацию о плагине - путь к решению, информацию о разработчике, общую справочную информацию и т.д.
Форму файла-манифеста можно найти на сайте Autodesk - например - по ссылке.
<AddIn Type="Application"> - Тип плагина - приложение. Тип "Command" - ссылка на внешнюю команду <Name> - имя приложения для использования внутри Revit <FullClassName> - Ссылка на класс Application в разрабатываемом проекте в формате Namespace.Application. Для внешней команды - ссылка должна указывать на класс Command <Text> - Название внешней команды. В случае добавления Application - поле исключается <Description> - Описание внешней команды во всплывающем сообщении. В случае добавления Application - поле исключается <VisibilityMode> - настройка видимости внешней команды. При добавлении Application - поле исключается <Assembly> - Полный путь к решению для приложения <AddInId> - Уникальный ID сборки. Можно получить в VisualStudio. Для этого необходимо дважды щелкнуть левой кнопкой на раздел Properties в обозревателе решений и затем нажать кнопку "Сведения о сборке"
Получаем GUID сборки
<VendorId> - Идентификатор разработчика ПО. Если разработчик внесен в перечень официальных разработчиков Autodesk - идентификатор будет отображаться при загрузке плагина, а плагин не будет требовать подтверждение о загрузке при каждом запуске Revit. <VendorDescription> - Описание разработчика ПО. Например ссылка на сайт или другие контакты разработчика
Пример заполненного файла-манифеста
После подготовки файла-манифеста, он размещается в одном из двух мест: ▻C:\ProgramData\Autodesk\Revit\Addins\ - центральная папка для размещения манифестов ▻C:\USERS\...\AppData\Roaming\Autodesk\Revit\Addins\ - локальная папка для манифестов у конкретного пользователя Манифест размещается в папке, соответствующей версии Revit.
Если Манифест был описан правильно - при запуске Revit появится сообщение с вопросом о подтверждении загрузки:
Подтверждение загрузки плагина
После подтверждения загрузки - будут созданы вкладки и панели, прописанные в классе Application. Плагин будет готов к работе
Группа RadioButtonGroup в общем плане представляет собой чек-бокс, в котором необходимо выбрать только один вариант из представленных (самый простой вариант - выбор Да/Нет)
Группа создается с помощью метода AddItem() элемента панели. В качестве аргумента принимает элементы RadioButtonGroupData. Элемент RadioButtonGroupData создается с помощью конструктора, который принимает одну строку в качестве аргумента - это внутреннее имя, которое будет использоваться при работе Revit:
RadioButtonGroup rbg = panel.AddItem(new RadioButtonGroupData("radioGroup")) as RadioButtonGroup;
Далее необходимо создать и добавить в группу кнопки. Создание кнопок для группы производится с помощью конструктора ToggleButtonData, который принимает в себя те же аргументы, что и обычная PushButton
ToggleButtonData tb1 = new ToggleButtondata("revitName","interfaceName","dllLocation","Namespace.ClassCommand")
Если группа используется в качестве чек-бокса и от кнопок не требуется выполнение каких-либо команд - при создании кнопок не указываются путь к исполнительному dll и ссылка на класс Command
ToggleButtonData tb1 = new ToggleButtondata("revitName","interfaceName")
Также для кнопки доступны все свойства, доступные для обычной PushButton
Добавление кнопки в группу производится с помощью метода Additem группы:
rbg.AddItem(rgb1);
Можно получить выбранный вариант с помощью метода Current у элемента группы
Внутри группы нельзя создавать разделители между кнопками, но допускается создание разделителя на панели между двумя группами RadioButtonGroup.
Первая и третья кнопка - с заданной командой для выполнения. Вторая и четвертая - просто чекбокс. Видно выбранные варианты.
Пример создания кнопки и группы
Тип 2. PulldownButton
Группа PulldownButton представляет собой раскрывающийся список, который содержит внутри несколько объектов типа PushButton. При этом вся группа имеет общий заголовок, нажав на который открываются варианты кнопок для запуска.
Группа PulldownButton создается с помощью метода AddItem() панели инструментов, который принимает элемент PullDownButtonData, который создается с помощью конструктора, принимающего две строки в качестве аргументов. Первая строка - внутреннее имя, используемое при работе Revit. Вторая строка - заголовок, который будет отображаться на панели.
Кнопки в группу добавляются с помощью метода AddItem группы. Добавлять можно элементы PushButton. Процесс создания кнопок описан в части 2.
Кнопки в группе могут быть разделены разделителями. Разделитель добавляется методом AddSeparator() группы.
Пример группы PullDownData с двумя кнопками и разделителем между ними
Пример кода для создания PullDownButton с добавлением в группу двух кнопок и разделителя между ними
Тип 3. SplitButton
Группа SplitButton полностью повторяет тип PulldownButton, за исключением того, что вместо общего заголовка для группы, отображается последняя использованная команда. Также как и PulldownButton хранит в себе несколько вариантов кнопок, которые могут быть отделены друг от друга разделителями.
Процесс добавления группы на панель и процесс добавления в нее кнопок идентичен PullDownButton, за исключением, что на панель добавляется элемент SplitButton, для создания которого используется конструктор SplitButtonData.
Пример SplitButton. Последней запускалась вторая кнопка
Пример создания элемента SplitButton на панели с двумя кнопками и разделителем между ними
Добавление элементов: элементы для сбора пользовательских данных
Тип 1. TextBox
TextBox представляет собой поле, куда может быть введен пользовательский текст, после чего этот текст может быть использован в отработке алгоритма плагина. Напрямую не является исполнительным элементом.
Для того, чтобы измененное значение было обновлено в алгоритме - пользователь должен нажать Enter после ввода текста. Также обновление может быть произведено нажатием на кнопку рядом с полем.
Также в коде должно быть прописано свойство, отвечающее за обновление значения по нажатию Enter
TextBox добавляется на панель инструментов с помощью метода AddItem() панели, который принимает в качестве аргумента TextBoxData с помощью конструктора, который в свою очередь принимает в качестве аргумента одну строку - внутреннее имя блока для использования в работе Revit.
Имеет несколько основных свойств:
textBox.Value = "Hello World" - свойство задает или выводит значение, введенное в поле textBox.ToolTip= "TextBox" - всплывающая подсказка, возникающая при наведении на поле textBox.Width = "TextBox" - задает ширину поля текстового блока. Поддерживает значение Double. textBox.Image = new BitmapImage(new Uri(@"16.png")) - задание иконки для кнопки обновления данных. Поддерживаются только иконки 16х16px textBox.ShowImageAsButton = true - включает отображение загруженной иконки в качестве кнопки обновления данных textBox.EnterPressed += CallBackOfTextBox - свойство для обновления значения блока по нажатию Enter. Для реализации - необходимо отдельно прописать событие, которое будет происходить при нажатии. Событие создается ниже, за границами события OnStartUp. Intellisense может подсказать реализацию.
Текстовое поле на панели и сообщение при нажатии на кнопку
Пример процесса создания и настройки текстового поля на панели
Описание события при нажатии. Будет выведено диалоговое окно, в котором будет отображено новое значение, введенное пользователем
Тип 2. ComboBox
Элемент ComboBox представляет собой раскрывающийся список, в котором можно выбрать один из добавленных в него элементов. По назначению похож на группу RadioButtonGroup, но на элементы не может быть завязано исполнение алгоритма. Может использоваться для выбора сценария отработки скрипта.
Элемент добавляется на панель с помощью метода AddItem() панели, который в качестве аргумента принимает элемент ComboBoxData, созданный с помощью конструктора, который в свою очередь принимает одну строку - внутреннее имя для работы Revit.
После создания бокса необходимо создать и добавить в него члены бокса. Элементы ComboBoxMember создаются на основе элементов ComboBoxMemberData, созданных конструктором, использующим две строки - внутреннее имя для Revit и имя члена бокса, которое будет отображаться при выборе.
Для каждого члена бокса можно добавить иконку размером 16х16px описанным ранее способом.
После создания и задания свойств - члены бокса добавляются в бокс с помощью метода AddItem() бокса. Также в бокс могут быть добавлены разделители между членами.
Получить выбранный член бокса можно с помощью метода Current() бокса.
Пример бокса. Выбрано второе значение.
Пример создания бокса с тремя членами. Между вторым и третьим вставлен разделитель. Всем членам бокса задана иконка
Элементы в скрытой области панели - SlideOut
На панель может быть добавлена область SlideOut, которая представляет собой скрывающуюся область на панели. Для раскрытия этой области необходимо нажать на кнопку в правом нижнем углу панели.
Скрытая область создается на панели с помощью метода AddSlideout(). Все элементы, добавленные на панель после создания скрытой области, автоматически добавляются в нее.
SlideOut в свернутом состоянии
SlideOut в развернутом состоянии
Slideout добавлен на панель, после чего добавлено текстовое поле
Группировка элементов на панели
Помимо индивидуального добавления на панель, элементы могут добавляться в составе группы. Число элементов в группе от 2 до 3.
В этом случае добавление элементов на панель производится с помощью метода AddStackedItems() панели. Данный метод принимает в себя в качестве аргументов от двух до трех элементов RibbonItemData() - это конструкторы, с помощью которых создаются элементы. Создание конструкторов для всех типов элементов было рассмотрено ранее (PushButtonData(),TextBoxData() и т.д.). При этом для элементов RibbonItemData() доступны все свойства, которые были доступны при создании элементов на панели индивидуально.
Кнопки в такой группе поддерживают иконку размером 16х16px
Группа из двух кнопок на панели
Пример создания двух кнопок с добавлением на панель в виде группы