Я понимаю, что иногда вам нужны свойства, такие как:
public int[] Transitions { get; set; }
или:
[SerializeField] private int[] m_Transitions;
public int[] Transitions { get { return m_Transitions; } set { m_Transitions = value; } }
Но у меня сложилось впечатление, что в разработке игр, если у вас нет причин поступать иначе, все должно быть как можно быстрее. Из того, что я прочитал, у меня сложилось впечатление, что Unity 3D не обязательно обеспечивает встроенный доступ через свойства, поэтому использование свойства приведет к дополнительному вызову функции.
Прав ли я постоянно игнорировать предложения Visual Studio не использовать открытые поля? (Обратите внимание, что мы используем int Foo { get; private set; }
там, где есть преимущество в показе предполагаемого использования.)
Я знаю, что преждевременная оптимизация - это плохо, но, как я уже сказал, в разработке игр вы не делаете что-то медленное, если подобные усилия могут сделать это быстро.
источник
ArrayList
). Это связано с тем, что у C нет универсальных шаблонов, и эти программисты на C, вероятно, обнаружили, что легче повторно реализовать связанные списки, чем сделать то же самое дляArrayLists
алгоритма изменения размера. Если вам нужна хорошая низкоуровневая оптимизация, заключите ее в капсулу!Ответы:
Не обязательно. Как и в прикладном программном обеспечении, в игре есть код, который критичен к производительности, и код, которого нет.
Если код выполняется несколько тысяч раз за кадр, тогда такая низкоуровневая оптимизация может иметь смысл. (Хотя вы, возможно, сначала захотите сначала проверить, действительно ли необходимо вызывать его так часто. Самый быстрый код - это код, который вы не запускаете)
Если код выполняется один раз в пару секунд, то удобочитаемость и удобство сопровождения гораздо важнее производительности.
С тривиальными свойствами в стиле
int Foo { get; private set; }
вы можете быть уверены, что компилятор оптимизирует оболочку свойств. Но:GetFoo()
/SetFoo(value)
метод: Для того, чтобы подразумевать , что есть намного больше , что происходит за кулисами. (или если мне нужно быть еще более уверенным, что другие поймут намек:CalculateFoo()
/SwitchFoo(value)
)источник
Update
полностью сэкономит вам больше времени, чемUpdate
быстрое.В случае сомнений используйте лучшие практики.
Вы сомневаетесь
Это был простой ответ. Конечно, реальность сложнее.
Во-первых, существует миф, что игровое программирование - это сверхвысокопроизводительное программирование, и все должно быть максимально быстрым. Я классифицирую программирование игр как программирование, ориентированное на производительность . Разработчик должен знать об ограничениях производительности и работать в них.
Во-вторых, существует миф о том, что максимально быстрое выполнение всех задач - это то, что заставляет программу работать быстро. В действительности, выявление и оптимизация узких мест - это то, что делает программу быстрой, и это гораздо проще / дешевле / быстрее сделать, если код легко поддерживать и изменять; чрезвычайно оптимизированный код трудно поддерживать и модифицировать. Отсюда следует, что для написания чрезвычайно оптимизированных программ вы должны использовать экстремальную оптимизацию кода так часто, как это необходимо, и как можно реже.
В-третьих, преимущество свойств и методов доступа в том, что они устойчивы к изменениям и, следовательно, облегчают поддержку и изменение кода - это то, что вам нужно для написания быстрых программ. Хотите создать исключение всякий раз, когда кто-то хочет установить ваше значение равным нулю? Используйте сеттер. Хотите отправить уведомление, когда значение меняется? Используйте сеттер. Хотите записать новое значение? Используйте сеттер. Хотите сделать ленивую инициализацию? Используйте геттер.
И в-четвертых, лично я не следую лучшим рекомендациям Microsoft в этом случае. Если мне нужно свойство с обычными и общедоступными методами получения и установки, я просто использую поле и просто изменяю его на свойство, когда это необходимо. Тем не менее, мне очень редко нужно такое тривиальное свойство - гораздо чаще, когда я хочу проверять граничные условия или хочу сделать установщик частным.
источник
Среде выполнения .net очень просто встроить простые свойства, и часто это происходит. Но это встраивание обычно отключается профилировщиками, поэтому его легко ввести в заблуждение и подумать, что изменение открытого свойства на открытое поле ускорит работу программного обеспечения.
В коде, который вызывается другим кодом, который не будет перекомпилирован, когда ваш код перекомпилируется, есть хороший случай для использования свойств, поскольку переход от поля к свойству является критическим изменением. Но для большей части кода, кроме возможности установить точку останова на get / set, я никогда не видел преимуществ в использовании простого свойства по сравнению с открытым полем.
Основная причина, по которой я использовал свойства в свои 10 лет в качестве профессионального программиста на C #, заключалась в том, чтобы другие программисты не говорили мне, что я не придерживаюсь стандарта кодирования. Это был хороший компромисс, поскольку вряд ли когда-либо имелись веские основания не использовать собственность.
источник
Структуры и открытые поля облегчат взаимодействие с некоторыми неуправляемыми API. Часто вы обнаружите, что низкоуровневый API хочет получить доступ к значениям по ссылке, что хорошо для производительности (так как мы избегаем ненужной копии). Использование свойств является препятствием для этого, и часто библиотеки-обертки делают копии для простоты использования, а иногда и для безопасности.
Из-за этого вы будете часто получать лучшую производительность, имея векторные и матричные типы, которые не имеют свойств, но имеют открытые поля.
Лучшие практики не создаются в вакууме. Несмотря на некоторый культ груза, в целом передовая практика существует по уважительной причине.
В этом случае у нас есть пара:
Свойство позволяет изменять реализацию без изменения клиентского кода (на двоичном уровне можно изменить поле на свойство без изменения клиентского кода на исходном уровне, однако оно будет компилироваться во что-то другое после изменения) , Это означает, что при использовании свойства с самого начала код, который ссылается на ваш, не нужно будет перекомпилировать просто для того, чтобы изменить то, что свойство делает внутренне.
Если не все возможные значения полей вашего типа являются допустимыми состояниями, то вы не хотите предоставлять их клиентскому коду, который его модифицирует. Таким образом, если некоторые комбинации значений недопустимы, вы хотите оставить поля закрытыми (или внутренними).
Я говорил код клиента. Это означает код, который вызывает ваш. Если вы не создаете библиотеку (или даже делаете библиотеку, но используете внутреннюю, а не публичную), вы обычно можете сойти с рук и с какой-то хорошей дисциплиной. В этой ситуации лучше всего использовать свойства, чтобы не позволить себе выстрелить себе в ногу. Более того, гораздо проще рассуждать о коде, если вы видите все места, где поле может измениться в одном файле, вместо того, чтобы беспокоиться о том, что оно модифицируется или нет, где-то еще. Фактически, свойства также хороши для установки точек останова, когда вы выясняете, что пошло не так.
Да, есть ценность видеть, что делается в промышленности. Тем не менее, у вас есть мотивация идти против лучших практик? или вы просто идете против лучших практик - усложняете работу кода - только потому, что это сделал кто-то другой? Ах, кстати, «другие делают это» - это то, как вы начинаете культ груза.
Итак ... Ваша игра работает медленно? Вам лучше посвятить время, чтобы выяснить узкое место и исправить это, вместо того, чтобы размышлять, что бы это могло быть. Вы можете быть уверены, что компилятор выполнит множество оптимизаций, поэтому есть вероятность, что вы смотрите не на ту проблему.
С другой стороны, если вы решаете, с чего начать, вам следует сначала позаботиться о том, какие алгоритмы и структуры данных, а не о мелких деталях, таких как поля и свойства.
Наконец, вы зарабатываете что-то, идя против лучших практик?
Для конкретных случаев (Unity и Mono для Android), Unity принимает значения по ссылке? Если этого не произойдет, он все равно скопирует значения, без увеличения производительности.
Если это так, если вы передаете эти данные в API, который принимает ref. Имеет ли смысл сделать поле общедоступным, или вы могли бы сделать тип способным напрямую вызывать API?
Да, конечно, могут быть оптимизации, которые вы можете сделать, используя структуры с открытыми полями. Например, вы получаете к ним доступ с помощью
указателейSpan<T>
или чего-то подобного. Они также компактны в памяти, что упрощает их сериализацию для отправки по сети или в постоянное хранилище (и да, это копии).Теперь, если вы выбрали правильные алгоритмы и структуры, если они оказываются узким местом, тогда вы решаете, как лучше всего это исправить ... это может быть структура с открытыми полями или нет. Вы сможете беспокоиться об этом, если и когда это произойдет. Тем временем вы можете беспокоиться о более важных вещах, таких как создание хорошей или веселой игры, в которую стоит играть
источник
:
Вы правы, зная, что преждевременная оптимизация - это плохо, но это только половина дела (см. Полную цитату ниже). Даже в разработке игр не все должно быть как можно быстрее.
Сначала заставь это работать. Только тогда оптимизируйте биты, которые в этом нуждаются. Это не означает, что первый проект может быть грязным или излишне неэффективным, но оптимизация не является приоритетом на данном этапе.
« Настоящая проблема заключается в том, что программисты потратили слишком много времени, беспокоясь об эффективности в неправильных местах и в неподходящее время ; преждевременная оптимизация - корень всего зла (или, по крайней мере, большей его части) в программировании». Дональд Кнут, 1974.
источник
Для базовых вещей, подобных этому, оптимизатор C # все равно все сделает правильно. Если вы беспокоитесь об этом (и если вы беспокоитесь об этом для более чем 1% кода, вы делаете это неправильно ), атрибут был создан для вас. Атрибут
[MethodImpl(MethodImplOptions.AggressiveInlining)]
значительно увеличивает вероятность того, что метод будет встроен (методы получения и установки свойств являются методами).источник
Экспонирование элементов типа структуры в качестве свойств, а не полей часто приводит к низкой производительности и семантике, особенно в тех случаях, когда структуры фактически рассматриваются как структуры, а не как «причудливые объекты».
Структура в .NET представляет собой набор полей, склеенных друг с другом, и которые для некоторых целей могут рассматриваться как единое целое. Платформа .NET Framework разработана для того, чтобы структуры могли использоваться почти так же, как объекты классов, и в некоторых случаях это может быть удобно.
Большая часть рекомендаций, связанных со структурами, основана на том, что программисты захотят использовать структуры как объекты, а не как склеенные вместе коллекции полей. С другой стороны, во многих случаях может быть полезно иметь что-то, что ведет себя как связка склеенных полей. Если это то, что нужно, совет .NET добавит ненужную работу как для программистов, так и для компиляторов, обеспечивая худшую производительность и семантику, чем простое непосредственное использование структур.
Основные принципы, которые следует учитывать при использовании структур:
Не передавайте и не возвращайте структуры по значению и не заставляйте их копировать без необходимости.
Если придерживаться принципа № 1, большие структуры будут работать так же хорошо, как и маленькие.
Если
Alphablob
это структура , содержащая 26 общественныхint
полей с именем аз, и свойство геттер ,ab
которая возвращает сумму своихa
иb
полями, то данныйAlphablob[] arr; List<Alphablob> list;
кодint foo = arr[0].ab + list[0].ab;
нужно будет прочитать поляa
иb
изarr[0]
, но нужно будет прочитать все 26 полей ,list[0]
даже если это будет игнорировать все, кроме двух из них. Если кто-то хочет иметь общую коллекцию, подобную списку, которая может эффективно работать с подобной структуройalphaBlob
, следует заменить индексированный метод получения методом:который затем будет вызывать
proc(ref backingArray[index], ref param);
. Учитывая такую коллекцию, если заменитьsum = myCollection[0].ab;
наэто позволит избежать необходимости копировать части
alphaBlob
, которые не будут использоваться в получателе свойств. Поскольку переданный делегат не имеет доступа ни к чему, кроме егоref
параметров, компилятор может передать статический делегат.К сожалению, .NET Framework не определяет ни один из видов делегатов, необходимых для того, чтобы эта штука работала хорошо, и синтаксис оказывается в некотором беспорядке. С другой стороны, этот подход позволяет избежать ненужного копирования структур, что, в свою очередь, сделает практичным выполнение действий на месте с большими структурами, хранящимися в массивах, что является наиболее эффективным способом доступа к хранилищу.
источник
list
свойства, и метод получения свойства дляab
оба встроены, JITter мог бы распознать, что только членыa
иb
необходимы, но я не знаю, какая версия JITter действительно делает такие вещи.