The following generic static method takes a string and returns an enum.
It nicely ignores case since I set the ignoreCase parameter to true.
However,开发者_如何学Go I also want to test if the enum exists, but the enum.IsDefined method to do this doesn't seem to have an ignoreCase parameter.
How can I test if the enum is defined or not and at the same ignore case?
using System;
namespace TestEnum2934234
{
class Program
{
static void Main(string[] args)
{
LessonStatus lessonStatus = StringHelpers.ConvertStringToEnum<LessonStatus>("prepared");
ReportStatus reportStatus = StringHelpers.ConvertStringToEnum<ReportStatus>("finished");
Console.WriteLine(lessonStatus.ToString());
Console.WriteLine(reportStatus.ToString());
Console.ReadLine();
}
}
public static class StringHelpers
{
public static T ConvertStringToEnum<T>(string text)
{
if (Enum.IsDefined(typeof(T), text)) //does not have ignoreCase parameter
return (T)Enum.Parse(typeof(T), text, true);
else
return default(T);
}
}
public enum LessonStatus
{
Defined,
Prepared,
Practiced,
Recorded
}
public enum ReportStatus
{
Draft,
Revising,
Finished
}
}
public enum MyEnum
{
Bar,
Foo
}
class Program
{
static void Main(string[] args)
{
var containsFoo = Enum.GetNames(typeof(MyEnum)).Any(x => x.ToLower() == "foo");
Console.WriteLine(containsFoo);
}
}
Along with @Darin's answer, in .NET 4.0, the Enum type now has a TryParse method:
MyEnum result;
Enum.TryParse("bar", true, out result);
The important thing to remember is that there is a fundamental difference in the behaviour of Parse vs TryParse. Parse methods will throw exceptions. TryParse methods will not. This is quite important to know if you are potentially trying to parse many items.
You might be able to get away with simply using Enum.TryParse
, as others have said.
However, if you want a more robust/general conversion that allows you to convert more than just strings, then you need to also use Enum.IsDefined
, which unfortunately, as you found, is not case-insensitive.
Enum.TryParse
is (can be) case-insensitive. But unfortunately, it allows out-of-range ints to get through!
So the solution is to use them together (and the order is important).
I wrote an extension method that does just that. It allows conversion from string, int/int?, and any other Enum/Enum? type like so:
string value1 = "Value1";
Enum2 enum2 = value1.ParseToEnum<Enum2>();
Debug.Assert(enum2.ToString() == value1);
Enum1 enum1 = Enum1.Value1;
enum2 = enum1.ParseToEnum<Enum2>();
Debug.Assert(enum2.ToString() == enum1.ToString());
int value2 = 1;
enum2 = value2.ParseToEnum<Enum2>();
Debug.Assert(enum2.GetHashCode() == value2);
Here's the heart of the method. This is the conversion part that answers your question. The variable value
is of type object
because of the "overloads" I have that take different types as the main input (see above), but you can do this with a variable of type string
just fine if that's all you want (obviously changing value.ToString()
to just value
).
if (value != null)
{
TEnum result;
if (Enum.TryParse(value.ToString(), true, out result))
{
// since an out-of-range int can be cast to TEnum, double-check that result is valid
if (Enum.IsDefined(typeof(TEnum), result.ToString()))
{
return result;
}
}
}
There's a lot more to my extension method... it allows you to specify defaults, handles out-of-range ints just fine, and is fully case-insensitive. I can post more of it if anybody's interested.
Use Enum.TryParse instead:
T val;
if(Enum.TryParse(text, true, out val))
return val;
else
return default(T);
I'm using Compact Framework 3.5, and:
Enum.TryParse
...doesn't exist. It does have:
Enum.IsDefined
..but that doesn't support the ignoreCase parameter. I'd like the best of both worlds, so came up with this (as a helper method)...
public bool TryParse<TEnum>(string value, bool ignoreCase, ref TEnum result) where TEnum : struct
{
bool parsed;
try
{
result = (TEnum)Enum.Parse(typeof(TEnum), value, ignoreCase);
parsed = true;
}
catch { }
return parsed;
}
HTH
enum DaysCollection
{
sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
}
public bool isDefined(string[] arr,object obj)
{
bool result=false;
foreach (string enu in arr)
{
result = string.Compare(enu, obj.ToString(), true) == 0;
if (result)
break;
}
return result;
}
private void button1_Click(object sender, EventArgs e)
{
object obj = "wednesday";
string[] arr = Enum.GetNames(typeof(DaysCollection)).ToArray();
isDefined(arr,obj);
}
Make text same case as enum string:
enum FileExts
{
jpg,
pdf
}
if (Enum.IsDefined(typeof(T), text.tolower())) //does not have ignoreCase parameter
return (T)Enum.Parse(typeof(T), text, true);
else
return default(T);
I had a similar concern and used a combination of both the .Enum.TryPase
(with the case-insensitive flag set as true
) and Enum.IsDefined
. Consider the following as a simplification to your helper class:
public static class StringHelpers
{
public static T ConvertStringToEnum<T>(string text)
{
T result;
return Enum.TryParse(text, true, out result)
&& Enum.IsDefined(result.ToString())
? result
: default(T);
}
}
And while we're at it, since the helper class is static and the method is static - we could make this an extension method on string
.
public static class StringExtensions
{
public static TEnum ToEnum<TEnum>(this string text)
where TEnum : struct, IComparable, IFormattable, IConvertible
{
TEnum result = default(TEnum);
return !string.IsNullOrWhiteSpace(text)
&& Enum.TryParse(text, true, out result)
&& Enum.IsDefined(typeof(TEnum), result.ToString())
? result
: default(TEnum);
}
}
Here, I created a .NET Fiddle
that clearly demonstrates this.
First use Enum.TryParse
method to get an object of type T
, then pass that object to Enum.IsDefined
method:
private static T ConvertStringToEnum<T>(string stringValue) where T : struct
{
if (System.Enum.TryParse(stringValue, out T result))
{
if (System.Enum.IsDefined(typeof(T), result) || result.ToString().Contains(","))
return result;
throw new System.Exception($"{stringValue} is not an underlying value of the {typeof(T).FullName} enumeration.");
}
throw new System.Exception($"{stringValue} is not a member of the {typeof(T).FullName} enumeration.");
}
public static T ConvertStringToEnum<T>(string text)
{
T returnVal;
try
{
returnVal = (T) Enum.Parse( typeof(T), text, true );
}
catch( ArgumentException )
{
returnVal = default(T);
}
return returnVal;
}
精彩评论