来自String的枚举

时间:2014-12-28 04:53:40

标签: dart

我有一个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){

     }

22 个答案:

答案 0 :(得分:36)

镜子并不总是可用,但幸运的是你不需要它们。这是相当紧凑的,应该做你想要的。

enum Fruit { apple, banana }

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

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

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

<强>修正: 正如评论部分中@frostymarvelous所提到的,这是正确的实现:

Fruit f = Fruit.values.firstWhere((e) => e.toString() == 'Fruit.' + str);

答案 1 :(得分:8)

使用镜像可以强制某些行为。我有两个想法。不幸的是,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

答案 2 :(得分:7)

这是@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
}

答案 3 :(得分:5)

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

答案 4 :(得分:3)

您的枚举

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
}

答案 5 :(得分:1)

我使用Dart 2.7 Collin Jackson's改进了Extension Methods答案,使其更加美观。

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
}

答案 6 :(得分:1)

将@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);

答案 7 :(得分:1)

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

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

答案 8 :(得分:1)

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

PS。我在这里没有故意使用类型。 枚举不在Dart中输入,将其视为使事情变得复杂。类仅用于缓存目的。

答案 9 :(得分:1)

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

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
}  

答案 10 :(得分:0)

我认为我的方法略有不同,但在某些情况下可能更方便。最后,我们有枚举类型的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中阻止了镜像,因为这会导致生成的程序包很大。

答案 11 :(得分:0)

我在一个项目中遇到了同样的问题,现有的解决方案不是很干净,并且不支持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仓库中还有更多文档,希望对您有所帮助。

答案 12 :(得分:0)

这是如此复杂,我做了一个简单的库就可以完成工作:

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
}

答案 13 :(得分:0)

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

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

这是将给定枚举类型转换为字符串的方法:

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

答案 14 :(得分:0)

@Collin Jackson对IMO的回答很好。在发现此问题之前,我曾使用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);
}

也许有人会觉得这很有用...

答案 15 :(得分:0)

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

答案 16 :(得分:0)

另一种变体,可能如何解决:

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

答案 17 :(得分:0)

    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,
    );
  }

答案 18 :(得分:0)

我使用此功能,我认为它很简单,不需要任何“ hack”:

composer.lock

您可以像这样使用它:

combiner

答案 19 :(得分:0)

当迁移到空安全时,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!));

答案 20 :(得分:0)

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

答案 21 :(得分:0)

简化版:

import 'package:flutter/foundation.dart';

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

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