【问题标题】:Enum from String来自字符串的枚举
【发布时间】:2014-12-28 04:53:39
【问题描述】:

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

     }

【问题讨论】:

  • 是的,似乎没有什么好的方法可以做到这一点。在我看到他们是如何实现它之前,我一直期待着这个特性被包含在 Dart 中。现在,我通常无法证明使用它的合理性。
  • @montyr75 想通了,希望它会很快改变
  • 如果你需要类似的东西,最好使用旧式枚举stackoverflow.com/a/13901969/217408

标签: dart


【解决方案1】:

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

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 在 cmets 部分中提到的,这是正确的实现:

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

【讨论】:

  • 警告!您的字符串必须采用 "Fruit.banana" 的形式才能工作
  • 你也可以使用:describeEnumpackage:flutter/foundation.dart 包中的Fruit.String str = "banana"; Fruit f = Fruit.values.firstWhere((e) => describeEnum(e) == str);
  • Dart 似乎在它的设计中做的大部分事情都是正确的,我很惊讶字符串转换并没有更优雅;也许将来他们会为此引入一些语法糖?
  • 覆盖意外的字符串值:firstWhere((e) => e.toString() == 'Fruit.' + str, orElse: () => null);
  • 从 Dart 2.15 开始,我们现在可以将枚举值名称作为字符串访问(即:Fruit.apple.name == 'apple')并写入:Fruit f = Fruit.values.firstWhere((e) => e.name == str); 而不是 Fruit f = Fruit.values.firstWhere((e) => e.toString() == 'Fruit.' + str);
【解决方案2】:

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

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

使用来自collection packagefirstWhereOrNull() 的空安全示例

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

【讨论】:

    【解决方案3】:

    这太复杂了,我做了一个简单的库来完成工作:

    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 库中改进的枚举

    我们在 dart:core 库(语言问题 #1511)中为枚举 API 添加了许多便利功能。您现在可以使用 .name 获取每个枚举值的字符串值:

    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".
    
    

    【讨论】:

      【解决方案4】:

      从 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 官方公告blog post 中阅读有关其他枚举改进的更多信息。

      【讨论】:

      • 考虑到 Dart 的新版本,这应该是正确的答案。
      • @FPGA,您能否考虑接受这个答案作为正确答案,因为它是 Dart 版本的最新版本?
      【解决方案5】:

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

      【讨论】:

      • 此代码需要审核,在当前版本中似乎不起作用
      • 你可以在 dart 中键入函数 imTyped(String Function(String a, int b) generatedString){ String newString = generatedString("hello", 5);返回新字符串; }
      【解决方案6】:

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

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

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

      【讨论】:

      • @Collin Jackson 解决方案在您使用枚举 toString() 将枚举转换为字符串时有效。 toString() 返回“Fruit.apple”
      • 要仅返回“apple”,请尝试以下操作: Fruit.apple.toString().split(".")[1];
      【解决方案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
      }
      

      【讨论】:

      • Fruit.from["banana"] -> 这是从哪里来的? from 无法识别。
      • @uzu from 是添加到枚举中的条目,以减少阅读代码的混乱。您不必添加它,但您必须使用另一个现有密钥。例如,如果您选择使用apple,它将是:Fruit.apple['banana']
      • 我喜欢你的解决方案,但要明确 from 并不是一个真正可用的枚举,使用 _from 将其设为私有可能是个好主意。 (这限制了它对这个文件/类的使用,但在很多情况下这正是你想要的)
      【解决方案8】:

      我用这个函数,我觉得很简单,不需要任何“hack”:

      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
      

      【讨论】:

      • 这应该是公认的答案。干净、优雅且不使用反射
      【解决方案9】:

      你的枚举

      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
      }
      

      【讨论】:

      • 将枚举转换为字符串。它能够将字符串转换为枚举吗?问题是关于那个。
      【解决方案10】:

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

      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
      }
      

      【讨论】:

        【解决方案11】:

        简化版:

        import 'package:flutter/foundation.dart';
        
        static Fruit? valueOf(String value) {
            return Fruit.values.where((e) => describeEnum(e) == value).first;
        }
        

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

        【讨论】:

          【解决方案12】:

          有几个枚举包允许我只获取枚举字符串而不是 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
          }  
          

          【讨论】:

            【解决方案13】:

            我在从 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。我在这里没有故意使用类型。 enum 不是 Dart 中的类型,将其视为一个会使事情变得复杂。类仅用于缓存目的。

            【讨论】:

              【解决方案14】:

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

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

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

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

              【讨论】:

                【解决方案15】:

                概括@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);
                

                【讨论】:

                  【解决方案16】:

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

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

                  【讨论】:

                    【解决方案17】:

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

                    【讨论】:

                      【解决方案18】:
                      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
                      

                      【讨论】:

                        【解决方案19】:

                        概括@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);
                        

                        【讨论】:

                          【解决方案20】:

                          有了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);
                          
                          

                          【讨论】:

                            【解决方案21】:

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

                            【讨论】:

                              【解决方案22】:

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

                              我真的很想能够做类似 MyType.parse(myString) 的事情,所以最终决定使用手动定义的类而不是枚举。通过一些布线,它在功能上几乎等同于 enum,但可以更轻松地修改。

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

                              【讨论】:

                                【解决方案23】:

                                另一种变体,如何解决:

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

                                【讨论】:

                                  【解决方案24】:
                                      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,
                                      );
                                    }
                                  

                                  【讨论】:

                                    【解决方案25】:

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

                                    【讨论】:

                                      【解决方案26】:

                                      你可以这样做:

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

                                      【讨论】:

                                        猜你喜欢
                                        • 1970-01-01
                                        • 2011-06-10
                                        • 2010-09-23
                                        • 1970-01-01
                                        • 2019-07-04
                                        • 1970-01-01
                                        • 2022-06-11
                                        • 1970-01-01
                                        • 1970-01-01
                                        相关资源
                                        最近更新 更多