Home > Software engineering >  How to replace System.FormatException
How to replace System.FormatException

Time:12-19

I'm currently learning c# and trying to make a simple calculation app. When the user inputs a letter instead of a number, this exception is thrown. How do I handle it so that instead of this, I can output "invalid" and allow the user to try again?

I've been looking at try-catch but I'm not sure if I'm on the right track.

CodePudding user response:

First off, Double.TryParse is what you'll want to use. If you're doing any form of division it'll get around the rounding issues involved with using an integer. (see more)

public double?[] ConvertInputs(string[] inputs)
{//We're taking an array of inputs to make it easier for the calculator to perform compound expressions.
    if (inputs is null)
    {//This should never happen, don't try/catch this just prevent it.
        throw new ArgumentNullException(nameof(inputs));
    }

    double?[] values = new double?[inputs.Length];
    for (int i = 0; i < inputs.Length; i  )
    {
        values[i] = Double.TryParse(inputs[i], out double value) ? value : null;
    }

    return values;
}

Then we'll use Array.IndexOf to check for any inputs which weren't valid:

public void Calculate(string[] inputs)
{
    if (inputs is null)
    {//This should never happen, don't try/catch this just prevent it.
        throw new ArgumentNullException(nameof(inputs));
    }

    double?[] values = this.ConvertInputs(inputs);
    if (values.Length == 0)
    {
         //user didn't give you any parameters, inform them of that!
         return;
    }  

    int errorIndex = Array.IndexOf(values, null);
    if (int != -1)//-1 is return for the value (in this case null) not being present.
    {
        string invalidInput = inputs[errorIndex];
        //tell the user this input was not valid.
        return;
    }

    //Do your calculation here. None of the values are null.
}

For a simpler quick and dirty way of doing this, see below.

public void Calculate(string inputA, string inputB)
{
    if (!Double.TryParse(inputA, out valueA) || !Double.TryParse(inputB, out valueB))
    {//tell your user something's invalid.
        return;
    }

    //Perform your calculation with valueA and valueB, they're both initialized.
}

CodePudding user response:

As some coments above said, there are some ways to handle it.

I also prefer to use int.TryParse in order to validate if the input is valid or not.

To summarize it. I would do create a method to wrap this validation:

public bool IsValidCalculationInput(string? inputA, string? inputB)
{
    if (string.IsNullOrWhiteSpace(inputA)) return false;
    if (string.IsNullOrWhiteSpace(inputB)) return false;
    if (!inputA.TryParseInt(out _)) return false;
    if (!inputB.TryParseInt(out _)) return false;
    return true;
}

Also I would create some extensions helping me handle this validations, in my case I've created a Class called IntegerExtensions:


public static class IntegerExtensions
{
    
    /// <summary>
    /// Gets generic type and implicit calls ToString()  method trying to convert a generic to int
    /// This methods doesnt throws exceptions and returns int.MinValue if conversion fails
    /// You can also force it to throw an exception by setting the throwException parameter to true
    /// </summary>
    /// <param name="value">Value to be parsed</param>
    /// <param name="result">Parsed value</param>
    /// <typeparam name="T">Generic Type</typeparam>
    /// <returns></returns>
    public static bool TryParseInt<T>(this T value, out int result, bool throwException =false)
    {
        try
        {
            return int.TryParse(value.ToString(), out result);
        }
        catch (Exception ex)
        {
            if (throwException) throw;
            result = Int32.MinValue;
            return false;
        }
    }
    
    /// <summary>
    /// Explicit calls ToString()  method converting a generic to int throwing an exception if it fails
    /// </summary>
    /// <param name="value"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static int ToInt<T>(this T value)
    {
        return int.Parse(value.ToString());
    }
}

In the end I would call it like this:

public void Calculate(string? inputA, string? inputB)
{
    if (!IsValidCalculationInput(inputA, inputB))
    {
        // Here you can handle the invalid result and return an class/throw an exception, it's your call.
        // In this case my method returns void, so I just return.
        return;
    }

    var parsedInputA = inputA.ToInt();
    var parsedInputB = inputB.ToInt();
    // Do the calculation
    // return parsedInputA   parsedInputB;

}

EDITS

I'm updating the IsValidCalculationInput to achive some improvements suggested in comments:

Now it will receive N parameters than it will return if the whole list of parameters is valid for calculation.

bool IsValidCalculationInput(out int?[] outputs,params string?[] inputs)
{
    outputs = new int?[inputs.Length];
    for (var i = 0; i < inputs.Length; i  )
    {
        if (string.IsNullOrWhiteSpace(inputs[i]) || !int.TryParse(inputs[i], out var parsedInput))
        {
            outputs[i] = null;
            return false;
        }

        outputs[i] = parsedInput;
    }
    return true;
}

Then you can use it like:

void Calculate(string inputA, string? inputB)
{
    if (!IsValidCalculationInput(out var inputsParsed,inputA, inputB))
    {
        // Here you can handle the invalid result and return an class/throw an exception, it's your call.
        // In this case my method returns void, so I just return.
        return;
    }
    
    // Do the calculation
    // return parsedInputA   parsedInputB;
    var result = 0;
    foreach (var input in inputsParsed)
        result = result   input.Value;

}
  • Related