5

I have an method for enums that looks like this:

public static TEnum GetEnumByStringValue<TEnum>(string value) where TEnum : struct, IConvertible, IComparable, IFormattable
{
  if(!typeof(TEnum).IsEnum)
  {
    throw new ArgumentException("TEnum must be an enumerated type.");
  }

  Type type = typeof(TEnum);
  FieldInfo[] fieldInfos = type.GetFields();
  foreach (FieldInfo fieldInfo in fieldInfos)
  {
    StringValue[] stringValues = fieldInfo.GetCustomAttributes(typeof(StringValue), false) as StringValue[];
    if (stringValues != null)
    {
      foreach (StringValue stringValue in stringValues)
      {
        if (stringValue.Value.Equals(value))
        {
          return (TEnum)Enum.Parse(typeof(TEnum), fieldInfo.Name);
        }
      }
    }

  }
  throw new ArgumentOutOfRangeException("value", "Value was not found in enum's string values.");
}

I'd like to implement a TryGetEnumByStringValue, that returns true or false instead of throwing an exception similar to the concept of int.Parse and int.TryParse. The way I see it, in my new method I could just call my other one, catch the exceptions (if any) and return accordingly, or I could refactor the existing one to return bool and again in my new method simply call the existing one and throw an exception if it returns false.

If I go with option 2 I lose exact exception details, and if I go with option 1 the exceptions are still thrown (I've always been taught exceptions are slow).

I could also refactor the existing one to take a bool indicating whether to throw exceptions or not, but that doesn't quite sit right with me.

Is there a pearl of wisdom I've missed for this sort of method style or pattern?

ldam
  • 4,412
  • 6
  • 45
  • 76
  • I'd simply use the method that you already have, no need to copy the entire logic, just re-use it – Fabjan May 31 '16 at 13:08
  • Please check the following link as example: [link](http://stackoverflow.com/questions/15294878/how-the-int-tryparse-actually-works) `int` also reuses it. – wake-0 May 31 '16 at 13:11
  • Your idea of passing a bool to indicate if it should throw could be applied to a private method and then your public methods would call that with the appropriate bool. – juharr May 31 '16 at 13:26

1 Answers1

2

If you already have a method that throws, then it's easy to make a Try... variant of it using ... suprise! try/catch:

public bool TryReturnSomething(..., out SomeType result) // ... - parameters
{
    try
    {
        result = ReturnSomething();
        return true;
    }
    catch(SomeException1 e) { } // catch all expected exception types
    catch(SomeException2 e) { }

    return false;
}

Looking into sources you will discover that Microsoft is indeed using this kind of pattern. They have an internal method which is called with validated parameters. And validation is done individually by Try... and a normal variant. See e.g. double.Parse() and double.TryParse(). The first one will throw for invalid input and the other will return false.

So you can create a private method which you call from both method variants. This private method should not validate anything (may raise an exception). Call this method in both public variants, which both validate parameters. (Try.. returns false and the other throws.)

mfluehr
  • 2,832
  • 2
  • 23
  • 31
Sinatr
  • 20,892
  • 15
  • 90
  • 319
  • 5
    But please take into account that normally you would use the TryGet pattern for performance issues so you would never throw an exception within this method, because this would slow down the execution of the method dramatically. See the [Microsoft design guideline](https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/exceptions-and-performance) here for more information - it is called Try-Parse pattern here. – Octoate Feb 06 '19 at 12:39
  • 1
    Note that you will need to either assign `result` before the `try` or after the last `catch`, since `out` parameters *must* be assigned some value by the method. Usually if the "success" path assignment fails, the value is set to the default for the type: `result = default(SomeType);` – Rufus L Mar 07 '19 at 15:26
  • The second paragraph is kind of misleading, "this kind of pattern" sounds like it means the pattern just discussed, always throwing and then in the Try variant catching. But that is not what's done. I suggest replacing "Microsoft is indeed using this kind of pattern" by "Microsoft has developed a reusable pattern, as follows" – Ben Voigt Jul 21 '23 at 15:37