Best Way to Create Enum of Strings

Best way to create enum of strings?

I don't know what you want to do, but this is how I actually translated your example code....

package test;

/**
* @author The Elite Gentleman
*
*/
public enum Strings {
STRING_ONE("ONE"),
STRING_TWO("TWO")
;

private final String text;

/**
* @param text
*/
Strings(final String text) {
this.text = text;
}

/* (non-Javadoc)
* @see java.lang.Enum#toString()
*/
@Override
public String toString() {
return text;
}
}

Alternatively, you can create a getter method for text.

You can now do Strings.STRING_ONE.toString();

How to define an enum with string value?

You can't - enum values have to be integral values. You can either use attributes to associate a string value with each enum value, or in this case if every separator is a single character you could just use the char value:

enum Separator
{
Comma = ',',
Tab = '\t',
Space = ' '
}

(EDIT: Just to clarify, you can't make char the underlying type of the enum, but you can use char constants to assign the integral value corresponding to each enum value. The underlying type of the above enum is int.)

Then an extension method if you need one:

public string ToSeparatorString(this Separator separator)
{
// TODO: validation
return ((char) separator).ToString();
}

Using Enum values as String literals

You can't. I think you have FOUR options here. All four offer a solution but with a slightly different approach...

Option One: use the built-in name() on an enum. This is perfectly fine if you don't need any special naming format.

    String name = Modes.mode1.name(); // Returns the name of this enum constant, exactly as declared in its enum declaration.

Option Two: add overriding properties to your enums if you want more control

public enum Modes {
mode1 ("Fancy Mode 1"),
mode2 ("Fancy Mode 2"),
mode3 ("Fancy Mode 3");

private final String name;

private Modes(String s) {
name = s;
}

public boolean equalsName(String otherName) {
// (otherName == null) check is not needed because name.equals(null) returns false
return name.equals(otherName);
}

public String toString() {
return this.name;
}
}

Option Three: use static finals instead of enums:

public final class Modes {

public static final String MODE_1 = "Fancy Mode 1";
public static final String MODE_2 = "Fancy Mode 2";
public static final String MODE_3 = "Fancy Mode 3";

private Modes() { }
}

Option Four: interfaces have every field public, static and final:

public interface Modes {

String MODE_1 = "Fancy Mode 1";
String MODE_2 = "Fancy Mode 2";
String MODE_3 = "Fancy Mode 3";
}

Associating enums with strings in C#

I like to use properties in a class instead of methods, since they look more enum-like.

Here's an example for a Logger:

public class LogCategory
{
private LogCategory(string value) { Value = value; }

public string Value { get; private set; }

public static LogCategory Trace { get { return new LogCategory("Trace"); } }
public static LogCategory Debug { get { return new LogCategory("Debug"); } }
public static LogCategory Info { get { return new LogCategory("Info"); } }
public static LogCategory Warning { get { return new LogCategory("Warning"); } }
public static LogCategory Error { get { return new LogCategory("Error"); } }

public override string ToString()
{
return Value;
}
}

Pass in type-safe string values as a parameter:

public static void Write(string message, LogCategory logCategory)
{
var log = new LogEntry { Message = message };
Logger.Write(log, logCategory.Value);
}

Usage:

Logger.Write("This is almost like an enum.", LogCategory.Info);

Create an enum with string values

TypeScript 2.4

Now has string enums so your code just works:

enum E {
hello = "hello",
world = "world"
};

/p>

TypeScript 1.8

Since TypeScript 1.8 you can use string literal types to provide a reliable and safe experience for named string values (which is partially what enums are used for).

type Options = "hello" | "world";
var foo: Options;
foo = "hello"; // Okay
foo = "asdf"; // Error!

More : https://www.typescriptlang.org/docs/handbook/advanced-types.html#string-literal-types

Legacy Support

Enums in TypeScript are number based.

You can use a class with static members though:

class E
{
static hello = "hello";
static world = "world";
}

You could go plain as well:

var E = {
hello: "hello",
world: "world"
}

Update:
Based on the requirement to be able to do something like var test:E = E.hello; the following satisfies this:

class E
{
// boilerplate
constructor(public value:string){
}

toString(){
return this.value;
}

// values
static hello = new E("hello");
static world = new E("world");
}

// Sample usage:
var first:E = E.hello;
var second:E = E.world;
var third:E = E.hello;

console.log("First value is: "+ first);
console.log(first===third);

How to get an enum value from a string value in Java

Yes, Blah.valueOf("A") will give you Blah.A.

Note that the name must be an exact match, including case: Blah.valueOf("a") and Blah.valueOf("A ") both throw an IllegalArgumentException.

The static methods valueOf() and values() are created at compile time and do not appear in source code. They do appear in Javadoc, though; for example, Dialog.ModalityType shows both methods.

A proper way of associating enums with strings

The only advantage with the former is that it's backwards-compatible with ancient C standards.

Apart from that, the latter alternative is superior, as it ensures data integrity even if the enum is modified or items change places. However, it should be completed with a check to ensure that the number of items in the enum corresponds with the number of items in the look-up table:

typedef enum {
STRING_HELLO,
STRING_WORLD,
STRING_N // counter
} string_enum_type;

const char *string_enumerations[] = {
[STRING_HELLO] = "Hello",
[STRING_WORLD] = "World"
};

_Static_assert(sizeof string_enumerations/sizeof *string_enumerations == STRING_N,
"string_enum_type does not match string_enumerations");

The above is the best method for a simple "enum - lookup table" coupling. Another option would be to use structs, but that's more suitable for more complex data types.


And finally, more as a side-note, the 3rd version would be to use "X macros". This is not recommended unless you have specialized requirements regarding code repetition and maintenance. I'll include it here for completeness, but I don't recommend it in the general case:

#define STRING_LIST          \
/* index str */ \
X(STRING_HELLO, "Hello") \
X(STRING_WORLD, "World")

typedef enum {
#define X(index, str) index,
STRING_LIST
#undef X
STRING_N // counter
} string_enum_type;

const char *string_enumerations[] = {
#define X(index, str) [index] = str,
STRING_LIST
#undef X
};

_Static_assert(sizeof string_enumerations/sizeof *string_enumerations == STRING_N,
"string_enum_type does not match string_enumerations");

How to enumerate an enum with String type?

Swift 4.2+

Starting with Swift 4.2 (with Xcode 10), just add protocol conformance to CaseIterable to benefit from allCases. To add this protocol conformance, you simply need to write somewhere:

extension Suit: CaseIterable {}

If the enum is your own, you may specify the conformance directly in the declaration:

enum Suit: String, CaseIterable { case spades = "♠"; case hearts = "♥"; case diamonds = "♦"; case clubs = "♣" }

Then the following code will print all possible values:

Suit.allCases.forEach {
print($0.rawValue)
}


Compatibility with earlier Swift versions (3.x and 4.x)

If you need to support Swift 3.x or 4.0, you may mimic the Swift 4.2 implementation by adding the following code:

#if !swift(>=4.2)
public protocol CaseIterable {
associatedtype AllCases: Collection where AllCases.Element == Self
static var allCases: AllCases { get }
}
extension CaseIterable where Self: Hashable {
static var allCases: [Self] {
return [Self](AnySequence { () -> AnyIterator<Self> in
var raw = 0
var first: Self?
return AnyIterator {
let current = withUnsafeBytes(of: &raw) { $0.load(as: Self.self) }
if raw == 0 {
first = current
} else if current == first {
return nil
}
raw += 1
return current
}
})
}
}
#endif

Convert a string to an enum in C#

In .NET Core and .NET Framework ≥4.0 there is a generic parse method:

Enum.TryParse("Active", out StatusEnum myStatus);

This also includes C#7's new inline out variables, so this does the try-parse, conversion to the explicit enum type and initialises+populates the myStatus variable.

If you have access to C#7 and the latest .NET this is the best way.

Original Answer

In .NET it's rather ugly (until 4 or above):

StatusEnum MyStatus = (StatusEnum) Enum.Parse(typeof(StatusEnum), "Active", true);

I tend to simplify this with:

public static T ParseEnum<T>(string value)
{
return (T) Enum.Parse(typeof(T), value, true);
}

Then I can do:

StatusEnum MyStatus = EnumUtil.ParseEnum<StatusEnum>("Active");

One option suggested in the comments is to add an extension, which is simple enough:

public static T ToEnum<T>(this string value)
{
return (T) Enum.Parse(typeof(T), value, true);
}

StatusEnum MyStatus = "Active".ToEnum<StatusEnum>();

Finally, you may want to have a default enum to use if the string cannot be parsed:

public static T ToEnum<T>(this string value, T defaultValue) 
{
if (string.IsNullOrEmpty(value))
{
return defaultValue;
}

T result;
return Enum.TryParse<T>(value, true, out result) ? result : defaultValue;
}

Which makes this the call:

StatusEnum MyStatus = "Active".ToEnum(StatusEnum.None);

However, I would be careful adding an extension method like this to string as (without namespace control) it will appear on all instances of string whether they hold an enum or not (so 1234.ToString().ToEnum(StatusEnum.None) would be valid but nonsensical) . It's often be best to avoid cluttering Microsoft's core classes with extra methods that only apply in very specific contexts unless your entire development team has a very good understanding of what those extensions do.

Enum from String

Using mirrors you could force some behaviour. I had two ideas in mind. Unfortunately Dart does not support typed functions:

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

Outputs:

Visibility.COLLAPSED
Visibility.HIDDEN


Related Topics



Leave a reply



Submit