Перечисление из строки

У меня есть 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 я have, есть ли способ записать ту же функцию, где в качестве параметра используется тип Enum? я попытался, но я понял, что я не могу использовать Enum.

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

     }

Ответ 1

Использование зеркал может привести к некоторому поведению. У меня было две идеи. К сожалению, 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

Зеркала не всегда доступны, но, к счастью, они вам не нужны. Это достаточно компактно и должно делать то, что вы хотите.

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

Ответ 3

Решение Collin Jackson не работает для меня, потому что Dart строит перечисления в EnumName.value а не просто value (например, Fruit.apple), и я пытался преобразовать значение строки, например apple а не преобразовывать Fruit.apple из get -идти.

Имея это в виду, это мое решение для перечисления из строковой задачи

enum Fruit {apple, banana}

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

Ответ 4

Есть несколько пакетов перечислений, которые позволили мне получить только строку перечисления, а не строку 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
}  

Ответ 5

У меня была такая же проблема с созданием объектов из JSON. В значениях JSON есть строки, но я хотел, чтобы enum проверял правильность значения. Я написал этот помощник, который работает с любым перечислением, а не с указанным:

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 не является типом в Дарт и рассматривается как сложный процесс. Класс используется исключительно для кеширования.

Ответ 6

Мое решение идентично решению Rob C, но без интерполяции строк:

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

Ответ 7

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

Может быть, кто-то найдет это полезным...

Ответ 8

Вот функция, которая преобразует данную строку в тип enum:

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

А вот как вы конвертируете данный тип перечисления в строку:

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

Ответ 9

Я думаю, что мой подход немного отличается, но в некоторых случаях может быть более удобным. Наконец, у нас есть 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, потому что это приводит к тому, что сгенерированные пакеты будут очень большими.

Ответ 10

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

Flutter изначально не поддерживает enum со значениями, однако мне удалось разработать вспомогательный пакет Vnum с использованием реализации класса и отражателей для решения этой проблемы.

Обратитесь в хранилище:

https://github.com/AmirKamali/Flutter_Vnum

Чтобы ответить на вашу проблему с помощью 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 есть больше документации, надеюсь, она вам поможет.