JAL Computing

C++COMProgramming .NET Mac Palm CPP/CLI Hobbies

 

Home
Up

Chapter 36 "Try, out"

In Chapter 14, I suggested that not all methods should throw an exception on error.  Sometimes it is simply more convenient to return false on error. If a method needs to return multiple "values", this can be accomplished by returning a structure or object. In .NET 2.0 the new TryParse methods seem to justify this position. However, the TryParse methods use an out parameter to return the converted value. 

According to MSDN the Double.TryParse method: "is like the Parse method, except this method does not throw an exception if the conversion fails. If the conversion succeeds, the return value is true and the result parameter is set to the outcome of the conversion. If the conversion fails, the return value is false and the result parameter is set to zero."

In this chapter, I am going to convert our mortgage calculator program to use the new version 2.0 "try, out" semantics. Here again is the method GetParameters from the m_C_v mortgage calculator program that returns a result structure. This method checks for pre-condition violations and returns both the success flag and the result in the return structure. The caller checks for the success flag and then extracts the result from the return structure on success like this:

            Mortgage.ParametersBoolStruct boolStruct = formView.GetParameters();
            if (boolStruct.IsSuccess && (boolStruct.Parameters.Target != Mortgage.INVALID))
            {
                controllerCurrent.Update(boolStruct.Parameters);
            }

In the "try, out" version, the caller declares a variable of the proper type and passes this variable as a parameter to the TryGetParameters method explicitly using the out keyword as in:

            Mortgage.Parameters parameters;
            bool success= formView.TryGetParameters(out parameters); // caller must specify out
            if (success && (parameters.Target != Mortgage.INVALID))
            {
                controllerCurrent.Update(parameters);
            }

If the caller does not specify out in the method call, the call will not compile. 

Note: The caller does not need to initialize the out parameter, but the method does need to assign a value to the out parameter before the method returns.

Here is the new TryGetParameters method using an out parameter:

        // version that uses out parameter
        public bool TryGetParameters(out Mortgage.Parameters result)
        {
            double principal = 0;
            double interest = 0;
            int months = 0;
            double payment = 0;
            bool isInputError = false;
            int target = Mortgage.INVALID;
            Mortgage.Parameters invalidParameters = new Mortgage.Parameters(principal,
                                                            interest,
                                                            months,
                                                            payment,
                                                            Mortgage.INVALID);
            result = invalidParameters;  // default out
            double[] arrayDb = new double[4];
            // validate user input, must allow zero
            try
            {
                principal = Double.Parse(textBoxPrincipal.Text);
                if (principal < 0)
                {
                    throw new Exception();
                }
                arrayDb[Mortgage.PRINCIPAL] = principal;
            }
            catch
            {
                textBoxPrincipal.Text = "Invalid Input.";
                isInputError = true;
            }
            try
            {
                interest = Double.Parse(textBoxInterest.Text);
                if ((interest < 0) || (interest > 100))
                {
                    throw new Exception();
                }
                arrayDb[Mortgage.INTEREST] = interest;
            }
            catch
            {
                textBoxInterest.Text = "Invalid Input.";
                isInputError = true;
            }
            try
            {
                months = Int32.Parse(textBoxMonths.Text);
                if (months < 0)
                {
                    throw new Exception();
                }
                arrayDb[Mortgage.MONTHS] = months;
            }
            catch
            {
                textBoxMonths.Text = "Invalid Input.";
                isInputError = true;
            }
            try
            {
                payment = Double.Parse(textBoxPayment.Text);
                if (payment < 0)
                {
                    throw new Exception();
                }
                arrayDb[Mortgage.PAYMENT] = payment;
            }
            catch
            {
                textBoxPayment.Text = "Invalid Input.";
                isInputError = true;
            }
            if (isInputError)
            {
                return false;  // result is invalidParameters
            }
            // one, and only one, "value" must be zero --> target
            int zeros = 0;
            for (int index = 0; index < 4; index++)
            {
                if (arrayDb[index] == 0)
                {
                    zeros++;
                    target = index;
                }
            }
            if (zeros > 1)
            {
                textBoxMessage.Text = "Too many zero parameters.";
                isInputError = true;
                return false;
            }
            if (zeros == 0)
            {
                textBoxMessage.Text = "One value must be zero.";
                isInputError = true;
                return false;
            }
            // success
            Mortgage.Parameters validParameters = new Mortgage.Parameters(principal,
                                                       interest,
                                                       months,
                                                       payment,
                                                       target);
            result = validParameters;
            return true;
        }

Struct vs Out

The single out parameter of the "try, out" approach is superior in that it avoids the need to declare a return structure, the Mortgage.ParametersBoolStruct. So why not just use out for all method calls that must return more than one value? The answer is simplicity. If you have multiple parameters, returning a structure or class is much simpler to understand and less error prone than returning multiple out parameters. Here again is our Calculate method using structures:

            public Mortgage.Calculation Calculate(Mortgage.Parameters m)
            {
                return Calculate(m.Principal, m.Interest, m.Months, m.Payment, m.Target);
            }

Pretty darn simple. Now here is the same call that uses multiple out parameters:

            private bool TryCalculate(Mortgage.Parameters p,
					out double principal,
					out double interest,
					out int months,
					out double payment,
					out int target)
            {
                Mortgage.Calculation result= this.Calculate(p.Principal, p.Interest, p.Months, p.Payment, p.Target);
                principal = result.Principal;
                interest = result.Interest;
                months = result.Months;
                payment = result.Payment;
                target = result.Target;
                return result.IsValid();
            }

You can only imagine the horror of trying to use a Calculate method does not use a either a Mortgage.Parameters or a Mortgage.Calculation structure. Using object oriented structures or classes instead of multiple out parameters simplifies coding and reduces the likelihood of error. It also allows the reuse of the return structure to pass the result between application "layers".

So to summarize, try to use return "values" rather than use out or ref. .NET 2.0 has introduced the "try, out" idiom that returns a single extra parameter using the out semantics and returns false on error. In the case of returning a single value using out, there is little likelihood of confusion. Prefer returning multiple parameters using an object oriented structure or class rather than multiple out parameters.

Download Project

You can download the modified version: M_C_V36.zip

Learn More

MSDN: Double.TryParse

MSDN: out

- 30 -
Send mail to [email protected] with questions or comments about this web site. Copyright © 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 © 
Last modified: 08/04/09
Hosted by www.Geocities.ws

1