Дартс Як отримати "значення" перерахування


83

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

Наприклад, невеликий фрагмент тестового коду повертає "day.MONDAY" у кожному випадку, коли я хочу "MONDAY"

enum day {MONDAY, TUESDAY}
print( 'Today is $day.MONDAY');
print( 'Today is $day.MONDAY.toString()');

Я правильно, що, щоб отримати лише "ПОНЕДІЛОК", мені потрібно буде проаналізувати рядок?


І немає ітератора?
Nate Lockwood,

1
Dart забезпечують спосіб значення набувають describeEnum см приклад stackoverflow.com/a/60615370/11827756
Kalpesh Dabhi

Дарту потрібно щось на зразок вбудованого властивості "name", щоб зменшити всю цю дурість (наприклад, day.MONDAY.name).
Піт Елвін,

Відповіді:


54

На жаль, ви праві, що метод toString повертається "day.MONDAY", і не тим кориснішим "MONDAY". Ви можете отримати решту рядка як:

day theDay = day.MONDAY;      
print(theDay.toString().substring(theDay.toString().indexOf('.') + 1));

Навряд чи зручно, правда.

Якщо ви хочете повторити всі значення, ви можете зробити це, використовуючи day.values:

for (day theDay in day.values) {
  print(theDay);
}

1
Старий спосіб Перерахування stackoverflow.com/questions/15854549 пропонує велику гнучкість , але не може бути використаний в якості констант. Створення бібліотеки та імпорт її з префіксом дозволяє це обійти (див. Цю відповідь у викладеному вище запитанні stackoverflow.com/a/15855913/217408 ).
Günter Zöchbauer

У чому проблема використання екземплярів "enum-class" як констант?
lrn

Не повинно виникнути жодних проблем зі створенням екземплярів const класу та їх доступністю як статичних членів const - це те, що реалізація мови enum робить у будь-якому випадку:class MyEnum { static const MyEnum someItem = const MyEnum(0); static const MyEnum someOtherItem = const MyEnum(1); final int id; const MyEnum(this.id); }
lrn

@Irm Я зміг переписати і протестувати кілька методів, які мені потрібні. Можливо, enum буде продовжено якогось дня, щоб вказати назву у вигляді рядка.
Nate Lockwood

@lrn Я спробував, і це спрацювало. Дякуємо, що вказали на це. Востаннє, коли я намагався, я отримав помилку на doSomething1([Status status = Status.off]) { DartPad
Günter Zöchbauer

102

Трохи коротше:

String day = theDay.toString().split('.').last;

9
простіший спосіб: theDay.toString (). split ('.'). останній
Хані

1
Дякую, @Hani! Ваше вдосконалення є і простішим, і "безпечнішим". Відповідь оновлено.
Jannie Theunissen

101

Dart 2.7 поставляється з новою функцією, яка називається методами розширення . Тепер ви можете написати власні методи для Enum так просто!

enum Day { monday, tuesday }

extension ParseToString on Day {
  String toShortString() {
    return this.toString().split('.').last;
  }
}

main() {
  Day monday = Day.monday;
  print(monday.toShortString()); //prints 'monday'
}

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

1
Я скасував останні зміни, оскільки він не працює без назви розширення

це не найкраще рішення. Тут Калпеш Дабхі запропонував кращий: stackoverflow.com/a/60615370/9695154
Густаво Родрігес,

describeEnumє лише методом
Flutter

52

Найпростіший спосіб отримати назву переліку - це стандартний метод за допомогою flutter / foundation.dart

describeEnum(enumObject)

enum Day {
  monday, tuesday, wednesday, thursday, friday, saturday, sunday
}

void validateDescribeEnum() {
  assert(Day.monday.toString() == 'Day.monday');
  assert(describeEnum(Day.monday) == 'monday');
}

8
Це можна використовувати лише у Flutter. Для коду лише дротиків (виконуйте стрілку замість пурхання), це призведе до помилки.
Roun

21

Є більш елегантне рішення:

enum SomeStatus {
  element1,
  element2,
  element3,
  element4,
}

const Map<SomeStatus, String> SomeStatusName = {
  SomeStatus.element1: "Element 1",
  SomeStatus.element2: "Element 2",
  SomeStatus.element3: "Element 3",
  SomeStatus.element4: "Element 4",
};

print(SomeStatusName[SomeStatus.element2]) // prints: "Element 2"

14
Ви називаєте це елегантним? Як? Додавши додатковий простір в пам’ять і 20 нових рядків?
GensaGames

2
це насправді анти-шаблон. коли вам потрібно додати більше значень до вашого переліку, вам потрібно змінити код у двох місцях, оскільки вам слід також змінити карту. уявіть, якщо у вас є сотні переліків у великій програмі та сотні карт для них у різних частинах програми. досить важко підтримувати.
Даніель Лейзен

4
Мені насправді це дуже подобається. Інші рішення здаються хитрими.
Тоні

10

Це ваш перелік:

enum Day {
  monday,
  tuesday,
}

Створити розширення (можливо, потрібно import 'package:flutter/foundation.dart';)

extension DayEx on Day {
  String get inString => describeEnum(this);
}

Використання:

void main() {
  Day monday = Day.monday;
  String inString = monday.inString; // 'monday'
}

1
Користуючись цим, describeEnumслід піти шляхом. Використання розширення - це чудовий бонус
п’єреа,

6

Я використовую наведені нижче функції, щоб отримати ім'я значення перерахування і, навпаки, значення перерахування за іменем:

String enumValueToString(Object o) => o.toString().split('.').last;

T enumValueFromString<T>(String key, Iterable<T> values) => values.firstWhere(
      (v) => v != null && key == enumValueToString(v),
      orElse: () => null,
    );

При використанні Dart 2.7 та новіших версій, тут будуть працювати методи розширення (як і для будь-яких інших Об'єктів):

extension EnumX on Object {
  String asString() => toString().split('.').last;
}

Вищезазначена реалізація не залежить від конкретних переліків.

Приклади використання:

enum Fruits {avocado, banana, orange}
...
final banana = enumValueFromString('banana', Fruits.values);
print(enumValueToString(banana)); // prints: "banana"
print(banana.asString()); // prints: "banana"

Редагувати від 05.04.2020: Додані перевірки на недійсність. valuesпараметр може бути Iterable, не обов'язково List. Додано реалізацію методу розширень. Видалено <Fruits>анотацію з прикладу, щоб показати, що дублювання імені класу не потрібно.


Дякуємо, що поділились Олександром! Це було саме те, що я шукав у моєму випадку ;-)
Рохантхевіз

найкраща відповідь для перерахування угоди
Махмуд Абу Елхеджа

3

Я так перебрав це і зробив пакет:

https://pub.dev/packages/enum_to_string

Також має зручну функцію, яка приймає enum.ValueOneта аналізує її на " Value one"

Це проста маленька бібліотека, але її модуль перевірений, і я вітаю будь-які доповнення для крайових випадків.


3
enum day {MONDAY, TUESDAY}
print( 'Today is ${describeEnum(day.MONDAY)}' );

вихід консолі: сьогодні ПОНЕДІЛНИК


2

Я використовую структуру, як показано нижче:

class Strings {
  static const angry = "Dammit!";
  static const happy = "Yay!";
  static const sad = "QQ";
}

2

Мій підхід принципово не відрізняється, але в деяких випадках може бути дещо зручнішим:

enum Day {
  monday,
  tuesday,
}

String dayToString(Day d) {
  return '$d'.split('.').last;
}

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

Ви також можете додати dayFromStringфункцію

Day dayFromString(String s) {
  return Day.values.firstWhere((v) => dayToString(v) == s);
}

Приклад використання:

void main() {
  Day today = Day.monday;
  print('Today is: ${dayToString(today)}');
  Day tomorrow = dayFromString("tuesday");
  print(tomorrow is Day);
}

1

У мене була однакова проблема в одному з моїх проектів, і існуючі рішення були не дуже чистими, і він не підтримував розширені функції, такі як json серіалізація / десеріалізація.

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

Адреса до сховища:

https://github.com/AmirKamali/Flutter_Vnum

Щоб відповісти на вашу проблему з використанням Vnum, ви можете застосувати свій код, як показано нижче:

@VnumDefinition
class Visibility extends Vnum<String> {
  static const VISIBLE = const Visibility.define("VISIBLE");
  static const COLLAPSED = const Visibility.define("COLLAPSED");
  static const HIDDEN = const Visibility.define("HIDDEN");

  const Visibility.define(String fromValue) : super.define(fromValue);
  factory Visibility(String value) => Vnum.fromValue(value,Visibility);
}

Ви можете використовувати його, як:

var visibility = Visibility('COLLAPSED');
print(visibility.value);

У репозиторії github є більше документації, сподіваємось, це допоможе вам.



1

Замість того, щоб визначати розширення для кожного переліку, ми можемо визначити розширення для об'єкта та отримати доступ до нього .enumValueз будь-якого переліку.

void main() {

  // ❌ Without Extension ❌

  print(Countries.Cote_d_Ivoire.toString().split('.').last.replaceAll("_", " ")); // Cote d Ivoire
  print(Movies.Romance.toString().split('.').last.replaceAll("_", " ")); //Romance


  // ✅ With Extension ✅

  print(Countries.Cote_d_Ivoire.enumValue); // Cote d Ivoire
  print(Movies.Romance.enumValue); //Romance
}

enum Countries { United_States, United_Kingdom, Germany, Japan, Cote_d_Ivoire }
enum Movies { Romance, Science_Fiction, Romantic_Comedy, Martial_arts }

extension PrettyEnum on Object {
  String get enumValue => this.toString().split('.').last.replaceAll("_", " ");
}

За допомогою цього ви навіть можете визначити перерахування декількох слів, де слова _в його назві розділяються знаком (підкреслення).


1

Створіть клас для допомоги:

class Enum {
    Enum._();

    static String name(value) {
        return value.toString().split('.').last;
    }
}

і зателефонуйте:

Enum.name(myEnumValue);

1

Іноді мені потрібно розділити ui-value та real-value, тому я визначав ключі та значення за допомогою Map. Таким чином, ми маємо більше гнучкості. І, використовуючи extension(починаючи з Dart 2.7), я створив метод зчитування його ключа та значення.

enum Status {
  progess,
  done,
}

extension StatusExt on Status {
  static const Map<Status, String> keys = {
    Status.progess: 'progess',
    Status.done: 'done',
  };

  static const Map<Status, String> values = {
    Status.progess: 'In Progress',
    Status.done: 'Well done',
  };

  String get key => keys[this];
  String get value => values[this];
}

// usage 1
Status status = Status.done;
String statusKey = status.key; // done
String statusValue = status.value; // Well done

// usage 2 (easy to make key and value list)
List<Status> statuses = Status.values;
List<String> statusKeys = statuses.map((e) => e.key).toList();
List<String> statusValues = statuses.map((e) => e.value).toList();
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.