Dart 如何将枚举的名称转换为字符串?

210
在Dart中,枚举类型不可用时,我曾编写了一些笨重且难以维护的代码来模拟枚举类型,现在想简化它。我需要获取枚举类型的名称作为字符串,就像Java中可以做的那样,但是无法实现。
例如,下面的小测试代码片段每次返回'day.MONDAY',而我希望得到的是'MONDAY'。
enum day {MONDAY, TUESDAY}
print( 'Today is $day.MONDAY');
print( 'Today is $day.MONDAY.toString()');

我理解的是,要仅获取“MONDAY”,需要解析字符串,是这样吗?


而且没有迭代器吗? - Nate Lockwood
2
Dart提供了获取值描述的方法describeEnum,可以参考以下示例 https://stackoverflow.com/a/60615370/11827756 - Kalpesh Dabhi
2
Dart需要类似于“名称”内置属性的东西来限制所有这些愚蠢行为(例如,day.MONDAY.name)。 - Pete Alvin
这是 https://github.com/dart-lang/language/issues/1511 。 - jamesdlin
2
它现在实际上已经有了:https://api.flutter.dev/flutter/foundation/describeEnum.html - schlenger
32个回答

235
Dart 2.15
enum Day { monday, tuesday }

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

Dart 2.7 - 2.14
有了名为扩展方法的新功能,你可以像这样简单地为枚举编写自己的方法!
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'
}

如此描述,尽管枚举已经正确导入,但我无法从不同的文件中使用扩展。当我按照文档示例并添加了一个名称到扩展时,它就可以工作了:“extension Formatting on Day”。如果不仅仅是我遇到这个问题,也许需要进行编辑。另外,响应应该更新为Jannie和mbartn回复的混合。 - ko0stik
1
我撤销了上次的编辑更改,因为没有扩展名名称它无法工作。 - user10539074
这不是最好的解决方案。Kalpesh Dabhi在这里提供了更好的解决方案:https://stackoverflow.com/a/60615370/9695154 - Gustavo Rodrigues
describeEnum 是 Flutter 中的一个方法,问题是如何在 Dart 中获取枚举的值。 - mbartn
1
@mbartn Flutter的实现示例可在https://api.flutter.dev/flutter/foundation/describeEnum.html中查看。 - Ian
为了防止第三方库的攻击/冲突,该扩展仅在本地文件中运行。要在其他文件中使用它,您将不得不导入扩展名本身,在这种情况下是:ParseToString。请记住,如果您需要i18n,则使用枚举作为字符串可能不是最佳方法。仍然需要一些“中间”代码。 - Adrian Moisa

148

更新Dart 2.15:

enum Day {
  monday,
  tuesday,
}

您可以在枚举上使用 name 属性。
String monday = Day.monday.name; // 'monday'

旧解决方案:

1. 直接方式:

var dayInString = describeEnum(Day.monday);
print(dayInString); // prints 'monday'
2. 使用扩展:
extension DayEx on Day {
  String get name => describeEnum(this);
}

您可以像这样使用它:

void main() {
  Day monday = Day.monday;
  print(monday.name); // 'monday'
}

4
利用describeEnum是一个好方法。使用扩展是一个不错的附加功能。 - pierrea
1
值得注意的是,describeEnum 是仅通过Flutter框架才能使用的方法。因此不适用于那些希望在纯Dart中执行相同操作的人们。 - shennan
2
这应该是第一个答案(当前的第一个答案已经过时了)。 - yoni keren

140

稍微短一点:

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

15
更简单的写法:theDay.toString().split('.').last。该代码用于获取日期(theDay)的字符串表示形式中的最后一部分,即日期的时间部分。 - Hani
2
谢谢,@Hani!你的改进更简单、更“安全”。已更新答案。 - Jannie Theunissen

105

以前正确的方式是通过toString方法获取枚举值的源名称,该方法返回"day.MONDAY"而不是更有用的"MONDAY"

自Dart 2.15以来,枚举类型已经公开了扩展getter,它只返回源名称,因此day.MONDAY.name == "MONDAY"

自Dart 2.17以来,您还可以向enum声明中添加自己的成员,并选择为某个值提供另一个名称,而不仅仅是其源名称,例如,具有更适当的大写:

enum Day {
  monday("Monday"),
  tuesday("Tuesday"),
  // ...
  saturday("Saturday");
  sunday("Sunday");
 
  final String name;
  Day(this.name);
 
  // And other members too.
  bool get isWorkday => index < saturday.index;
}

然后你会得到 Day.sunday.name == "Sunday"(隐藏了扩展的name getter,它将返回“sunday”)。

在这些功能之前,你只能从toString字符串中提取其余部分来获取名称:

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

不可否认,这个方式确实非常不方便。

另一种将枚举类型名称转换为字符串的方式更短,但也不太高效,因为它会多创建一个不必要的字符串作为前缀:

theDay.toString().split('.').last

如果性能不重要,那可能就是我会写的,只为了简洁。

如果你想迭代所有值,可以使用day.values

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

1
旧的枚举方式 https://dev59.com/XGUo5IYBdhLWcg3wrhBD 提供了更多的灵活性,但不能用作常量。创建一个库并使用前缀导入可以解决这个问题(请参见上面链接问题中的答案https://dev59.com/XGUo5IYBdhLWcg3wrhBD#15855913)。 - Günter Zöchbauer
使用“枚举类”实例作为常量的问题是什么? - lrn
@Irm 我已经能够重写和测试我需要的几个方法了。也许将来枚举类型会被扩展,以便提供字符串形式的名称。 - Nate Lockwood
@lrn 我试了一下,它起作用了。感谢你指出来。上次我尝试时,在 doSomething1([Status status = Status.off]) { 处出现了错误。DartPad - Günter Zöchbauer
对我来说,这段代码用于解析toString()返回的字符串更易读:unumName(var target) => (target.toString()).split('.')[1]; 这样写是否等效或者是一种不好的做法?我在Dart方面还是个新手。 - Nate Lockwood
显示剩余5条评论

82

从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');
}

12
这只能在Flutter中使用。对于仅使用Dart代码(而非Flutter运行)的情况,它会引发错误。 - Roun
1
完整的实现请参见 https://api.flutter.dev/flutter/foundation/describeEnum.html。 - Ian
查看此帖子,它将解决所有问题:https://arkapp.medium.com/supercharge-enum-with-extensions-in-flutter-abf8fdf706fe - abdul rehman

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

控制台输出:今天是星期一


23

有一个更加优雅的解决方案:

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"

31
你是在说这是优雅的吗?如何才能做到呢?通过在内存中添加额外空间和20行新代码吗? - GensaGames
5
这实际上是一种反模式。当您需要向枚举类型添加更多值时,您需要在两个地方修改代码,因为您还需要修改该映射。如果您在大型应用程序中有数百个枚举和数百个不同部分的映射,那么维护它们会相当困难。 - Daniel Leiszen
5
我其实非常喜欢这个。其他的解决方案看起来很不专业。 - Tony
1
如果您正在使用JsonSerializable,则可以自动生成此Map。由于大多数实际项目需要使用某种JSON解析器,因此我认为这是最佳解决方案。其他人则依赖于enum.toString()的实现细节中包含一个点。 - Dread Boy
现在Dart有enum.name,所以没有理由再使用这种模式了。 - rjh

13
有时候我需要区分UI值和真实值,所以我使用Map定义了键和值。通过这种方式,我们具有更多的灵活性。并且通过使用扩展(自Dart 2.7开始),我创建了一个方法来读取其键和值。

有时候我需要区分UI值和真实值,所以我使用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];

  // NEW
  static Status fromRaw(String raw) => keys.entries
      .firstWhere((e) => e.value == raw, orElse: () => null)
      ?.key;
}

// 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();

// usage 3. create Status enum from string.
Status done1 = StatusExt.fromRaw('done') // Status.done
Status done2 = StatusExt.fromRaw('dude') // null

11
https://medium.com/dartlang/dart-2-17-b216bfc80c5d中所述:

Dart 2.17中,我们现在可以对枚举成员进行一般性支持。 这意味着我们可以添加保存状态的字段、设置该状态的构造函数、具有功能的方法,甚至覆盖现有成员。

示例:

enum Day {
   MONDAY("Monday"),
   TUESDAY("Tuesday");

  const Day(this.text);
  final String text;
}

输出:

void main() {
  const day = Day.MONDAY;
  print(day.text); /// Monday
}

对于上述功能,覆盖Dart版本如下,目标为2.17及以上

environment:
  sdk: ">=2.17.0 <3.0.0"

它能工作,但有一个错误需要纠正枚举 Day { MONDAY("星期一"), TUESDAY("星期二"); const Day(this.text); final String text; } - Rick Robin

9
我使用以下结构:

我使用以下结构:

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

1
使用 abstract 关键字使其不能被实例化,因为没有理由想要实例化这个类。 - KR Tirtho
你会如何在下拉小部件或任何小部件中使用它? - dianesis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接