Швидкі константи: Struct або Enum


80

Я не впевнений, які з обох краще визначити константами. Структура або перелік. Структура буде скопійована кожного разу, коли я її використовую чи ні? На static letмою думку, коли я думаю про структуру з константами, немає сенсу, що вона буде постійно копіюватися. Але якщо це не буде скопійовано, то це не має значення, що я беру?

Які переваги робить вибір структури або переліку?

Франциско каже, що використовуйте Struct's.

Рей Вендерліх каже, використовуй Енума. Але мені не вистачає обґрунтування.


3
Обґрунтування наведено у пов'язаній статті: "Перевага використання переліку без регістру полягає в тому, що воно не може випадково створити інстанцію і працює як чистий простір імен".
Martin R

Добре, це звучить логічно. Тож я повинен використовувати перелічення у 90% своїх випадків. І як тільки щось потрібно створити або створити змінну, я використовую структуру. Правильно?
Paixsn

2
Чому б вам не визначити їх у класах, які ними користуються? Навіщо потрібно розміщувати всі константи в одній структурі? Ви все ще можете мати їх в одному файлі, якщо використовуєте розширення. Якщо ви вирішуєте між переліченням та структурою, я не кажу ні з точки зору архітектури.
Султан

Тому що мені потрібна структура, яку я можу включити у велику частину своїх проектів. Мені знадобляться однакові константи в усіх. Тому я не хочу писати це кілька разів.
Paixsn

1
@SnowN Я не проти констант, але кажу вам, що немає необхідності об'єднувати їх усіх в одну загальну структуру / перелік, якщо вони не мають нічого спільного.
Султан

Відповіді:


130

Працюють як структури, так і переліки. Як приклад, обидва

struct PhysicalConstants {
    static let speedOfLight = 299_792_458
    // ...
}

і

enum PhysicalConstants {
    static let speedOfLight = 299_792_458
    // ...
}

робота і визначити статичну властивість PhysicalConstants.speedOfLight.

Re: Структура буде скопійована кожного разу, коли я її використовую чи ні?

Обидва structі enumє типами значень, так що це стосується і перелічень. Але це тут не має значення, оскільки вам зовсім не потрібно створювати значення: Статичні властивості (їх також називають type властивостями ) - це властивості самого типу, а не екземпляра цього типу.

Re: Які переваги має вибір структури або переліку?

Як згадувалося у статті, пов’язаній із посиланням :

Перевага використання переліку без регістру полягає в тому, що воно не може випадково створити екземпляр і працює як чистий простір імен.

Отже, для структури,

let foo = PhysicalConstants()

створює (марне) значення типу PhysicalConstants, але для безрезультатного переліку не вдається скомпілювати:

let foo = PhysicalConstants()
// error: 'PhysicalConstants' cannot be constructed because it has no accessible initializers

Існує один конкретний випадок, що стосується операторів перемикання, коли ви не можете використовувати безперечкові перелічення. Див. Мою відповідь для обговорення та можливих обхідних шляхів.
Тім Фукуа

6
Незначне доповнення: якщо ми додамо private init() {}до Structприкладу, він також матиме "перевагу" у тому, що неможливо випадково створити екземпляр на льоту. (Природно, що деякі "користувачі-розробники" можуть обійти цю "перевагу", включивши ініціалізатор у розширення до Struct:, але якщо ми з якихось причин віддамо перевагу використанню Struct"чистого простору імен", а не а enum, тоді приватний ініціалізатор може бути гарною захисною системою "не використовувати-як-екземпляр").
dfrib

... і я щойно зрозумів, що саме цей момент був (начебто) згаданий в іншій відповіді нижче (хоча приватна проблема з тим самим файлом, про яку згадується, більше не присутня в Swift 3).
dfrib

це дуже не корисна перевага. Я насправді прийшов сюди для порівняння між struct Constants static let speedOfLight = 300vs. enum Constants enum Light : Int case speed = 300Чи можете ви порівняти і ті, що у вашій відповіді? Або є інша відповідь на це порівняння?
Мед

@Honey: Це вже інше питання. Цей (як я зрозумів) стосується надання простору імен. Статичні константи можна визначити всередині структури або всередині переліку. Вони можуть мати різні типи, а ви можете мати кілька констант з однаковим значенням. - Випадки перерахування визначають (взаємно відмінні) значення одного типу.
Martin R

17

Ось коротка відповідь: чи ваші константи повинні бути унікальними? Потім використовуйте перерахування, яке виконує це.

Хочете використовувати кілька різних констант, щоб містити одне і те ж значення (часто корисне для наочності)? Потім використовуйте структуру, яка це дозволяє.


1
Я думаю, що з тих пір він не буде використовувати окремі випадки, тому йому завжди потрібно буде писатиStaticVars.pi.rawValue
Михайло

8

Одна відмінність між ними полягає в тому, що структури можуть бути створені там, де перерахування не можуть. Тож у більшості сценаріїв, коли вам просто потрібно використовувати константи, мабуть, найкраще використовувати перелічення, щоб уникнути плутанини.

Наприклад:

struct Constants {
    static let someValue = "someValue"
}

let _ = Constants()

Наведений вище код все ще буде дійсним.

Якщо ми використовуємо перерахування:

enum Constants {
    static let someValue = "someValue"
}

let _ = Constants() // error

Наведений вище код буде недійсним, і тому уникайте плутанини.


6

Використання Xcode 7.3.1 та Swift 2.2

Незважаючи на те, що я погоджуюсь з Мартіном Р, і керівництво по стилю Рея Вендерліха добре вказує на те, що перелічення є кращими майже у всіх випадках використання, оскільки це чистий простір імен, є одне місце, де використовують structкозирі enums.

Переключити оператори

Почнемо з версії struct:

struct StaticVars {
    static let someString = "someString"
}

switch "someString" {
case StaticVars.someString: print("Matched StaticVars.someString")
default: print("Didn't match StaticVars.someString")
}

Використовуючи структуру, це збігатиметься та роздруковуватиметься Matched StaticVars.someString.

Тепер давайте розглянемо версію enum без випадків (змінивши лише ключове слово structна enum):

enum StaticVars {
    static let someString = "someString"
}

switch "someString" {
case StaticVars.someString: print("Matched StaticVars.someString")
default: print("Didn't match StaticVars.someString")
}

Ви помітите, що ви отримуєте помилку часу компіляції в операторі switch на case StaticVars.someString:рядку. Помилка Enum case 'someString' not found in type 'String'.

Існує псевдо-обхідний шлях шляхом перетворення властивості static у закриття, яке замість цього повертає тип.

Тож ви б змінили це так:

enum StaticVars {
    static let someString = { return "someString" }
}

switch "someString" {
case StaticVars.someString(): print("Matched StaticVars.someString")
default: print("Didn't match StaticVars.someString")
}

Зверніть увагу на необхідність дужок у заяві case, оскільки це тепер функція.

Недоліком є ​​те, що тепер, коли ми зробили її функцією, вона виконується кожного разу, коли її викликають. Отже, якщо це просто простий примітивний тип, наприклад, Stringабо Int, це не так вже й погано. По суті, це обчислювана властивість. Якщо це константа, яку потрібно обчислити, і ви хочете обчислити її лише один раз, розгляньте можливість обчислити її в іншу властивість і повернути це вже обчислене значення в закритті.

Ви також можете замінити ініціалізатор за замовчуванням на приватний, і тоді ви отримаєте такий самий тип помилки часу компіляції, як і у випадку безперечкового перерахування.

struct StaticVars {
    static let someString = "someString"
    private init() {}
}

Але з цим ви хочете помістити декларацію структури у власний файл, тому що якщо ви оголосите її в тому ж файлі, що, скажімо, клас View Controller, файл цього класу все одно зможе випадково створити екземпляр марного екземпляр StaticVars, але поза файлом класу він буде працювати за призначенням. Але це ваш дзвінок.


3
Очевидно, ця проблема була виправлена. "Безкриптова версія перерахування" компілюється та працює, як очікувалось, у Xcode 8 beta 6.
Martin R,

1
Прямо! Мені дуже подобається використовувати безрезультатні перелічення для вигоди "без інстанції". І я також радий, що розпочав свою публікацію з інформації про версію Xcode, інакше могло б бути питання "працює на моїй машині".
Тім Фукуа

@MartinR Як ви вже зазначали, що в Xcode 8 відсортовано випадок "безпідставного перерахування", тож яка різниця в оголошенні статичного дозволу у "struct" та у "переліченні".
G.Abhisek

@ G.Abhisek: Я намагався відповісти на це у своїй відповіді. Використання переліку без регістрів заважає створити (марний) екземпляр цього типу. Для самої константи це зовсім не має значення.
Martin R

@MartinR Отже, це означає, що коли ми отримуємо доступ через структуру, ми без потреби створюємо екземпляр, тоді як у випадку перечислення він діє як простір імен.
G.Abhisek

4

У рамках Combine компанія Apple вирішила віддати перевагу перерахуванням просторів імен.

enum Publishers

Простір імен для типів, які служать видавцями.

enum Subscribers

Простір імен для типів, які служать абонентами.

enum Subscriptions

Простір імен для символів, пов’язаних з передплатою.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.