How to TryParse for Enum value? - c#

I want to write a function which can validate a given value (passed as a string) against possible values of an enum. In the case of a match, it should return the enum instance; otherwise, it should return a default value.
The function may not internally use try/catch, which excludes using Enum.Parse, which throws an exception when given an invalid argument.
I'd like to use something along the lines of a TryParse function to implement this:
public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
object enumValue;
if (!TryParse (typeof (TEnum), strEnumValue, out enumValue))
{
return defaultValue;
}
return (TEnum) enumValue;
}

Enum.IsDefined will get things done. It may not be as efficient as a TryParse would probably be, but it will work without exception handling.
public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
if (!Enum.IsDefined(typeof(TEnum), strEnumValue))
return defaultValue;
return (TEnum)Enum.Parse(typeof(TEnum), strEnumValue);
}
Worth noting: a TryParse method was added in .NET 4.0.

As others have said, you have to implement your own TryParse. Simon Mourier is providing a full implementation which takes care of everything.
If you are using bitfield enums (i.e. flags), you also have to handle a string like "MyEnum.Val1|MyEnum.Val2" which is a combination of two enum values. If you just call Enum.IsDefined with this string, it will return false, even though Enum.Parse handles it correctly.
Update
As mentioned by Lisa and Christian in the comments, Enum.TryParse is now available for C# in .NET4 and up.
MSDN Docs

Here is a custom implementation of EnumTryParse. Unlike other common implementations, it also supports enum marked with the Flags attribute.
/// <summary>
/// Converts the string representation of an enum to its Enum equivalent value. A return value indicates whether the operation succeeded.
/// This method does not rely on Enum.Parse and therefore will never raise any first or second chance exception.
/// </summary>
/// <param name="type">The enum target type. May not be null.</param>
/// <param name="input">The input text. May be null.</param>
/// <param name="value">When this method returns, contains Enum equivalent value to the enum contained in input, if the conversion succeeded.</param>
/// <returns>
/// true if s was converted successfully; otherwise, false.
/// </returns>
public static bool EnumTryParse(Type type, string input, out object value)
{
if (type == null)
throw new ArgumentNullException("type");
if (!type.IsEnum)
throw new ArgumentException(null, "type");
if (input == null)
{
value = Activator.CreateInstance(type);
return false;
}
input = input.Trim();
if (input.Length == 0)
{
value = Activator.CreateInstance(type);
return false;
}
string[] names = Enum.GetNames(type);
if (names.Length == 0)
{
value = Activator.CreateInstance(type);
return false;
}
Type underlyingType = Enum.GetUnderlyingType(type);
Array values = Enum.GetValues(type);
// some enums like System.CodeDom.MemberAttributes *are* flags but are not declared with Flags...
if ((!type.IsDefined(typeof(FlagsAttribute), true)) && (input.IndexOfAny(_enumSeperators) < 0))
return EnumToObject(type, underlyingType, names, values, input, out value);
// multi value enum
string[] tokens = input.Split(_enumSeperators, StringSplitOptions.RemoveEmptyEntries);
if (tokens.Length == 0)
{
value = Activator.CreateInstance(type);
return false;
}
ulong ul = 0;
foreach (string tok in tokens)
{
string token = tok.Trim(); // NOTE: we don't consider empty tokens as errors
if (token.Length == 0)
continue;
object tokenValue;
if (!EnumToObject(type, underlyingType, names, values, token, out tokenValue))
{
value = Activator.CreateInstance(type);
return false;
}
ulong tokenUl;
switch (Convert.GetTypeCode(tokenValue))
{
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.SByte:
tokenUl = (ulong)Convert.ToInt64(tokenValue, CultureInfo.InvariantCulture);
break;
//case TypeCode.Byte:
//case TypeCode.UInt16:
//case TypeCode.UInt32:
//case TypeCode.UInt64:
default:
tokenUl = Convert.ToUInt64(tokenValue, CultureInfo.InvariantCulture);
break;
}
ul |= tokenUl;
}
value = Enum.ToObject(type, ul);
return true;
}
private static char[] _enumSeperators = new char[] { ',', ';', '+', '|', ' ' };
private static object EnumToObject(Type underlyingType, string input)
{
if (underlyingType == typeof(int))
{
int s;
if (int.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(uint))
{
uint s;
if (uint.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(ulong))
{
ulong s;
if (ulong.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(long))
{
long s;
if (long.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(short))
{
short s;
if (short.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(ushort))
{
ushort s;
if (ushort.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(byte))
{
byte s;
if (byte.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(sbyte))
{
sbyte s;
if (sbyte.TryParse(input, out s))
return s;
}
return null;
}
private static bool EnumToObject(Type type, Type underlyingType, string[] names, Array values, string input, out object value)
{
for (int i = 0; i < names.Length; i++)
{
if (string.Compare(names[i], input, StringComparison.OrdinalIgnoreCase) == 0)
{
value = values.GetValue(i);
return true;
}
}
if ((char.IsDigit(input[0]) || (input[0] == '-')) || (input[0] == '+'))
{
object obj = EnumToObject(underlyingType, input);
if (obj == null)
{
value = Activator.CreateInstance(type);
return false;
}
value = obj;
return true;
}
value = Activator.CreateInstance(type);
return false;
}

In the end you have to implement this around Enum.GetNames:
public bool TryParseEnum<T>(string str, bool caseSensitive, out T value) where T : struct {
// Can't make this a type constraint...
if (!typeof(T).IsEnum) {
throw new ArgumentException("Type parameter must be an enum");
}
var names = Enum.GetNames(typeof(T));
value = (Enum.GetValues(typeof(T)) as T[])[0]; // For want of a better default
foreach (var name in names) {
if (String.Equals(name, str, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)) {
value = (T)Enum.Parse(typeof(T), name);
return true;
}
}
return false;
}
Additional notes:
Enum.TryParse is included in .NET 4. See here http://msdn.microsoft.com/library/dd991876(VS.100).aspx
Another approach would be to directly wrap Enum.Parse catching the exception thrown when it fails. This could be faster when a match is found, but will likely to slower if not. Depending on the data you are processing this may or may not be a net improvement.
EDIT: Just seen a better implementation on this, which caches the necessary information: http://damieng.com/blog/2010/10/17/enums-better-syntax-improved-performance-and-tryparse-in-net-3-5

Based on .NET 4.5
Sample code below
using System;
enum Importance
{
None,
Low,
Medium,
Critical
}
class Program
{
static void Main()
{
// The input value.
string value = "Medium";
// An unitialized variable.
Importance importance;
// Call Enum.TryParse method.
if (Enum.TryParse(value, out importance))
{
// We now have an enum type.
Console.WriteLine(importance == Importance.Medium);
}
}
}
Reference : http://www.dotnetperls.com/enum-parse

enum EnumStatus
{
NAO_INFORMADO = 0,
ENCONTRADO = 1,
BLOQUEADA_PELO_ENTREGADOR = 2,
DISPOSITIVO_DESABILITADO = 3,
ERRO_INTERNO = 4,
AGARDANDO = 5
}
...
if (Enum.TryParse<EnumStatus>(item.status, out status)) {
}

I have an optimised implementation you could use in UnconstrainedMelody. Effectively it's just caching the list of names, but it's doing so in a nice, strongly typed, generically constrained way :)

There's currently no out of the box Enum.TryParse. This has been requested on Connect (Still no Enum.TryParse) and got a response indicating possible inclusion in the next framework after .NET 3.5. You'll have to implement the suggested workarounds for now.

The only way to avoid exception handling is to use the GetNames() method, and we all know that exceptions shouldn't be abused for common application logic :)

Is caching a dynamically generated function/dictionary permissable?
Because you don't (appear to) know the type of the enum ahead of time, the first execution could generate something subsequent executions could take advantage of.
You could even cache the result of Enum.GetNames()
Are you trying to optimize for CPU or Memory? Do you really need to?

As others already said, if you don't use Try&Catch, you need to use IsDefined or GetNames...
Here are some samples...they basically are all the same, the first one handling nullable enums. I prefer the 2nd one as it's an extension on strings, not enums...but you can mix them as you want!
www.objectreference.net/post/Enum-TryParse-Extension-Method.aspx
flatlinerdoa.spaces.live.com/blog/cns!17124D03A9A052B0!605.entry
mironabramson.com/blog/post/2008/03/Another-version-for-the-missing-method-EnumTryParse.aspx
lazyloading.blogspot.com/2008/04/enumtryparse-with-net-35-extension.html

There is not a TryParse because the Enum's type is not known until runtime. A TryParse that follows the same methodology as say the Date.TryParse method would throw an implicit conversion error on the ByRef parameter.
I suggest doing something like this:
//1 line call to get value
MyEnums enumValue = (Sections)EnumValue(typeof(Sections), myEnumTextValue, MyEnums.SomeEnumDefault);
//Put this somewhere where you can reuse
public static object EnumValue(System.Type enumType, string value, object NotDefinedReplacement)
{
if (Enum.IsDefined(enumType, value)) {
return Enum.Parse(enumType, value);
} else {
return Enum.Parse(enumType, NotDefinedReplacement);
}
}

Have a look at the Enum class (struct ? ) itself. There is a Parse method on that but I'm not sure about a tryparse.

This method will convert a type of enum:
public static TEnum ToEnum<TEnum>(object EnumValue, TEnum defaultValue)
{
if (!Enum.IsDefined(typeof(TEnum), EnumValue))
{
Type enumType = Enum.GetUnderlyingType(typeof(TEnum));
if ( EnumValue.GetType() == enumType )
{
string name = Enum.GetName(typeof(HLink.ViewModels.ClaimHeaderViewModel.ClaimStatus), EnumValue);
if( name != null)
return (TEnum)Enum.Parse(typeof(TEnum), name);
return defaultValue;
}
}
return (TEnum)Enum.Parse(typeof(TEnum), EnumValue.ToString());
}
It checks the underlying type and get the name against it to parse. If everything fails it will return default value.

Related

Check if value is in Enum range without using IsDefined

There are a few other Questions on how to convert Enums and what happens if the value parsed is out-of-range, like in:
public enum SomeTypes
{
SomeType1 = 1,
SomeType2 = 2,
SomeType3 = 3
}
public class SomeClass
{
...
var inRange = (SomeTypes) 1;
var outOfRange = (SomeTypes) 5;
...
}
Going out-of-range will not produce any error.
But I found the hard way that if you try to serialize-deserialize an enum with an out-of-range value you'll get weird errors. For example, I was getting something like
"error parsing the message or timeout exceeded"
which kept me looking for other reasons than the enum out-of-range.
Suggestions to handle this are by the means of the Enum.IsDefined. That seems to work quite nicely, but then there's this rather bold warning on msdn:
"Do not use System.Enum.IsDefined(System.Type,System.Object) for enumeration range checks as it is based on the runtime type of the enumeration, which can change from version to version."
So, my question is, can we safely use Enum.IsDefined or what is the correct way to check if the value of an enum is out-of-range without using the Enum.IsDefined?
Use Enum.GetValues():
public bool IsInRange(int value){
var values = Enum.GetValues(typeof(SomeTypes)).Cast<int>().OrderBy(x => x);
return value >= values.First() && value <= values.Last();
}
[EDIT]
In case you want to check if the item is defined instead of just checking if it's inside the range, you can do similarly:
public bool IsDefined(int value){
var values = Enum.GetValues(typeof(SomeTypes)).Cast<int>().OrderBy(x => x);
return values.Contains(value);
}
There is an option for something simpler:
int value;
bool isInRange = string.IsNullOrEmpty(Enum.GetName(typeof(myEnumType), value));
I did a similar thing with objects DataContract
You must decorate the items in the list with [EnumMember] and then you could obtain the enum name with this method. So you would know if value exists in the enum cos returns his enum name.
public static string GetEnumNameFromValue(System.Type typeEnum, string value)
{
FieldInfo[] fis = typeEnum.GetFields();
foreach (FieldInfo fi in fis)
{
EnumMemberAttribute[] attributes = (EnumMemberAttribute[])fi.GetCustomAttributes(typeof(EnumMemberAttribute), false);
if (attributes.Length > 0)
{
if (string.Compare(attributes[0].Value, value, true) == 0)
{
return fi.Name;
}
}
}
return null;
}

Whoa, what the TryParse

I've got a Session that contains particular integer values, which are indexed with given controls. Normally, the following would work just fine:
int value;
int.TryParse(Session["Key"].ToString(), out value);
However, I do need to account for null. Where, if the string fails the default out would return a null. Except I noticed that int.TryParse doesn't work with:
int? value = null;
int.TryParse(Session["Key"].ToString(), out value);
So how can you try that parse, if fails it results in the null?
I found this question and the Microsoft Developer Network dictates:
When this method returns, contains the signed integer value
equivalent of the number contained in s, if the conversion succeeded,
or zero if the conversion failed. The conversion fails if the string
parameter is null or String.Empty, is not of the correct format, or
represents a number less than Min Value or greater than Max Value. This
parameter is passed uninitialized.
Which plainly states, if int.TryParse fails the integer will hold a value of zero. In the instance of my usage, zero could be a valid value. So I need null, any thoughts?
Sure; utilize the return value of int.TryParse (which returns if the conversion succeeded or not):
int? retValue = null;
int parsedValue = 0;
if (int.TryParse(Session["Key"].ToString(), out parsedValue))
retValue = parsedValue;
else
retValue = null;
return retValue;
A little verbose I'll admit, but you could wrap it in a function.
int tmp;
int? value = int.TryParse(Session["Key"].ToString(), out tmp) ? (int?)tmp : null;
The problem is the word "null." What does it mean? null could mean the value was indeterminable, an exception was thrown, simply that the value is null, or some other contextual meaning. Your question is a perfect example, because you, yourself, are arbitrarily stating that, in your opinion, null means the parsing of the string failed.
Microsoft's TryParse paradigm is great, but for limited usage. Consider these Scenarios:
string == "89"
string == null
string == "Hello World"
string == ""
string == "2147483650"
Yet, your only options are to assign an Integer or Null to your output, and to return true or false.
Assuming it worked, what are you going to do with that information? Something like this?
int? value = null;
if (int.TryParse(Session["Key"].ToString(), out value)) {
if (value == null)
// Handle "Appropriate" null
else
// Handle appropriate numeric value
}
else {
// Note: value == null here, and TryParse failed
// Handle null...
// What if the reason it failed was because the number was too big?
// What if the string was Empty and you wanted to do something special?
// What if the string was actually junk? Like "(423)322-9876" ?
// Long-Story Short: You don't know what to do here without more info.
}
Consider this NullableInt TryParse example:
public bool TryParseNullableInt(string input, out int? output)
{
int tempOutput;
output = null;
if (input == null) return true;
if (input == string.Empty) return true; // Would you rather this be 0?
if (!int.TryParse(input, out tempOutput))
return false; // What if string was "2147483650"... or "Twenty Three"?
output = tempOutput;
return true;
}
One solution is to use an enumeration TryParse instead of a boolean TryParse:
public ParseStatus TryParseNullableInt(string input, out int? output)
{
int tempInteger;
output = null;
if (input == null) return ParseStatus.Success;
if (input == string.Empty) { output = 0; return ParseStatus.Derived; }
if (!int.TryParse(input, out tempInteger)) {
if (ParseWords(input, out tempInteger)) { // "Twenty Three" = 23
output = tempInteger;
return ParseStatus.Derived;
}
long tempLong;
if (long.TryParse(input, out tempLong))
return ParseStatus.OutOfRange;
return ParseStatus.NotParsable;
}
output = tempInteger;
return ParseStatus.Success;
}
Another problem is the existence of the out variable. Your third option is to use a descriptive monad, something like this:
public Maybe<int?> TryParseNullableInt(string input)
{
if (input == null) return Maybe.Success(null);
if (input == string.Empty) { return Maybe.Derived(0); }
int tempInteger;
if (!int.TryParse(input, out tempInteger)) {
if (ParseWords(input, out tempInteger)) { // "Twenty Three" = 23
return Maybe.Derived(tempInteger);
}
long tempLong;
if (long.TryParse(input, out tempLong))
return Maybe.OutOfRange();
return Maybe.NotParsable();
}
return Maybe.Success(tempInteger);
}
You can use Monads as Single-Enumerable Values, or like so:
Maybe<int?> result = TryParseNullableInt("Hello");
if (result.HasValue) {
if (result.Status == ParseStatus.Success)
// Do something you want...
else if (result.Status == ParseStatus.Derived)
// Do something else... more carefully maybe?
}
else if (result.Status == ParseStatus.OutOfRange)
MessageUser("That number is too big or too small");
else if (result.Status == ParseStatus.NotParsable)
// Do something
With Monads, and possibly enumeration TryParses, you now have all the info you need from a descriptive return and nobody has to guess what null might mean.

How can I assign an enumerated type value to a variable?

I have some factory code that creates objects based on the value of a class member representing an enum:
public enum BeltPrinterType
{
None,
ZebraQL220,
ONiel
// add more as needed
}
public static BeltPrinterType printerChoice = BeltPrinterType.None;
public class BeltPrinterFactory : IBeltPrinterFactory
{
// http://stackoverflow.com/questions/17955040/how-can-i-return-none-as-a-default-case-from-a-factory?noredirect=1#comment26241733_17955040
public IBeltPrinter NewBeltPrinter()
{
switch (printerChoice)
{
case BeltPrinterType.ZebraQL220:
return new ZebraQL220Printer();
case BeltPrinterType.ONiel:
return new ONielPrinter();
default:
return new None();
}
}
}
I need to set the value of printerChoice before I call NewBeltPrinter() - that is, if it has been changed from its default "None" value. So I'm trying to assign that value based on the string representation, but have gotten to the proverbial point of no continuance with this attempt:
string currentPrinter = AppSettings.ReadSettingsVal("beltprinter");
Type type = typeof(PrintUtils.BeltPrinterType);
foreach (FieldInfo field in type.GetFields(BindingFlags.Static | BindingFlags.Public))
{
string display = field.GetValue(null).ToString();
if (currentPrinter == display)
{
//PrintUtils.printerChoice = (type)field.GetValue(null);
PrintUtils.printerChoice = ??? what now ???
break;
}
}
I've tried everything I could think of, and have been repaid with nothing but constant reprimands from the compiler, which has pretty much been dis[mis]sing me as a knave and a sorry rascal.
Does anybody know what I should replace the question marks with?
What about just this instead of your second code block:
PrintUtils.printerChoice = (PrintUtils.BeltPrinterType)
Enum.Parse(typeof(PrintUtils.BeltPrinterType),
AppSettings.ReadSettingsVal("beltprinter"));
Use Enum.Parse to convert a string into the enum value.
(or Enum.TryParse to attempt to do it without raising an exception if it didn't parse)
edit
If you don't have an Enum.Parse available, then you will have to do the conversion yourself:
switch (stringValue)
{
case "BeltPrinterType.ONiel": enumValue = BeltPrinterType.ONiel; break;
...etc...
}
I can't compile to .NET 1.1 but this seems to work in 2.0
PrintUtils.printerChoice = (BeltPrinterType)field.GetValue(null);
EDIT : I just realized this was basically a comment in your code... But yeah I really don't see why this wouldn't work even in 1.1
From the Smart Device Framework 1.x code base:
public static object Parse(System.Type enumType, string value, bool ignoreCase)
{
//throw an exception on null value
if(value.TrimEnd(' ')=="")
{
throw new ArgumentException("value is either an empty string (\"\") or only contains white space.");
}
else
{
//type must be a derivative of enum
if(enumType.BaseType==Type.GetType("System.Enum"))
{
//remove all spaces
string[] memberNames = value.Replace(" ","").Split(',');
//collect the results
//we are cheating and using a long regardless of the underlying type of the enum
//this is so we can use ordinary operators to add up each value
//I suspect there is a more efficient way of doing this - I will update the code if there is
long returnVal = 0;
//for each of the members, add numerical value to returnVal
foreach(string thisMember in memberNames)
{
//skip this string segment if blank
if(thisMember!="")
{
try
{
if(ignoreCase)
{
returnVal += (long)Convert.ChangeType(enumType.GetField(thisMember, BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase).GetValue(null),returnVal.GetType(), null);
}
else
{
returnVal += (long)Convert.ChangeType(enumType.GetField(thisMember, BindingFlags.Public | BindingFlags.Static).GetValue(null),returnVal.GetType(), null);
}
}
catch
{
try
{
//try getting the numeric value supplied and converting it
returnVal += (long)Convert.ChangeType(System.Enum.ToObject(enumType, Convert.ChangeType(thisMember, System.Enum.GetUnderlyingType(enumType), null)),typeof(long),null);
}
catch
{
throw new ArgumentException("value is a name, but not one of the named constants defined for the enumeration.");
}
//
}
}
}
//return the total converted back to the correct enum type
return System.Enum.ToObject(enumType, returnVal);
}
else
{
//the type supplied does not derive from enum
throw new ArgumentException("enumType parameter is not an System.Enum");
}
}
}

Parsing enum flags from comma-separated list or integer

I have an XML that contains several flags, some of them are unsigned 32-bit integers and others are unsigned 64-bit integers. Some of them are written in a comma-separated list and others are in hex style.
See this example:
<Color>Blue,Red</Color>
<Color>0xC</Color>
As I don't want to write a method to parse each enum, I decided to use a generic method. But Visual Studio won't let me build the solution. Here's my method:
public static T ParseFlags<T>(string value) where T : struct
{
T result = (T)((object)0);
string[] array;
// Remove white spaces and delimit string if it is comma-separated
if (ParseDelimitedString(value, ',', out array))
{
for (int i = 0; i < array.Length; i++)
{
T flag = (T)((object)0);
// Check if value is member of enumeration
if (Enum.TryParse<T>(array[i], out flag))
{
result |= (T)((object)flag);
}
}
}
else
{
switch (Type.GetTypeCode(Enum.GetUnderlyingType(typeof(T))))
{
// Remove hex characters and parse node's inner text
case TypeCode.UInt32:
result = (T)((object)ParseUint(value));
break;
case TypeCode.UInt64:
result = (T)((object)ParseUlong(value));
break;
}
}
return result;
}
The error message I get is:
Error 1 Operator '|=' cannot be applied to operands of type 'T' and
'T'
Is there a way to do this?
You are doing a lot of work that can be done for you. For example, if your enum is declared with the FlagsAttribute then Enum.Parse will parse comma separated values for you.
public static T ParseFlags<T>(string value) where T : struct
{
T result;
ulong temp;
if (Enum.TryParse(value, out result))
{
return result;
}
string hexNum = value.StartsWith("0x") ? value.Substring(2) : value;
if (ulong.TryParse(hexNum, NumberStyles.HexNumber, null, out temp))
{
return (T)Enum.ToObject(typeof(T), temp);
}
throw new ArgumentException("value could not be parsed");
}
I tested this with various Flags enum types with short, int, and ulong backing values.
If you know what type of enum you're parsing:
[Fact]
public void when_parsing_options_then_can_combine_flags()
{
var values = "Singleline | Compiled";
var options = values.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
.Select(value => (RegexOptions)Enum.Parse(typeof(RegexOptions), value))
.Aggregate(RegexOptions.None, (current, value) => current |= value);
Assert.Equal(RegexOptions.Singleline | RegexOptions.Compiled, options);
}
Try this:
public static T ParseFlags<T>(string value) where T : struct
{
long result = 0L;
string[] array;
// Remove white spaces and delimit string if it is comma-separated
if (ParseDelimitedString(value, ',', out array))
{
for (int i = 0; i < array.Length; i++)
{
T flag = default(T);
// Check if value is member of enumeration
if (Enum.TryParse<T>(array[i], out flag))
{
result |= (long)flag;
}
}
}
else
{
switch (Type.GetTypeCode(Enum.GetUnderlyingType(typeof(T))))
{
// Remove hex characters and parse node's inner text
case TypeCode.UInt32:
result = ParseUint(value);
break;
case TypeCode.UInt64:
result = ParseUlong(value);
break;
}
}
return (T)((object)result);
}
Hope it helps.
"|=" usage in that snippet makes me think you meant to use the Enum as bitset not just any old Enum. If this is true, you should make a small change - declare the local "result" as int and adjust the casts appropriately, your return statement should then be "return (T)(object)result;". That line in question with "|" would look like: "result |= (int)(object)flag;". Perhaps, there is a better answer but, note that enums are integers and your scenario of bitset are covered well enough with this solution unless there are situations I missed or you did not state.

What is the C# equivalent of NaN or IsNumeric?

What is the most efficient way of testing an input string whether it contains a numeric value (or conversely Not A Number)? I guess I can use Double.Parse or a regex (see below) but I was wondering if there is some built in way to do this, such as javascript's NaN() or IsNumeric() (was that VB, I can't remember?).
public static bool IsNumeric(this string value)
{
return Regex.IsMatch(value, "^\\d+$");
}
This doesn't have the regex overhead
double myNum = 0;
String testVar = "Not A Number";
if (Double.TryParse(testVar, out myNum)) {
// it is a number
} else {
// it is not a number
}
Incidentally, all of the standard data types, with the glaring exception of GUIDs, support TryParse.
update
secretwep brought up that the value "2345," will pass the above test as a number. However, if you need to ensure that all of the characters within the string are digits, then another approach should be taken.
example 1:
public Boolean IsNumber(String s) {
Boolean value = true;
foreach(Char c in s.ToCharArray()) {
value = value && Char.IsDigit(c);
}
return value;
}
or if you want to be a little more fancy
public Boolean IsNumber(String value) {
return value.All(Char.IsDigit);
}
update 2 ( from #stackonfire to deal with null or empty strings)
public Boolean IsNumber(String s) {
Boolean value = true;
if (s == String.Empty || s == null) {
value=false;
} else {
foreach(Char c in s.ToCharArray()) {
value = value && Char.IsDigit(c);
}
} return value;
}
I prefer something like this, it lets you decide what NumberStyle to test for.
public static Boolean IsNumeric(String input, NumberStyles numberStyle) {
Double temp;
Boolean result = Double.TryParse(input, numberStyle, CultureInfo.CurrentCulture, out temp);
return result;
}
In addition to the previous correct answers it is probably worth pointing out that "Not a Number" (NaN) in its general usage is not equivalent to a string that cannot be evaluated as a numeric value. NaN is usually understood as a numeric value used to represent the result of an "impossible" calculation - where the result is undefined. In this respect I would say the Javascript usage is slightly misleading. In C# NaN is defined as a property of the single and double numeric types and is used to refer explicitly to the result of diving zero by zero. Other languages use it to represent different "impossible" values.
I know this has been answered in many different ways, with extensions and lambda examples, but a combination of both for the simplest solution.
public static bool IsNumeric(this String s)
{
return s.All(Char.IsDigit);
}
or if you are using Visual Studio 2015 (C# 6.0 or greater) then
public static bool IsNumeric(this String s) => s.All(Char.IsDigit);
Awesome C#6 on one line. Of course this is limited because it just tests for only numeric characters.
To use, just have a string and call the method on it, such as:
bool IsaNumber = "123456".IsNumeric();
Yeah, IsNumeric is VB. Usually people use the TryParse() method, though it is a bit clunky. As you suggested, you can always write your own.
int i;
if (int.TryParse(string, out i))
{
}
I like the extension method, but don't like throwing exceptions if possible.
I opted for an extension method taking the best of 2 answers here.
/// <summary>
/// Extension method that works out if a string is numeric or not
/// </summary>
/// <param name="str">string that may be a number</param>
/// <returns>true if numeric, false if not</returns>
public static bool IsNumeric(this String str)
{
double myNum = 0;
if (Double.TryParse(str, out myNum))
{
return true;
}
return false;
}
You can still use the Visual Basic function in C#. The only thing you have to do is just follow my instructions shown below:
Add the reference to the Visual Basic Library by right clicking on your project and selecting "Add Reference":
Then import it in your class as shown below:
using Microsoft.VisualBasic;
Next use it wherever you want as shown below:
if (!Information.IsNumeric(softwareVersion))
{
throw new DataException(string.Format("[{0}] is an invalid App Version! Only numeric values are supported at this time.", softwareVersion));
}
Hope, this helps and good luck!
VB has the IsNumeric function. You could reference Microsoft.VisualBasic.dll and use it.
Simple extension:
public static bool IsNumeric(this String str)
{
try
{
Double.Parse(str.ToString());
return true;
}
catch {
}
return false;
}
public static bool IsNumeric(string anyString)
{
if (anyString == null)
{
anyString = "";
}
if (anyString.Length > 0)
{
double dummyOut = new double();
System.Globalization.CultureInfo cultureInfo = new System.Globalization.CultureInfo("en-US", true);
return Double.TryParse(anyString, System.Globalization.NumberStyles.Any, cultureInfo.NumberFormat, out dummyOut);
}
else
{
return false;
}
}
Maybe this is a C# 3 feature, but you could use double.NaN.
Actually, Double.NaN is supported in all .NET versions 2.0 and greater.
I was using Chris Lively's snippet (selected answer) encapsulated in a bool function like Gishu's suggestion for a year or two. I used it to make sure certain query strings were only numeric before proceeding with further processing. I started getting some errant querystrings that the marked answer was not handling, specifically, whenever a comma was passed after a number like "3645," (returned true). This is the resulting mod:
static public bool IsNumeric(string s)
{
double myNum = 0;
if (Double.TryParse(s, out myNum))
{
if (s.Contains(",")) return false;
return true;
}
else
{
return false;
}
}
This is a modified version of the solution proposed by Mr Siir. I find that adding an extension method is the best solution for reuse and simplicity in the calling method.
public static bool IsNumeric(this String s)
{
try { double.Parse(s); return true; }
catch (Exception) { return false; }
}
I modified the method body to fit on 2 lines and removed the unnecessary .ToString() implementation. For those not familiar with extension methods here is how to implement:
Create a class file called ExtensionMethods. Paste in this code:
using System;
using System.Collections.Generic;
using System.Text;
namespace YourNameSpaceHere
{
public static class ExtensionMethods
{
public static bool IsNumeric(this String s)
{
try { double.Parse(s); return true; }
catch (Exception) { return false; }
}
}
}
Replace YourNameSpaceHere with your actual NameSpace. Save changes. Now you can use the extension method anywhere in your app:
bool validInput = stringVariable.IsNumeric();
Note: this method will return true for integers and decimals, but will return false if the string contains a comma. If you want to accept input with commas or symbols like "$" I would suggest implementing a method to remove those characters first then test if IsNumeric.
I have a slightly different version which returns the number. I would guess that in most cases after testing the string you would want to use the number.
public bool IsNumeric(string numericString, out Double numericValue)
{
if (Double.TryParse(numericString, out numericValue))
return true;
else
return false;
}
If you don't want the overhead of adding the Microsoft.VisualBasic library just for isNumeric, here's the code reverse engineered:
public bool IsNumeric(string s)
{
if (s == null) return false;
int state = 0; // state 0 = before number, state 1 = during number, state 2 = after number
bool hasdigits = false;
bool hasdollar = false;
bool hasperiod = false;
bool hasplusminus = false;
bool hasparens = false;
bool inparens = false;
for (var i = 0; i <= s.Length - 1; i++)
{
switch (s[i])
{
case char n when (n >= '0' && n <= '9'):
if (state == 2) return false; // no more numbers at the end (i.e. "1 2" is not valid)
if (state == 0) state = 1; // begin number state
hasdigits = true;
break;
case '-':
case '+':
// a plus/minus is allowed almost anywhere, but only one, and you cannot combine it with parenthesis
if (hasplusminus || hasparens) return false;
if (state == 1) state = 2; // exit number state (i.e. "1-" is valid but 1-1 is not)
hasplusminus = true;
break;
case ' ':
case '\t':
case '\r':
case '\n':
// don't allow any spaces after parenthesis/plus/minus, unless there's a $
if (state == 0 && (hasparens || (hasplusminus && !hasdollar))) return false;
if (state == 1) state = 2; // exit number state
break;
case ',':
// do not allow commas unless in the middle of the number, and not after a decimal
if (state != 1 || hasperiod) return false;
break;
case '.':
// only allow one period in the number
if (hasperiod || state == 2) return false;
if (state == 0) state = 1; // begin number state
hasperiod = true;
break;
case '$':
// dollar symbol allowed anywhere, but only one
if (hasdollar) return false;
if (state == 1) state = 2; // exit number state (i.e. "1$" is valid but "1$1" is not)
hasdollar = true;
break;
case '(':
// only allow one parens at the beginning, and cannot combine with plus/minus
if (state != 0 || hasparens || hasplusminus) return false;
hasparens = true;
inparens = true;
break;
case ')':
if (state == 1 && inparens) state = 2; // exit number state
if (state != 2 || !inparens) return false; // invalid end parens
inparens = false; // end parens mode
break;
default:
// oh oh, we hit a bad character
return false;
}
}
// must have at leats one digit, and cannot have imbalanced parenthesis
if (!hasdigits || inparens) return false;
// if we got all the way to here...
return true;
}

Categories