tags:

views:

2895

answers:

11

I want to write a utility wherein can validate a given value (string) against possible values of enum. And in case of match, return the enum instance, else return a default value. PS: I don't want to user Try...catch here.

public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue){
   TEnum enumValue;
   if(!TryParse(typeof(TEnum),strEnumValue, out enumValue)){ //Looking for implementation??????
return defaultValue;
   }
return enumValue;
}

I am looking for some optmized implementation of "TryParse..." here. No try...catch or Enum.GetNames() solution, please.

[Edit] I know about Enum.Parse(typeof(TEnum), strEnumValue) method. It throw ArgumentException if strEnumValue is not valid.

Looking for TryParse........

Thanks

A: 

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

Spence
I know about Enum.Parse(typeof(TEnum), strEnumValue) method.It throws ArgumentException if strEnumValue is not valid.Looking for TryParse........
Amby
+1  A: 

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 :)

Philippe Leybaert
It's not the *only* way. Enum.IsDefined(..) will prevent exceptions being thrown in user code.
Thorarin
+2  A: 

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.

Ahmad Mageed
+1  A: 

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?

Nader Shirazie
Idea is to optimize CPU. Agree that i can do it at the cost memory. But its not the solution i am looking for. Thanks.
Amby
+3  A: 

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

Richard
I was going to suggest using default(T) to set the default value. Turns out this wouldn't work for all enums. E.g. If the underlying type for the enum was int default(T) will always return 0, which may or may not be valid for the enum.
Daniel Ballinger
+15  A: 

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.

Thorarin
Best answer I've seen so far... no try/catch, no GetNames :)
Thomas Levesque
Drawbacks to Enum.IsDefined: http://blogs.msdn.com/brada/archive/2003/11/29/50903.aspx
Nader Shirazie
GetNames() has the same drawbacks...
Thorarin
also there is no ignore case on IsDefined
Anthony Johnston
@Anthony: if you want to support case insensitivity, you would need `GetNames`. Internally, all these methods (including `Parse`) use `GetHashEntry`, which does the actual reflection - once. On the bright side, .NET 4.0 has a TryParse, and it's generic too :)
Thorarin
+6  A: 

As others have said, you have to implement your own TryParse. If you are using bitfield enums, you also have to handle a string like "MyEnum.Val1|MyEnum.Val2". If you just call Enum.IsDefined with this string, it will return false, even though Enum.Parse handles it correctly.

Idex
A: 

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
+1  A: 

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 :)

Jon Skeet
A: 

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.

Naveed Ahmed
what is this doing "Enum.GetName(typeof(HLink.ViewModels.ClaimHeaderViewModel.ClaimStatus), EnumValue)"Probably some dependency on your local code.
Amby
A: 

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); } } //======================================================= //Service provided by Telerik (www.telerik.com) //Conversion powered by NRefactory. //Twitter: @telerik, @toddanglin //Facebook: facebook.com/telerik //=======================================================

ben