Wpf Databinding Not Updating

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.

Best way to parse a string to an enum

Answer based on the comment by @super

var (
capabilitiesMap = map[string]Capability{
"read": Read,
"create": Create,
"update": Update,
"delete": Delete,
"list": List,
}
)
func ParseString(str string) (Capability, bool) {
c, ok := capabilitiesMap[strings.ToLower(str)]
return c, ok
}

Parse string as Typescript Enum

TS4.1 ANSWER:

type UsedProductType = `${UsedProduct}`;

PRE TS-4.1 ANSWER:

TypeScript doesn't make this easy for you so the answer isn't a one-liner.

An enum value like UsedProduct.Yes is just a string or number literal at runtime (in this case, the string "yes"), but at compile time it is treated as a subtype of the string or number literal. So, UsedProduct.Yes extends "yes" is true. Unfortunately, given the type UsedProduct.Yes, there is no programmatic way to widen the type to "yes"... or, given the type UsedProduct, there is no programmatic way to widen it to "yes" | "no" | "unknown". The language is missing a few features which you'd need to do this.

There is a way to make a function signature which behaves like parseUsedProduct, but it uses generics and conditional types to achieve this:

type Not<T> = [T] extends [never] ? unknown : never
type Extractable<T, U> = Not<U extends any ? Not<T extends U ? unknown : never> : never>

declare function asEnum<E extends Record<keyof E, string | number>, K extends string | number>(
e: E, k: K & Extractable<E[keyof E], K>
): Extract<E[keyof E], K>

const yes = asEnum(UsedProduct, "yes"); // UsedProduct.yes
const no = asEnum(UsedProduct, "no"); // UsedProduct.no
const unknown = asEnum(UsedProduct, "unknown"); // UsedProduct.unknown
const yesOrNo = asEnum(UsedProduct,
Math.random()<0.5 ? "yes" : "no"); // UsedProduct.yes | UsedProduct.no

const unacceptable = asEnum(UsedProduct, "oops"); // error

Basically it takes an enum object type E and a string-or-number type K, and tries to extract the property value(s) of E that extend K. If no values of E extend K (or if K is a union type where one of the pieces doesn't correspond to any value of E), the compiler will give an error. The specifics of how Not<> and Extractable<> work are available upon request.

As for the implementation of the function you will probably need to use a type assertion. Something like:

function asEnum<E extends Record<keyof E, string | number>, K extends string | number>(
e: E, k: K & Extractable<E[keyof E], K>
): Extract<E[keyof E], K> {
// runtime guard, shouldn't need it at compiler time
if (Object.values(e).indexOf(k) < 0)
throw new Error("Expected one of " + Object.values(e).join(", "));
return k as any; // assertion
}

That should work. In your specific case we can hardcode UsedProduct:

type Not<T> = [T] extends [never] ? unknown : never
type Extractable<T, U> = Not<U extends any ? Not<T extends U ? unknown : never> : never>
function parseUsedProduct<K extends string | number>(
k: K & Extractable<UsedProduct, K>
): Extract<UsedProduct, K> {
if (Object.values(UsedProduct).indexOf(k) < 0)
throw new Error("Expected one of " + Object.values(UsedProduct).join(", "));
return k as any;
}

const yes = parseUsedProduct("yes"); // UsedProduct.yes
const unacceptable = parseUsedProduct("oops"); // error

Hope that helps. Good luck!

What is the best way to strictly parse string to enum?

You can do this yourself using Enum.GetValues(Type enumType).

public static MyEnum? ParseEnum(string input)
{
return (MyEnum?) Enum.GetValues(typeof(MyEnum)).OfType<object>()
.FirstOrDefault(v => v.ToString() == input);
}

Adding support for integers you can also add.

public static MyEnum? ParseEnum(string input)
{
int value;
var isInt = int.TryParse(input, out value);
return (MyEnum?) Enum.GetValues(typeof(MyEnum)).OfType<object>()
.FirstOrDefault(v => v.ToString() == input
|| (isInt & (int)v == value));
}

Since I am not 100% sure about your last requirements I add one that includes the name as well and is more generic

public static T? ParseEnum<T>(string input)
where T : struct
{
int value;
var isInt = int.TryParse(input, out value);
return (T?)Enum.GetValues(typeof(T)).OfType<object>()
.FirstOrDefault(v => v.ToString() == typeof(T).Name + input
|| (isInt & (int)v == value));
}

How do I convert a string to enum in TypeScript?

Enums in TypeScript 0.9 are string+number based. You should not need type assertion for simple conversions:

enum Color{
Red, Green
}

// To String
var green: string = Color[Color.Green];

// To Enum / number
var color : Color = Color[green];

Try it online

I have documention about this and other Enum patterns in my OSS book : https://basarat.gitbook.io/typescript/type-system/enums

Parse string to enum type

What about something like:

public static class EnumUtils
{
public static Nullable<T> Parse<T>(string input) where T : struct
{
//since we cant do a generic type constraint
if (!typeof(T).IsEnum)
{
throw new ArgumentException("Generic Type 'T' must be an Enum");
}
if (!string.IsNullOrEmpty(input))
{
if (Enum.GetNames(typeof(T)).Any(
e => e.Trim().ToUpperInvariant() == input.Trim().ToUpperInvariant()))
{
return (T)Enum.Parse(typeof(T), input, true);
}
}
return null;
}
}

Used as:

MyEnum? value = EnumUtils.Parse<MyEnum>("foo");

(Note: old version used try/catch around Enum.Parse)

Convert string into Enum

Rohit,

The value of preferenceKey could not easily be converted to an enum as you would expect. The strings could be parsed using Enum.Parse() method however the enum names must be different than what you have in the database. The problems are

  1. Your string starts with a number
  2. Your string contains the - characters

Now that being said you could design a different approach to your naming convetion of an enum as an example (I dont like this example personally but might work).

First define a new attribute called EnumName this attribute will be used to decorate your enums with the name that you expect from the database. Such as

[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public sealed class EnumNameAttribute : Attribute
{
readonly string name;

public EnumNameAttribute(string name)
{
this.name = name;
}

public string Name { get { return this.name; } }
}

The next step will be to define your enums (what I dont like is the names)

public enum Preferences
{
[EnumName("6E-SF-TYPE")]
SIX_E_SF_TYPE = 0,
[EnumName("6E-SF-VALUE")]
SIX_E_SF_VALUE = 1
}

Simple enough, we have our enum with each item decorated with the EnumName attribute. The next step will be to create a method to parse the enum based on the string from the database.

public static class EnumNameParser
{
public static Preferences ParseString(string enumString)
{
var members = typeof(Preferences).GetMembers()
.Where(x => x.GetCustomAttributes(typeof(EnumNameAttribute), false).Length > 0)
.Select(x =>
new
{
Member = x,
Attribute = x.GetCustomAttributes(typeof(EnumNameAttribute), false)[0] as EnumNameAttribute
});

foreach(var item in members)
{
if (item.Attribute.Name.Equals(enumString))
return (Preferences)Enum.Parse(typeof(Preferences), item.Member.Name);
}

throw new Exception("Enum member " + enumString + " was not found.");
}
}

This method simply takes an input stream, evaluates all the EnumNameAttributes and returns the first match (or exception if not found).

Then this can be called such as.

string enumString = "6E-SF-TYPE";
var e = EnumNameParser.ParseString(enumString);

Now if you want to take the enum and get the name for the database you can extend your helper method with

public static string GetEnumName(this Preferences preferences)
{
var memInfo = typeof(Preferences).GetMember(preferences.ToString());
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(EnumNameAttribute), false);
if (attrs != null && attrs.Length > 0)
return ((EnumNameAttribute)attrs[0]).Name;
}
throw new Exception("No enum name attribute defined");

}

Parsing a string to a generic Enum type

You can easy parse string to your enum instance. But you should define enum types

static void Main(string[] args)
{
string worker = "Small_Motivator";

var provider = new EnumProvider();

var enumValue = provider.GetByValue(worker);
}

public class EnumProvider
{
public object GetByValue(string sourceStr)
{
var enumTypes = new[]
{
typeof(Enums.Motivators),
typeof(Enums.Movers),
typeof(Enums.Reactors)
};

foreach (var type in enumTypes)
{
var enumValues = Enum.GetValues(type)
.Cast<object>()
.Select(x => x.ToString())
.ToArray();

if (enumValues.Any(x => x == sourceStr))
{
return Enum.Parse(type, sourceStr);
}
}

throw new ArgumentException($"{sourceStr} not supported");
}
}

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