J'ai un Enum
et une fonction pour le créer à partir d'un String
parce que je n'ai pas pu trouver un moyen intégré de le faire
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');
mais il semble que je doive réécrire cette fonction pour chaque Enum que j'ai, y a-t-il un moyen d'écrire la même fonction là où elle prend le type Enum comme paramètre? J'ai essayé mais j'ai compris que je ne pouvais pas lancer de casting sur Enum.
//is something with the following signiture actually possible?
dynamic enumFromString(Type enumType,String value){
}
En utilisant des miroirs, vous pourriez forcer un certain comportement. J'avais deux idées en tête. Malheureusement, Dart ne prend pas en charge les fonctions tapées:
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);
}
Les sorties:
Visibility.COLLAPSED
Visibility.HIDDEN
Les miroirs ne sont pas toujours disponibles, mais heureusement, vous n'en avez pas besoin. C'est assez compact et devrait faire ce que vous voulez.
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
Fix: Comme mentionné par @frostymarvelous dans la section commentaires, c'est une implémentation correcte:
Fruit f = Fruit.values.firstWhere((e) => e.toString() == 'Fruit.' + str);
Ma solution est identique à la solution de Rob C mais sans interpolation de chaîne:
T getEnumFromString<T>(Iterable<T> values, String value) {
return values.firstWhere((type) => type.toString().split(".").last == value,
orElse: () => null);
}
Collin Jackson la solution n'a pas fonctionné pour moi car Dart stringifie les énumérations en EnumName.value
plutôt que simplement value
(par exemple, Fruit.Apple
), et j'essayais de convertir la valeur de chaîne comme Apple
plutôt que de convertir Fruit.Apple
dès le départ.
Dans cet esprit, c'est ma solution pour le problème d'énumération de chaîne
enum Fruit {Apple, banana}
Fruit getFruitFromString(String fruit) {
fruit = 'Fruit.$fruit';
return Fruit.values.firstWhere((f)=> f.toString() == fruit, orElse: () => null);
}
Tout cela est tellement compliqué que j'ai créé une bibliothèque simple qui fait le travail:
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
}
J'ai amélioré Collin Jackson réponse en utilisant Dart 2.7 Méthodes d'extension pour le rendre plus élégant.
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
}
Il existe quelques packages enums qui m'ont permis d'obtenir uniquement la chaîne enum plutôt que la chaîne type.value (Apple, pas Fruit.Apple).
https://pub.dartlang.org/packages/built_value (c'est plus à jour)
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
}
@Collin Jackson a une très bonne réponse OMI. J'avais utilisé une boucle for-in pour obtenir un résultat similaire avant de trouver cette question. Je passe définitivement à l'utilisation de la méthode firstWhere.
En développant sa réponse, voici ce que j'ai fait pour supprimer le type des chaînes de valeur:
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);
}
Peut-être que quelqu'un trouvera cela utile ...
J'ai eu le même problème avec la construction d'objets à partir de JSON. En JSON, les valeurs sont des chaînes, mais je voulais que l'énumération valide si la valeur est correcte. J'ai écrit cet assistant qui fonctionne avec n'importe quelle énumération, pas une spécifiée:
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();
Usage:
enumHelper.str2enum(Category.values, json['category']);
PS. Je n'ai pas utilisé de types exprès ici. enum n'est pas du type dans Dart et le traiter comme on complique les choses. La classe est utilisée uniquement à des fins de mise en cache.
J'ai eu le même problème dans l'un de mes projets et les solutions existantes n'étaient pas très propres et ne prenaient pas en charge les fonctionnalités avancées telles que la sérialisation/désérialisation json.
Flutter ne prend actuellement pas en charge enum enum avec des valeurs, cependant, j'ai réussi à développer un package d'assistance Vnum
en utilisant l'implémentation de classe et de réflecteurs pour surmonter ce problème.
Adresse du référentiel:
https://github.com/AmirKamali/Flutter_Vnum
Pour répondre à votre problème en utilisant Vnum
, vous pouvez implémenter votre code comme ci-dessous:
@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);
}
Vous pouvez l'utiliser comme:
var visibility = Visibility('COLLAPSED');
print(visibility.value);
Il y a plus de documentation dans le dépôt github, j'espère que cela vous aidera.
Voici la fonction qui convertit une chaîne donnée en type énumération:
EnumType enumTypeFromString(String typeString) => EnumType.values
.firstWhere((type) => type.toString() == "EnumType." + typeString);
Et voici comment convertir un type d'énumération donné en chaîne:
String enumTypeToString(EnumType type) => type.toString().split(".")[1];
Voici une manière alternative à l'approche de @ mbartn en utilisant des extensions, en étendant le enum
lui-même au lieu de 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
}
Si les performances de O(n) sont acceptables, vous pouvez également incorporer la réponse de @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
}
Je pense que mon approche est légèrement différente, mais pourrait être plus pratique dans certains cas. Enfin, nous avons parse et tryParse pour les types d'énumération:
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;
}
}
EDIT: cette approche ne fonctionne PAS dans les applications Flutter, par défaut les miroirs sont bloqués dans Flutter car cela fait que les packages générés sont très volumineux.