9

When parsing user input, it is generally recommended to not to throw and catch exceptions but rather to use validation methods. In the .NET BCL, this would be the difference between, for example, int.Parse (throws an exception on invalid data) and int.TryParse (returns false on invalid data).

I am designing my own

Foo.TryParse(string s, out Foo result)

method and I'm unsure about the return value. I could use bool like .NET's own TryParse method, but that would give no indication about the type of error, about the exact reason why s could not be parsed into a Foo. (For example, s could have unmatched parenthesis, or the wrong number of characters, or a Bar without a corresponding Baz, etc.)

As a user of APIs, I strongly dislike methods which just return a success/failure Boolean without telling me why the operation failed. This makes debugging a guessing game, and I don't want to impose that on my library's clients either.

I can think of a lot of workarounds to this issue (return status codes, return an error string, add an error string as an out parameter), but they all have their respective downsides, and I also want to stay consistent with the conventions of the .NET Framework.

Thus, my question is as follows:

Are there methods in the .NET Framework which (a) parse input without throwing exceptions and (b) still return more detailed error information than a simple true/false Boolean?

  • 1
    That link does not conclude it is not recommended to throw and catch exceptions. There are times that the best way is to use Parse(). – paparazzo Jan 23 '18 at 21:23
4

I would recommend using the monad pattern for your return type.

ParseResult<Foo> foo = FooParser.Parse("input");

Note also, it should not be Foo's responsibility to figure out how it should be parsed from user input as this directly binds your domain layer to your UI layer, and also violates the single responsibility principal.

You can also make a parse result class specific to Foo instead of using generics, depending on your use case.

A foo specific parse result class might look something like this:

class FooParseResult
{
     Foo Value { get; set; }
     bool PassedRequirement1 { get; set; }
     bool PassedRequirement2 { get; set; }
}

Here is the Monad version:

class ParseResult<T>
{
     T Value { get; set; }
     string ParseErrorMessage { get; set; }
     bool WasSuccessful { get; set; }
}

I am not aware of any methods in the .net framework that return detailed parse error information.

  • I understand your comment about UI layer binding, but in this case there exists a standardized, canonical string representation of Foo, so it makes sense to have Foo.ToString and Foo.Parse. – Heinzi Jan 22 '18 at 15:41
  • And, w.r.t. my bolded question, can you give me an example from the .NET BCL that uses this pattern? – Heinzi Jan 22 '18 at 15:46
  • 3
    How is that a monad? – JacquesB Jan 22 '18 at 15:49
  • @Heinzi: Any method that returns a Func<T> would meet that criteria, if you include in T the information you need. Returning detailed error information is largely up to you. Have you considered using a Maybe<T>? See mikhail.io/2016/01/monads-explained-in-csharp – Robert Harvey Jan 22 '18 at 15:49
  • @JacquesB: I was kinda wondering the same thing. The method signature is compatible with modanic behavior, but that's about it. – Robert Harvey Jan 22 '18 at 15:50
1

You could look at ModelState in the MVC framework. It represent an attempted parse of some input and may have a collection of errors.

That said, I don't think there is a recurring pattern for this in the .net BCL, since exceptions is - for better or worse - the established pattern for reporting error conditions in .net. I think you should just go ahead and implement your own solution suiting your problem, for example a ParseResult class with two subclasses, SuccessfulParse and FailedParse, where SuccessfulParse has a property with the parsed value and FailedParse has an error message property. Combining this with pattern matching in C# 7 could be pretty elegant.

1

I've run into similar problems with wanting to use a TryParse/Convert/etc. method where I sometimes need to know how and why it failed.

I ended up taking inspiration from how some serializers handle errors and using events. This way the syntax for my TryX(..., out T) method looks as clean as any other and reliably returns a simple false as the pattern implies.

However, when I want need more details I just add an Event Handler and get whatever results I need in a package as complex or simple as I want (the MyEventArgs below). Add it to a list of strings, add an ExceptionDispatchInfo and capture Exceptions; let the caller decide if and how it wants to deal with anything that goes wrong.

public class Program
{
    public static void Main()
    {
        var c = new MyConverter();

        //here's where I'm subscibing to errors that occur
        c.Error += (sender, args) => Console.WriteLine(args.Details);

        c.TryCast<int>("5", out int i);
    }
}

//here's our converter class
public class MyConverter
{
    //invoke this event whenever something goes wrong and fill out your EventArgs with details
    public event EventHandler<MyEventArgs> Error;

    //intentionally stupid implementation
    public bool TryCast<T>(object input, out T output)
    {
        bool success = true;
        output = default (T);

        //try-catch here because it's an easy way to demonstrate my example
        try
        {
            output = (T)input;
        }
        catch (Exception ex)
        {
            success = false;
            Error?.Invoke(this, new MyEventArgs{Details = ex.ToString()});
        }

        return success;
    }
}

//stores whatever information you want to make available
public class MyEventArgs : EventArgs
{
    public string Details {get; set;}
}
New contributor
Chakrava is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
0

First, understand the reasons for the recommendations you are quoting in your question.

The reason that it's recommended not to throw exceptions when parsing is because throwing an exception is expensive. If you're parsing a data set with a nested loop (one loop for rows, one loop for columns) and you're throwing an exception in the inner loop because one of the columns has an alphanumeric character in it, your parse routine will grind to a screeching halt.

In that event, you have three choices:

  1. Throw early and fail fast, or
  2. Use TryParse. If an error occurs, repeat with Parse, throw once to get the error information, and continue without parsing the data in the bad column.
  3. Write your own parse method that uses TryParse initially, and if an error occurs, have some custom logic that can determine why the parse failed without throwing an exception.

As other answers have observed, it's easy enough to create an object that contains either a value or an error. It's also easy enough to fashion your own TryParseWithErrorInformation() method using this tactic, provided your mindful of the .

-5

How often do you have a Class that will parse from string? You can New and have a string constructor.

If you look under the covers there is not much different other than throw the exception.

It would be pretty easy to have TryParseInt32 return the reason as an output.

Often an Exception is indeed an exception and Try Catch is what people want to use.

Not ware of any .NET parse that return bool and reason and does not throw an Exception.

A separate TryParse has the down side of more methods to test. TryParseReason is another method to test.

    [System.Security.SecuritySafeCritical]  // auto-generated
    internal unsafe static Int32 ParseInt32(String s, NumberStyles style, NumberFormatInfo info) {

        Byte * numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
        NumberBuffer number = new NumberBuffer(numberBufferBytes);
        Int32 i = 0;

        StringToNumber(s, style, ref number, info, false);

        if ((style & NumberStyles.AllowHexSpecifier) != 0) {
            if (!HexNumberToInt32(ref number, ref i)) { 
                throw new OverflowException(Environment.GetResourceString("Overflow_Int32"));
            }
        }
        else {
            if (!NumberToInt32(ref number, ref i)) {
                throw new OverflowException(Environment.GetResourceString("Overflow_Int32"));
            }
        }
        return i;           
    }


    [System.Security.SecuritySafeCritical]  // auto-generated
    internal unsafe static Boolean TryParseInt32(String s, NumberStyles style, NumberFormatInfo info, out Int32 result) {

        Byte * numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
        NumberBuffer number = new NumberBuffer(numberBufferBytes);
        result = 0;

        if (!TryStringToNumber(s, style, ref number, info, false)) {
            return false;
        }

        if ((style & NumberStyles.AllowHexSpecifier) != 0) {
            if (!HexNumberToInt32(ref number, ref result)) { 
                return false;
            }
        }
        else {
            if (!NumberToInt32(ref number, ref result)) {
                return false;
            }
        }
        return true;           
    }
  • The love on this site never ends – paparazzo Jan 24 '18 at 1:34
  • 1
    Well, your second method doesn't return detailed information about why the parse failed; it only returns a Boolean. Further, I don't see how this method adds any value beyond the already robust Int32.TryParse() method. – Robert Harvey 15 hours ago

protected by gnat 14 hours ago

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.