I'm writing some Enum functionality, and have the following:
public static T ConvertStringToEnumValue<T>(string valueToConvert,
bool isCaseSensitive)
{
if (String.IsNullOrWhiteSpace(valueToConvert))
return (T)typeof(T).TypeInitializer.Invoke(null);
valueToConvert = valueToConvert.Replace(" ", "");
if (typeof(T).BaseType.FullName != "System.Enum" &&
typeof(T).BaseType.FullName != "System.ValueType")
{
throw new ArgumentException("Type must be of Enum and not " +
typeof (T).BaseType.FullName);
}
if (typeof(T).BaseType.FullName == "System.ValueType")
{
return (T)Enum.Parse(Nullable.GetUnderlyingType(typeof(T)),
valueToConvert, !isCaseSensitive);
}
return (T)Enum.Parse(typeof(T), valueToConvert, !isCaseSensitive);
}
I now call this with the following:
EnumHelper.ConvertStringToEnumValue<Enums.Animals?>("Cat");
This works as expected. However, if I run this:
EnumHelper.ConvertStringToEnumValue<Enums.Animals?>(null);
it breaks with the error that the TypeInitializer is null.
Does anyone know how to solve this?
Thanks all!
try
if (String.IsNullOrWhiteSpace(valueToConvert))
return default(T);
I have an different approach, using extension and generics.
public static T ToEnum<T>(this string s) {
if (string.IsNullOrWhiteSpace(s))
return default(T);
s = s.Replace(" ", "");
if (typeof(T).BaseType.FullName != "System.Enum" &&
typeof(T).BaseType.FullName != "System.ValueType") {
throw new ArgumentException("Type must be of Enum and not " + typeof(T).BaseType.FullName);
}
if (typeof(T).BaseType.FullName == "System.ValueType")
return (T)Enum.Parse(Nullable.GetUnderlyingType(typeof(T)), s, true);
return (T)Enum.Parse(typeof(T), s, true);
}
Use like this...
Gender? g = "Female".ToEnum<Gender?>();
This one gets the job done and it also looks pretty. Hope it helps!
/// <summary>
/// <para>More convenient than using T.TryParse(string, out T).
/// Works with primitive types, structs, and enums.
/// Tries to parse the string to an instance of the type specified.
/// If the input cannot be parsed, null will be returned.
/// </para>
/// <para>
/// If the value of the caller is null, null will be returned.
/// So if you have "string s = null;" and then you try "s.ToNullable...",
/// null will be returned. No null exception will be thrown.
/// </para>
/// <author>Contributed by Taylor Love (Pangamma)</author>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="p_self"></param>
/// <returns></returns>
public static T? ToNullable<T>(this string p_self) where T : struct
{
if (!string.IsNullOrEmpty(p_self))
{
var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));
if (converter.IsValid(p_self)) return (T)converter.ConvertFromString(p_self);
if (typeof(T).IsEnum) { T t; if (Enum.TryParse<T>(p_self, out t)) return t;}
}
return null;
}
https://github.com/Pangamma/PangammaUtilities-CSharp/tree/master/src/StringExtensions
this is what I using, maybe useful for someone!
public static class EnumExtension
{
public static TEnum? ParseOrDefault<TEnum>(string value, bool ignoreCase = false, TEnum? #default = default)
where TEnum : struct, Enum
{
TEnum? #enum;
try { #enum = (TEnum)Enum.Parse(typeof(TEnum), value, ignoreCase); }
catch { #enum = #default; }
return #enum;
}
}
Related
To try make it short, I have created the following enum
public enum Frequency
{
[Description("Monthly")]
Monthly,
[Description("Quarterly")]
Quarterly,
[Description("N/A")]
NA
}
I then have a combo box using the same description strings.
When I select a new selection, specifically the "N/A" one, it fails to read it correctly.
The code that I am using to search for the right enum based on the passed in string is...
/// Returns an enum of the specified type that matches the string value passed in. Note this does ignore case
<param name="value">The string value.</param>
public static TEnum GetEnum<TEnum>(string value)
{
if (string.IsNullOrEmpty(value))
{
// Default not set value name
value = "None";
}
return (TEnum)System.Enum.Parse(typeof(TEnum), value.Replace(" ", string.Empty), true);
}
So when the value = "N/A", I get the following error..
"An unhandled exception of type 'System.ArgumentException' occurred in mscorlib.dll"
Additional information: Requested value 'N/A' was not found."
I can't seem to understand why this could be happening. There is another, pre-existing combo box where the decription also contains a '/' character and the same error happens. So its not something I have done wrong, it seems, but just the behavior of the enum string checking.
Any insight into why this is causing problems would be incredibly appreciated. :)
Thanks!
EDIT:
More information..
So this is the code that triggers the enum search..
if (this.FrequencyCombo.SelectedItem != null && !this.FrequencyCombo.SelectedItem.Equals(Utilities.GetDescription(currentLoan.Frequency)))
{
currentLoan.Frequency = Utilities.GetEnum<Frequency>(this.FrequencyCombo.SelectedItem.ToString());
}
Replace your method with the following, you are trying to match the description with the value:
/// <summary>
/// Gets the Enum from a matching description value
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="description"></param>
/// <returns></returns>
public static T GetValueFromDescription<T>(string description)
{
var type = typeof(T);
if (!type.IsEnum) throw new InvalidOperationException();
foreach (var field in type.GetFields())
{
var attribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attribute != null)
{
if (attribute.Description == description)
{
return (T)field.GetValue(null);
}
}
else
{
if (field.Name == description)
{
return (T)field.GetValue(null);
}
}
}
throw new ArgumentException("Enum description not found.", "Description");
}
Enum.Parse takes a string argument representing the name of the enum value, not the description, i.e. what Enum.ToString() returns. You'd need a method that finds an enum value by description, like this one:
public static TEnum GetEnumByDescription<TEnum>(string desc) where TEnum : struct
{
if(string.IsNullOrEmpty(desc))
{
return default(TEnum);
}
foreach(var field in typeof(TEnum).GetFields(BindingFlags.Static | BindingFlags.Public))
{
var attr = (DescriptionAttribute)field.GetCustomAttribute(typeof(DescriptionAttribute));
if(attr != null && attr.Description == desc)
{
return (TEnum)field.GetValue(null);
}
}
return default(TEnum);
}
Something like this will do. It lists all members, gets their descriptions and compares them to the string you're looking for.
public static T GetByDescription<T>(string description) {
return Enum.GetValues(typeof(T))
.OfType<T>()
.First(f => {
var memberInfo = typeof(T).GetMember(f.ToString());
var desc = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
return desc.Length == 1 && ((DescriptionAttribute)desc[0]).Description.Equals(description, StringComparison.InvariantCultureIgnoreCase);
});
}
How to use it:
GetByDescription<Frequency>("Monthly");
GetByDescription<Frequency>("N/A");
Related: Getting attributes of Enum's value
I have the following node in my web.config
<parameter value="100" type="System.Int64, mscorlib" />
which is read into the following ConfigurationProperty
public class ParameterElement : ConfigurationElement
{
[ConfigurationProperty("type", IsRequired = false, DefaultValue = "System.String, mscorlib")]
[TypeConverter(typeof (TypeNameConverter))]
public Type Type
{
get { return (Type) this["type"]; }
set { this["type"] = value; }
}
[ConfigurationProperty("value", IsRequired = true)]
public object Value
{
get { return ... ? }
set { this["value"] = value; }
}
}
This is correctly establishing the Type that I've set on the node, but how can I return the value in that type? Everything that I've tried returns the following exception:
Unable to find a converter that supports conversion to/from string for the property 'value' of type 'Object'.
It may be too late, but I had the same need and i found a solution.
First you need to add a TypeConverter on Value to deserialize the configuration, I choose StringConverter but we could implement a XMLConverter or a JSONConverter.
Second, you must parse the string, xml or json
[TypeConverter(typeof(StringConverter))]
[ConfigurationProperty(ValueKey, IsRequired = true)]
public object Value
{
get { return this[ValueKey].ConvertTo(this.Type); }
set { this[ValueKey] = value; }
}
Here my extension method to parse the string
/// <summary>
/// Convert an object to a specific type with a support of string parsing if needed
/// </summary>
/// <param name="input">Object to convert</param>
/// <param name="type">Type of converted object</param>
/// <returns>Object converted</returns>
public static object ConvertTo(this object input, Type type)
{
object returnValue;
try
{
// Try change type
returnValue = System.Convert.ChangeType(input, type);
}
catch(Exception exception)
{
if(exception is InvalidCastException || exception is FormatException)
{
// Try to parse because the cast is invalid
// If the type is an enumeration
if(type.IsEnum)
{
// Try to parse the string
try { returnValue = Enum.Parse(type, input.ToString(), true); }
catch(Exception) { returnValue = System.Convert.ChangeType(input, typeof(int)); }
}
else if(type.IsNullable())
{
// If the type is a nullable type (int?,long?,double?....)
returnValue = input == null ? null : System.Convert.ChangeType(input, Nullable.GetUnderlyingType(type));
}
else if(input is string || input == null)
{
// If the original type is string, we try to parse : if parsing failed then return value is the default value of the type
if(!((string)input).TryParse(type, out returnValue))
{
// Conversion "1" to true is not supported by previous case, so if return type is boolean try to convert "1" to true
if(type == typeof(Boolean))
returnValue = ((string)input).ToBoolean();
}
}
else
throw new InvalidCastException(String.Format("Unable to cast \"{0}\" in {1}", input, type.Name));
}
else
throw;
}
// return the value converted
return returnValue;
}
/// <summary>
/// Try to parse a string
/// </summary>
/// <param name="text">Text to parse</param>
/// <param name="type">Type of result</param>
/// <param name="result">Result</param>
/// <returns>True if string was parsed, else false</returns>
public static bool TryParse(this string text, Type type, out object result)
{
// Get specific converter for the type
TypeConverter converter = TypeDescriptor.GetConverter(type);
// If there is a converter and conversion is valid
if(converter != null && converter.IsValid(text))
{
// Convert
result = converter.ConvertFromInvariantString(text);
return true;
}
else
{
// Return the default value of the type
result = type.GetDefaultValue();
return false;
}
}
/// <summary>
/// Get the default value of a type
/// </summary>
/// <param name="type">Type</param>
/// <returns>Default value</returns>
public static object GetDefaultValue(this Type type)
{
return type.IsValueType ? Activator.CreateInstance(type) : null;
}
/// <summary>
/// Define if a type is a nullable type (int?, long?, double?...)
/// </summary>
/// <param name="type">Type</param>
/// <returns>true if the type is a nullable type</returns>
public static bool IsNullable(this Type type)
{
return (!type.IsGenericType) ? false : type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
}
The database I am working with currently has a varchar field, and in my code I want to map the potential values to a enumeration like:
public enum UserStatus
{
Anonymous,
Enrolled,
SuperUser
}
At the database level for this column, there is a constrain on it where the value has to be:
ANONYMOUS
ENROLLED
SUPERUSER
Is it possible for me to do:
UserStatus.SuperUser.ToString()
And have that value be SUPERUSER, and this be consistant and not screw up down the road?
A better solution may be to take advantage of the DescriptionAttribute:
public enum UserStatus
{
[Description("ANONYMOUS")]
Anonymous,
[Description("ENROLLED")]
Enrolled,
[Description("SUPERUSER")]
SuperUser
}
Then use something like:
/// <summary>
/// Class EnumExtenions
/// </summary>
public static class EnumExtenions
{
/// <summary>
/// Gets the description.
/// </summary>
/// <param name="e">The e.</param>
/// <returns>String.</returns>
public static String GetDescription(this Enum e)
{
String enumAsString = e.ToString();
Type type = e.GetType();
MemberInfo[] members = type.GetMember(enumAsString);
if (members != null && members.Length > 0)
{
Object[] attributes = members[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0)
{
enumAsString = ((DescriptionAttribute)attributes[0]).Description;
}
}
return enumAsString;
}
/// <summary>
/// Gets an enum from its description.
/// </summary>
/// <typeparam name="TEnum">The type of the T enum.</typeparam>
/// <param name="description">The description.</param>
/// <returns>Matching enum value.</returns>
/// <exception cref="System.InvalidOperationException"></exception>
public static TEnum GetFromDescription<TEnum>(String description)
where TEnum : struct, IConvertible // http://stackoverflow.com/a/79903/298053
{
if (!typeof(TEnum).IsEnum)
{
throw new InvalidOperationException();
}
foreach (FieldInfo field in typeof(TEnum).GetFields())
{
DescriptionAttribute attribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attribute != null)
{
if (attribute.Description == description)
{
return (TEnum)field.GetValue(null);
}
}
else
{
if (field.Name == description)
{
return (TEnum)field.GetValue(null);
}
}
}
return default(TEnum);
}
}
So now you're referencing UserStatus.Anonymous.GetDescription().
Of course you could always make your own DatabaseMapAttribute (or what-have-you) and create your own extension methods. Then you can kill a reference to System.ComponentModel. Completely your call.
You can't override ToString for enums, instead you can create your own Extension Method like:
public static class MyExtensions
{
public static string ToUpperString(this UserStatus userStatus)
{
return userStatus.ToString().ToUpper();// OR .ToUpperInvariant
}
}
And then call it like:
string str = UserStatus.Anonymous.ToUpperString();
Enum.ToString supports 4 different formats. I'd go for:
UserStatus.SuperUser.ToString("G").ToUpper();
"G" ensures that it will try first to get the string representation of your enum.
I got an Int16 value, from the database, and need to convert this to an enum type. This is unfortunately done in a layer of the code that knows very little about the objects except for what it can gather through reflection.
As such, it ends up calling Convert.ChangeType which fails with an invalid cast exception.
I found what I consider a smelly workaround, like this:
String name = Enum.GetName(destinationType, value);
Object enumValue = Enum.Parse(destinationType, name, false);
Is there a better way, so that I don't have to move through this String operation?
Here's a short, but complete, program that can be used if anyone need to experiment:
using System;
public class MyClass
{
public enum DummyEnum
{
Value0,
Value1
}
public static void Main()
{
Int16 value = 1;
Type destinationType = typeof(DummyEnum);
String name = Enum.GetName(destinationType, value);
Object enumValue = Enum.Parse(destinationType, name, false);
Console.WriteLine("" + value + " = " + enumValue);
}
}
Enum.ToObject(.... is what you're looking for!
C#
StringComparison enumValue = (StringComparison)Enum.ToObject(typeof(StringComparison), 5);
VB.NET
Dim enumValue As StringComparison = CType([Enum].ToObject(GetType(StringComparison), 5), StringComparison)
If you do a lot of Enum converting try using the following class it will save you alot of code.
public class Enum<EnumType> where EnumType : struct, IConvertible
{
/// <summary>
/// Retrieves an array of the values of the constants in a specified enumeration.
/// </summary>
/// <returns></returns>
/// <remarks></remarks>
public static EnumType[] GetValues()
{
return (EnumType[])Enum.GetValues(typeof(EnumType));
}
/// <summary>
/// Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
/// <remarks></remarks>
public static EnumType Parse(string name)
{
return (EnumType)Enum.Parse(typeof(EnumType), name);
}
/// <summary>
/// Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
/// </summary>
/// <param name="name"></param>
/// <param name="ignoreCase"></param>
/// <returns></returns>
/// <remarks></remarks>
public static EnumType Parse(string name, bool ignoreCase)
{
return (EnumType)Enum.Parse(typeof(EnumType), name, ignoreCase);
}
/// <summary>
/// Converts the specified object with an integer value to an enumeration member.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
/// <remarks></remarks>
public static EnumType ToObject(object value)
{
return (EnumType)Enum.ToObject(typeof(EnumType), value);
}
}
Now instead of writing (StringComparison)Enum.ToObject(typeof(StringComparison), 5); you can simply write Enum<StringComparison>.ToObject(5);.
Based on the #Peter's answer here is the method for Nullable<int> to Enum conversion:
public static class EnumUtils
{
public static bool TryParse<TEnum>(int? value, out TEnum result)
where TEnum: struct, IConvertible
{
if(!value.HasValue || !Enum.IsDefined(typeof(TEnum), value)){
result = default(TEnum);
return false;
}
result = (TEnum)Enum.ToObject(typeof(TEnum), value);
return true;
}
}
Using EnumUtils.TryParse<YourEnumType>(someNumber, out result) becomes useful for many scenarios. For example, WebApi Controller in Asp.NET does not have default protection against invalid Enum params. Asp.NET will just use default(YourEnumType) value, even if some passes null, -1000, 500000, "garbage string" or totally ignores the parameter. Moreover, ModelState will be valid in all these cases, so one of the solution is to use int? type with custom check
public class MyApiController: Controller
{
[HttpGet]
public IActionResult Get(int? myEnumParam){
MyEnumType myEnumParamParsed;
if(!EnumUtils.TryParse<MyEnumType>(myEnumParam, out myEnumParamParsed)){
return BadRequest($"Error: parameter '{nameof(myEnumParam)}' is not specified or incorrect");
}
return this.Get(washingServiceTypeParsed);
}
private IActionResult Get(MyEnumType myEnumParam){
// here we can guarantee that myEnumParam is valid
}
If you are storing an Enum in a DataTable but don't know which column is an enum and which is a string/int, you can access the value this way:
foreach (DataRow dataRow in myDataTable.Rows)
{
Trace.WriteLine("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=");
foreach (DataColumn dataCol in myDataTable.Columns)
{
object v = dataRow[dataCol];
Type t = dataCol.DataType;
bool e = false;
if (t.IsEnum) e = true;
Trace.WriteLine((dataCol.ColumnName + ":").PadRight(30) +
(e ? Enum.ToObject(t, v) : v));
}
}
I currently use this handy conversion extension method to do conversions between types:
public static T To<T>(this IConvertible obj)
{
return (T)Convert.ChangeType(obj, typeof(T));
}
However, it doesn't like converting valid values to Nullable, for example, this fails:
"1".To<int?>();
Obviously, 1 is easily converted to an (int?), but it gets the error:
Invalid cast from 'System.String' to 'System.Nullable`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'.
This is an obviously simplified example, in reality I'm using it to do conversions from string types like so:
packageDb.Quantity = package.package.ElementDeep(Namespace + "PackageQuantity", Namespace + "ActualQuantity", Namespace + "Quantity").ValueOrNull().To<int?>();
If Convert.ChangeType doesn't like Nullable, anyone have any great ideas?
public static T To<T>(this IConvertible obj)
{
Type t = typeof(T);
Type u = Nullable.GetUnderlyingType(t);
if (u != null)
{
return (obj == null) ? default(T) : (T)Convert.ChangeType(obj, u);
}
else
{
return (T)Convert.ChangeType(obj, t);
}
}
public static T To<T>(this IConvertible obj)
{
Type t = typeof(T);
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
t = t.GetGenericArguments()[0];
return (T)Convert.ChangeType(obj, t);
}
But if the conversion fail, it will throw an exception, not returning a null as should be expected.
I've ended up with this
private static T To<T>(this Object #object, Boolean returnDefaultOnException)
{
Type type = typeof(T);
Type underlyingTypeOfNullable = Nullable.GetUnderlyingType(type);
try
{
return (T) Convert.ChangeType(#object, underlyingTypeOfNullable ?? type);
}
catch (Exception exception)
{
if (returnDefaultOnException)
return default(T);
String typeName = type.Name;
if (underlyingTypeOfNullable != null)
typeName += " of " + underlyingTypeOfNullable.Name;
throw new InvalidCastException("Object can't be cast to " + typeName, exception);
}
}
public static T To<T>(this Object #object) { return #object.To<T>(returnDefaultOnException: false); }
public static T ToOrDefault<T>(this Object #object) { return #object.To<T>(returnDefaultOnException: true); }
It behaves like the LINQ extension methods Single and SingleOrDefault and First and FirstOrDefault.
In short, To<T>() tries to convert and throws on failure while ToOrDefault<T>() tries to convert and returns default(T) on failure.
Maybe I'm missing the point, but in the instance of Nullable, how does your method provide either a readability, performance, or maintenance advantage over a simple cast, like (int?)1 ?
Aside from that, perhaps another extension method?
public static T? ToNullable<T>(this T obj) where T:struct
{
return (T?)obj;
}
Edit
After reviewing your edit, why would the generic function that I provided not work as a substitute to your To<T> function in that line of code? You can't allow a conversion to Nullable for any type (which is why ChangeType doesn't work) because that generic only accepts value types. You'll either have to use a function like the one I provided or change your signature of To<T> to only accept value types and add a special case for Nullable<T>.
Luke's solution was good for me (and obviously got his up vote) but I simplified it for me this way
private static Type ResolveType(String typeName)
{
Type t = Type.GetType(typeName);
if (t == null)
return null;
Type u = Nullable.GetUnderlyingType(t);
if (u != null) {
t = u;
}
return t;
}
because I started from a string not from a type...
thoughts?
This is the method that I currently use (I got my answer on SO), it converts from string to nullable type:
public static Nullable<T> ConvertToNullable<T>(this string s) where T : struct
{
if (!string.IsNullOrEmpty(s.Trim()))
{
TypeConverter conv = TypeDescriptor.GetConverter(typeof(Nullable<>).MakeGenericType(typeof(T)));
return (Nullable<T>)conv.ConvertFrom(s);
}
return null;
}
extend #LukeH code:
public static T GetValue<T>(string Literal, T DefaultValue)
{
if (Literal == null || Literal == "" || Literal == string.Empty) return DefaultValue;
IConvertible obj = Literal;
Type t = typeof(T);
Type u = Nullable.GetUnderlyingType(t);
if (u != null)
{
return (obj == null) ? DefaultValue : (T)Convert.ChangeType(obj, u);
}
else
{
return (T)Convert.ChangeType(obj, t);
}
}
This method does what you need, and it looks nice while doing it.
/// <summary>
/// <para>More convenient than using T.TryParse(string, out T).
/// Works with primitive types, structs, and enums.
/// Tries to parse the string to an instance of the type specified.
/// If the input cannot be parsed, null will be returned.
/// </para>
/// <para>
/// If the value of the caller is null, null will be returned.
/// So if you have "string s = null;" and then you try "s.ToNullable...",
/// null will be returned. No null exception will be thrown.
/// </para>
/// <author>Contributed by Taylor Love (Pangamma)</author>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="p_self"></param>
/// <returns></returns>
public static T? ToNullable<T>(this string p_self) where T : struct
{
if (!string.IsNullOrEmpty(p_self))
{
var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));
if (converter.IsValid(p_self)) return (T)converter.ConvertFromString(p_self);
if (typeof(T).IsEnum) { T t; if (Enum.TryParse<T>(p_self, out t)) return t;}
}
return null;
}
https://github.com/Pangamma/PangammaUtilities-CSharp/tree/master/src/StringExtensions