字符串中的枚举

我有一个 Enum和一个函数来创建它从一个 String,因为我不能找到一个内置的方式来做到这一点

enum Visibility{VISIBLE,COLLAPSED,HIDDEN}


Visibility visibilityFromString(String value){
return Visibility.values.firstWhere((e)=>
e.toString().split('.')[1].toUpperCase()==value.toUpperCase());
}


//used as
Visibility x = visibilityFromString('COLLAPSED');

但似乎我必须为每个 Enum 重写这个函数,有没有办法写出同样的函数,其中使用 Enum 类型作为参数?我试过了,但是我发现我不能选 Enum。

//is something with the following signiture actually possible?
dynamic enumFromString(Type enumType,String value){


}
112134 次浏览

使用镜像可以强制某些行为。我有两个想法。遗憾的是,Dart 不支持类型化函数:

import 'dart:mirrors';


enum Visibility {VISIBLE, COLLAPSED, HIDDEN}


class EnumFromString<T> {
T get(String value) {
return (reflectType(T) as ClassMirror).getField(#values).reflectee.firstWhere((e)=>e.toString().split('.')[1].toUpperCase()==value.toUpperCase());
}
}


dynamic enumFromString(String value, t) {
return (reflectType(t) as ClassMirror).getField(#values).reflectee.firstWhere((e)=>e.toString().split('.')[1].toUpperCase()==value.toUpperCase());
}


void main() {
var converter = new EnumFromString<Visibility>();


Visibility x = converter.get('COLLAPSED');
print(x);


Visibility y = enumFromString('HIDDEN', Visibility);
print(y);
}

产出:

Visibility.COLLAPSED
Visibility.HIDDEN

镜像并不总是可用的,但幸运的是您不需要它们。

enum Fruit { apple, banana }


// Convert to string
String str = Fruit.banana.toString();


// Convert to enum
Fruit f = Fruit.values.firstWhere((e) => e.toString() == 'Fruit.' + str);


assert(f == Fruit.banana);  // it worked

非常感谢@Frost 对答案的纠正

Collin Jackson 的解决方案对我不起作用,因为 Dart 将枚举字符串化为 EnumName.value,而不仅仅是 value(例如,Fruit.apple) ,我试图转换字符串值,比如 apple,而不是从头开始转换 Fruit.apple

考虑到这一点,这就是我对字符串问题的枚举的解决方案

enum Fruit {apple, banana}


Fruit getFruitFromString(String fruit) {
fruit = 'Fruit.$fruit';
return Fruit.values.firstWhere((f)=> f.toString() == fruit, orElse: () => null);
}

有几个枚举包允许我只获取枚举字符串,而不是 type.value 字符串(Apple,而不是 Fruit)。苹果)。

Https://pub.dartlang.org/packages/built_value (这是最新资料)

Https://pub.dartlang.org/packages/enums

void main() {
print(MyEnum.nr1.index);            // prints 0
print(MyEnum.nr1.toString());       // prints nr1
print(MyEnum.valueOf("nr1").index); // prints 0
print(MyEnum.values[1].toString())  // prints nr2
print(MyEnum.values.last.index)     // prints 2
print(MyEnum.values.last.myValue);  // prints 15
}

@ 科林杰克逊有一个非常好的答案,国际海事组织。在找到这个问题之前,我使用了 for-in 循环来获得类似的结果。我肯定会切换到使用 firstWhere 方法。

在他的答案的基础上,我做了如下处理从值字符串中移除类型的工作:

enum Fruit { apple, banana }


class EnumUtil {
static T fromStringEnum<T>(Iterable<T> values, String stringType) {
return values.firstWhere(
(f)=> "${f.toString().substring(f.toString().indexOf('.')+1)}".toString()
== stringType, orElse: () => null);
}
}


main() {
Fruit result = EnumUtil.fromStringEnum(Fruit.values, "apple");
assert(result == Fruit.apple);
}

也许有人会发现这个有用。

我在从 JSON 构建对象时遇到了同样的问题。在 JSON 中,值是字符串,但我希望枚举验证该值是否正确。我编写了这个助手,它使用任何枚举,而不是指定的枚举:

class _EnumHelper {




var cache = {};


dynamic str2enum(e, s) {
var o = {};
if (!cache.containsKey(e)){
for (dynamic i in e) {
o[i.toString().split(".").last] = i;
}
cache[e] = o;
} else {
o = cache[e];
}
return o[s];
}
}


_EnumHelper enumHelper = _EnumHelper();

用法:

enumHelper.str2enum(Category.values, json['category']);

附言。我不是故意在这里使用类型的。枚举在 Dart 中不是类型,并且把它当作一个使事情复杂化的类型。类仅用于缓存目的。

下面是将给定字符串转换为枚举类型的函数:

EnumType enumTypeFromString(String typeString) => EnumType.values
.firstWhere((type) => type.toString() == "EnumType." + typeString);

下面是如何将给定的枚举类型转换为字符串:

String enumTypeToString(EnumType type) => type.toString().split(".")[1];

我的解决方案与 Rob C 的解决方案相同,但是没有字符串插值:

T enumFromString<T>(Iterable<T> values, String value) {
return values.firstWhere((type) => type.toString().split(".").last == value,
orElse: () => null);
}

使用 收集包裹中的 firstWhereOrNull()的 Null 安全示例

static T? enumFromString<T>(Iterable<T> values, String value) {
return values.firstWhereOrNull((type) => type.toString().split(".").last == value);
}

我认为我的方法略有不同,但在某些情况下可能更方便。最后,我们为枚举类型提供了 parse 和 tryParse:

import 'dart:mirrors';


class Enum {
static T parse<T>(String value) {
final T result = (reflectType(T) as ClassMirror).getField(#values)
.reflectee.firstWhere((v)=>v.toString().split('.').last.toLowerCase() == value.toLowerCase()) as T;
return result;
}


static T tryParse<T>(String value, { T defaultValue }) {
T result = defaultValue;
try {
result = parse<T>(value);
} catch(e){
print(e);
}
return result;
}
}

编辑: 这种方法在 Flutter 应用程序中不起作用,默认情况下,Flutter 中的镜像被阻塞,因为它导致生成的包非常大。

我在一个项目中遇到过同样的问题,现有的解决方案不是很干净,而且不支持 json 序列化/反序列化这样的高级特性。

Flutter 本身目前不支持带值的枚举,但是,我设法使用类和反射器实现开发了一个帮助程序包 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 repo 中有更多的文档,希望它能帮到你。

这一切太复杂了,我做了一个简单的图书馆来完成这项工作:

Https://pub.dev/packages/enum_to_string

import 'package:enum_to_string:enum_to_string.dart';


enum TestEnum { testValue1 };


convert(){
String result = EnumToString.parse(TestEnum.testValue1);
//result = 'testValue1'


String resultCamelCase = EnumToString.parseCamelCase(TestEnum.testValue1);
//result = 'Test Value 1'
    

final result = EnumToString.fromString(TestEnum.values, "testValue1");
// TestEnum.testValue1
}

更新: 2022/02/10 Dart v2.15实现了一些额外的枚举方法,可以解决您的问题。

从这里: https://medium.com/dartlang/dart-2-15-7e7a598e508a

改进了省道中的枚举: 核心库

我们在 dart: core 库(语言问题 # 1511)中对枚举 API 做了一些方便的添加。现在可以使用。姓名:

enum MyEnum {
one, two, three
}
void main() {
print(MyEnum.one.name);  // Prints "one".
}

还可以按名称查找枚举值:

print(MyEnum.values.byName('two') == MyEnum.two);  // Prints "true".

最后,您可以得到所有名称-值对的映射:

final map = MyEnum.values.asNameMap();
print(map['three'] == MyEnum.three);  // Prints "true".


我改进了 科林 · 杰克逊的的答案使用 Dart 2.7 推广方法,使它更优雅。

enum Fruit { apple, banana }


extension EnumParser on String {
Fruit toFruit() {
return Fruit.values.firstWhere(
(e) => e.toString().toLowerCase() == 'fruit.$this'.toLowerCase(),
orElse: () => null); //return null if not found
}
}


main() {
Fruit apple = 'apple'.toFruit();
assert(apple == Fruit.apple); //true
}

这里有一种使用扩展来替代@mbartn 方法的方法,扩展 enum本身而不是 String

更快,但更乏味

// We're adding a 'from' entry just to avoid having to use Fruit.apple['banana'],
// which looks confusing.
enum Fruit { from, apple, banana }


extension FruitIndex on Fruit {
// Overload the [] getter to get the name of the fruit.
operator[](String key) => (name){
switch(name) {
case 'banana': return Fruit.banana;
case 'apple':  return Fruit.apple;
default:       throw RangeError("enum Fruit contains no value '$name'");
}
}(key);
}


void main() {
Fruit f = Fruit.from["banana"];
print("$f is ${f.runtimeType}"); // Outputs: Fruit.banana is Fruit
}

不那么乏味,但是更慢

如果 O (n)表演可以接受,你也可以加入@Collin Jackson 的回答:

// We're adding a 'from' entry just to avoid having to use Fruit.apple['banana']
// which looks confusing.
enum Fruit { from, apple, banana }


extension FruitIndex on Fruit {
// Overload the [] getter to get the name of the fruit.
operator[](String key) =>
Fruit.values.firstWhere((e) => e.toString() == 'Fruit.' + key);
}


void main() {
Fruit f = Fruit.from["banana"];
print("$f is ${f.runtimeType}"); // Outputs: Fruit.banana is Fruit
}

更新:

void main() {
Day monday = Day.values.byName('monday'); // This is all you need
}


enum Day {
monday,
tuesday,
}

老办法:

你的 Enum

enum Day {
monday,
tuesday,
}

添加这个扩展(需要一个 import 'package:flutter/foundation.dart';)

extension EnumEx on String {
Day toEnum() => Day.values.firstWhere((d) => describeEnum(d) == toLowerCase());
}

用法:

void main() {
String s = 'monday'; // String
Day monday = s.toEnum(); // Converted to enum
}

枚举在 Dart 中有太多的限制。扩展方法可以向实例添加方法,但不能添加静态方法。

我真的希望能够做类似 MyType.parse (myString)的事情,所以最终决定使用手动定义的类而不是枚举。对于某些连接,它在功能上几乎等同于枚举,但是可以更容易地修改。

class OrderType {
final String string;
const OrderType._(this.string);


static const delivery = OrderType._('delivery');
static const pickup = OrderType._('pickup');


static const values = [delivery, pickup];


static OrderType parse(String value) {
switch (value) {
case 'delivery':
return OrderType.delivery;
break;
case 'pickup':
return OrderType.pickup;
break;
default:
print('got error, invalid order type $value');
return null;
}
}


@override
String toString() {
return 'OrderType.$string';
}
}


// parse from string
final OrderType type = OrderType.parse('delivery');
assert(type == OrderType.delivery);
assert(type.string == 'delivery');

另一种解决方案:

enum MyEnum {
value1,
value2,
}


extension MyEnumX on MyEnum {
String get asString {
switch (this) {
case MyEnum.value1:
return _keyValue1;
case MyEnum.value2:
return _keyValue2;
}
throw Exception("unsupported type");
}


MyEnum fromString(String string) {
switch (string) {
case _keyValue1:
return MyEnum.value1;
case _keyValue2:
return MyEnum.value2;
}
throw Exception("unsupported type");
}
}


const String _keyValue1 = "value1";
const String _keyValue2 = "value2";


void main() {
String string = MyEnum.value1.asString;
MyEnum myEnum = MyEnum.value1.fromString(string);
}
    enum HttpMethod { Connect, Delete, Get, Head, Options, Patch, Post, Put, Trace }


HttpMethod httpMethodFromString({@required String httpMethodName}) {
assert(httpMethodName != null);


if (httpMethodName is! String || httpMethodName.isEmpty) {
return null;
}


return HttpMethod.values.firstWhere(
(e) => e.toString() == httpMethodName,
orElse: () => null,
);
}

我使用这个函数,我认为它很简单,不需要任何形式的“黑客”:

T enumFromString<T>(List<T> values, String value) {
return values.firstWhere((v) => v.toString().split('.')[1] == value,
orElse: () => null);
}

你可以这样使用它:

enum Test {
value1,
value2,
}


var test = enumFromString(Test.value, 'value2') // Test.value2

Generalization@CopsOnRoad 的解决方案适用于任何枚举类型,

enum Language { en, ar }


extension StringExtension on String {
T toEnum<T>(List<T> list) => list.firstWhere((d) => d.toString() == this);
}


String langCode = Language.en.toString();
langCode.toEnum(Language.values);

当迁移到 null-safe 时,Iterable.firstWhere 方法不再接受 orElse: () = > null。下面是考虑到零安全性的实现:

import 'package:collection/collection.dart';


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


T? enumFromString<T>(String key, List<T> values) => values.firstWhereOrNull((v) => key == enumToString(v!));
enum Fruit { orange, apple }


// Waiting for Dart static extensions
// Issue https://github.com/dart-lang/language/issues/723
// So we will be able to Fruit.parse(...)
extension Fruits on Fruit {
static Fruit? parse(String raw) {
return Fruit.values
.firstWhere((v) => v.asString() == raw, orElse: null);
}


String asString() {
return this.toString().split(".").last;
}
}
...
final fruit = Fruits.parse("orange"); // To enum
final value = fruit.asString(); // To string

简化版:

import 'package:flutter/foundation.dart';


static Fruit? valueOf(String value) {
return Fruit.values.where((e) => describeEnum(e) == value).first;
}

使用方法 describeEnum可以帮助您避免使用拆分来获得元素的名称。

你可以这样做:

extension LanguagePreferenceForString on String {
LanguagePreferenceEntity toLanguagePrerence() {
switch (this) {
case "english":
return LanguagePreferenceEntity.english;
case "turkish":
return LanguagePreferenceEntity.turkish;
default:
return LanguagePreferenceEntity.english;
}
}
}

推广@Pedro Sousa 的优秀解决方案,并使用内置的 describeEnum函数:

extension StringExtension on String {
T toEnum<T extends Object>(List<T> values) {
return values.singleWhere((v) => this.equalsIgnoreCase(describeEnum(v)));
}
}

用法:

enum TestEnum { none, test1, test2 }
final testEnum = "test1".toEnum(TestEnum.values);
expect(testEnum, TestEnum.test1);

从 Dart 版本2.15开始,您可以更方便地使用 .values.byName.values.asNameMap()按名称查找枚举值:

enum Visibility {
visible, collapsed, hidden
}


void main() {
// Both calls output `true`
print(Visibility.values.byName('visible') == Visibility.visible);
print(Visibility.values.asNameMap()['visible'] == Visibility.visible);
}

你可以在官方的 Dart 2.15公告 博客文章中阅读更多关于枚举改进的内容。

使用 Dart 2.15,我们现在可以做到这一点,这是更清洁

  // Convert to string
String fruitName = Fruit.banana.name;


// Convert back to enum
Fruit fruit = Fruit.values.byName(fruitName);
print(fruit); // Fruit.banana
assert(fruit == Fruit.banana);


由于 Dart 2.17,你可以通过增强枚举优雅地解决这个问题。

(见 https://stackoverflow.com/a/71412047/15760132)

只需在您选择的枚举中添加一个静态方法,如下所示:

enum Example {
example1,
example2,
example3;


static Example? fromName(String name) {
for (Example enumVariant in Example.values) {
if (enumVariant.name == name) return enumVariant;
}
return null;
}
}

然后您可以像这样查找枚举:

Example? test = Example.fromName("example1");
    

print(test);    // returns Example.example1

我最常用的简单方法:

 Gender convertToEnum(String value) {
if (value.toLowerCase() == "male") {
return Gender.Male;
} else if (value.toLowerCase() == "female") {
return Gender.Female;
} else {
return Gender.Others;
}
}

调用方法如下:

Gender genderValue = convertToEnum("male");

我的 Enum:

Gender{
Male,
Female,
Others
}

这可以通过称为 Enhanced Enum的新 Dart 2.17特性来实现。

pubspec.yaml中使用此修改省道版本到2.17及以上

environment:
sdk: ">=2.17.0 <3.0.0"

枚举可见度{可见,折叠,隐藏}

enum Visibility{
VISIBLE("Visible"),
COLLAPSED("Collapsed"),
HIDDEN("Hidden");


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

怎么用?

void main() {
const visibility = Visibility.VISIBLE;
print(visibility.text); // Visible
}

枚举日历类型{ JALALI,GREGORIAN } ; 列表 calendarTypeList = [ calendartype.jalali.name , calendartype.gregorian.name ] ; FirstWhere ((element) = > element.name = = keyName) ;

import 'package:collection/collection.dart';


enum Page {
login,
profile,
contact,
}


Widget page(String key){
Page? link = Page.values.firstWhereOrNull((e) => e.toString().split('.').last == key);
switch (link) {
case Page.login:
return LoginView();
case Page.profile:
return const ProfileView();
case Page.contact:
return const ContactView();
default:
return const Empty();
}
}

您可以像下面这样编写 getEnumgetEnum将遍历枚举值并返回与所需字符串相等的第一个枚举。

Sample getEnum(String name) => Sample.values.firstWhere(
(v) => v.name.toLowerCase() == name.toLowerCase(),
orElse: () => throw Exception('Enum value not found.'),
);


enum SampleEnum { first, second, third }

更新

你也可以这样做:

final SampleEnum nameEnum = SampleEnum.values.byName('second');  // SampleEnum.second

用法:

void main() {
print(getEnum('first'));
}

在最新版本的 Dart 中,enum可以支持自定义字段和方法。因此,最现代的方法是为 name/label 编写一个自定义字段和一个静态解析器函数。

例如:

enum Foo {
a('FIRST'),
b('SECOND'),
c('THIRD'),
unknown('UNKNOWN'); // make sure the last element ends in `;`


final String label; // define a private field


const Foo(this.label); // constructor


static Foo fromString(String label) { // static parser method
return values.firstWhere(
(v) => v.label == label,
orElse: () => Foo.unknown,
);
}
}

用法示例:

final foo = Foo.fromString('FIRST'); // will get `Foo.a`