xsharp.eu • Error handling in X# vs VO
Page 1 of 3

Error handling in X# vs VO

Posted: Fri Jul 26, 2019 9:49 am
by wriedmann
Hi,

in my VO code I'm using begin/end sequence very often to control where an error is occurring, and to inhibit that the program crashes because of a foreseable error.
The globally set errorblock helps a lot to keep my code small.

In X# there is no global error handler available. Therefore I'm thinking about using try/end try in much fewer places because the needed local error handling blows up my code - I have methods where the error handling has more lines of code than the processing itself, and there is the risk to not see the error because it is handled internally and not passed up to the calling code.

I know I can throw my own exception, and pass the current exception as InnerException, but I have yet to find my solution how to
handle this entire thing.

Maybe someone of you has some suggestion.....

Wolfgang

Error handling in X# vs VO

Posted: Fri Jul 26, 2019 10:06 am
by ArneOrtlinghaus
I have regretted inserting begin/end/try blocks without recover section. So I have tried to check every such usage on correct error handling in the same code part or a code part in an upper code part. I have tried to use a global try block, but together with the GUI-Classes this cannot trap errors correctly if errors appear in events. So a main task in our preparation to X# was to insert as many additional begin/end/try blocks in program parts that call much other code. For example a pushbutton method should include such a blocks . For avoiding having to insert such a sequence in every application pushbutton method, I have modified methods in the GUI-Classes and in our framework derived from the GUI-Classes so that these basic mechanisms contain already correct handling of exceptions.

(Exception handling is nice, but also risky. I remember a project a colleague made in JAVA many years ago. He was so proud handling EVERYTHING with exceptions. But he forgot to decode the exceptions correctly in the calling parts. So we spent much time discovering the real reason for strange exceptions happening at the customer's site that put us in complete wrong directions for searching the reason.)

Error handling in X# vs VO

Posted: Fri Jul 26, 2019 11:23 am
by NickFriend
Hi Wolfgang,

I have a different approach.... I never allow exceptions to propagate up through code. Any method that may be subject to some sort of exception (eg anything accessing data) always returns a 'Result' object that can wrap up any error.

Let's say I have a method that should return an int. In the method I'll use try/catch as normal, but the return type of the method won't be int, it'll be MyResultObject<int>. In the calling method you can then interrogate the result object to see what happened, without having to deal directly with an exception.

My result class is something like this...

Code: Select all

public class CIOResult<T>
{
    /// <summary>
    /// Enum of different outcomes eg Success, NotFound, etc
    /// </summary>
    public CIOResultType Result { get; set; }

    /// <summary>
    /// Optional Exception object that can be returned for debugging.
    /// </summary>
    public Exception CIOException { get; set; }

    /// <summary>
    /// Construtor used when a call has failed, and an exception is to be returned.
    /// </summary>
    /// <param name="result">Result enum value.</param>
    /// <param name="excep">Exception object.</param>
    public CIOResult(CIOResultType result, Exception excep)
    {
        Result = result;
        CIOException = excep;
    }

    /// <summary>
    /// Constructor used when a value is to be returned.
    /// </summary>
    /// <param name="result">Result enum value.</param>
    /// <param name="cargo">Data object to be returned.</param>
    public CIOResult(CIOResultType result, T cargo)
    {
        Result = result;
        Cargo = cargo;
    }

    /// <summary>
    /// Constructor used when only the result is to be returned.
    /// </summary>
    /// <param name="result">Enum value of result.</param>
    public CIOResult(CIOResultType result)
    {
        Result = result;
    }
    
    /// <summary>
    /// Constructor for successful completion and to return a data object.
    /// </summary>
    /// <param name="cargo">Data object to return.</param>
    public CIOResult(T cargo) : base()
    {
        Result = CIOResultType.success;
        Cargo = cargo;
    }

    /// <summary>
    /// Constructor for successful completion but without a data object.
    /// </summary>
    public CIOResult()
    {
        Result = CIOResultType.success;
    }

    /// <summary>
    /// Data object to be included in the CIOResult object.
    /// </summary>
    public T Cargo { get; set; }
}
Obviously if there's some runtime error that's not caught by this it'll still blow up the program, but this organises everything else and makes error trapping much more manageable.

Not sure if I answered the question or not to be honest!!

Nick

Error handling in X# vs VO

Posted: Fri Jul 26, 2019 11:38 am
by wriedmann
Hi Nick,

Any method that may be subject to some sort of exception (eg anything accessing data) always returns a 'Result' object that can wrap up any error.

This is really a nice idea!
I have to think about, but it could be that I will steal it <g>.

Wolfgang

Error handling in X# vs VO

Posted: Fri Jul 26, 2019 12:19 pm
by wriedmann
Hi Nick,
based on your code I have now written the following class:

Code: Select all

public class ResultEx<T>
	protect _oResult as T
	protect _oException as Exception
	protect _oCargo as object

constructor( oResult as T, oException as Exception, oCargo as object )

	_oResult := oResult
	_oException := oException
	_oCargo := oCargo

	return

constructor( oResult as T, oException as Exception )

	self( oResult, oException, null )

	return

constructor( oResult as T )

	self( oResult, null, null )

	return

constructor( oResult as T, oCargo as object )

	self( oResult, null, oCargo )

	return

public property Result as T get _oResult set _oResult := value
public property Exception as Exception get _oException set _oException := value
public property Cargo as object get _oCargo set _oCargo := value

end class
And this a sample how to use it:

Code: Select all

function TestResultEx( lSuccess as logic ) as void
	local oResult			as ResultEx<logic>

	oResult := ExecuteTestResultEx( lSuccess )

	if oResult:Result
		System.Console.WriteLine( "Execution succeeded" )
	else
		System.Console.WriteLine( String.Format( "Execution failed with error message '{0}'", oResult:Exception:Message ) )
	endif

function ExecuteTestResultEx( lSuccess as logic ) as ResultEx<logic>
	local oResult as ResultEx<logic>

	oResult := ResultEx<logic>{ true }

	try

	if lSuccess == false
		throw Exception{ "I have to fail" }
	endif

	catch oEx as Exception

	oResult:Exception	:= oEx
	oResult:Result		:= false

	end try

	return oResult
Thank you again!

Wolfgang

P.S. Did I mentioned that I like generics?

Error handling in X# vs VO

Posted: Fri Jul 26, 2019 1:43 pm
by wriedmann
Hi Arne,

I have regretted inserting begin/end/try blocks without recover section.

In VO, this works well because of the global error handler.
Such a code in VO does what it should:

Code: Select all

function MyFunc() as logic
local lReturn as logic

lReturn := true

begin sequence

< processing something >

recover

lReturn := false

end sequence

return lReturn
because it shows an eventual error message using the global error handler.
But in .NET this does not reports the error, unfortunately.
I have lots of these constructs in my code, unfortunately.
Wolfgang

Error handling in X# vs VO

Posted: Fri Jul 26, 2019 1:48 pm
by NickFriend
Hi Wolfgang,

That's not quite how I'd do it. My intention was that Cargo is the generic object to hold any data or return value from the method call, and Result is something to give info on the actual process (success, failure, no data found, exception thrown, etc), so can be an enum of pre-established values to describe different outcomes.

So when you return a result object, the generic T type is the return type, not the type of process result. And the process outcome can be used to direct code for error handling etc in the calling method.

So if you had ResultEx<MyObject> as a return value from a method you'd know there was an object of type MyObject in Cargo and Result contains something like success/failure/notfound which gives you info about the outcome of the called method. This makes it easy to strongly type everything as well.

Nick

Error handling in X# vs VO

Posted: Fri Jul 26, 2019 1:51 pm
by ArneOrtlinghaus
Hi Wolfgang,
yes, I know that it works in VO and we have had recoverable errors with this solution many thousands of times. However I haven't found any similar solution in X#. The global error blocks as proposed by different colleagues like Dieter Crispien worked with batch executions, but did not work correctly with the GUI in my tests.

Arne

Error handling in X# vs VO

Posted: Fri Jul 26, 2019 2:25 pm
by wriedmann
Hi Nick,
That's not quite how I'd do it. My intention was that Cargo is the generic object to hold any data or return value from the method call, and Result is something to give info on the actual process (success, failure, no data found, exception thrown, etc), so can be an enum of pre-established values to describe different outcomes.
Yes, I understand that from your code. But I prefer it the way as I have written it. Most of the times a logic return is enough for me, and very often I return some sort of object. And if I need a result enumeration, I can simply use that as the result type.
It is more the cargo member that I currently don't need, but it could be useful, so I have implemented it.
And I have added a subclass like this one (to cover the most used return type without generic):

Code: Select all

class LogicResultEx inherit ResultEx<logic>

constructor( lResult as logic, oException as Exception, oCargo as object )

	super( lResult, oException, oCargo )

	return

constructor( oResult as logic, oException as Exception )

	self( oResult, oException, null )

	return

constructor( oResult as logic )

	self( oResult, null, null )

	return

constructor( oResult as logic, oCargo as object )

	self( oResult, null, oCargo )

	return
Wolfgang

Error handling in X# vs VO

Posted: Fri Jul 26, 2019 2:30 pm
by wriedmann
Hi Arne,
However I haven't found any similar solution in X#. The global error blocks as proposed by different colleagues like Dieter Crispien worked with batch executions, but did not work correctly with the GUI in my tests.
I do think that the development team needs to add something to the translation of begin/end sequence.
Maybe they can give us the possibility to set a global error handler, maybe using the ErrorBlock() function from VO, and put a call to that in the recover clause of the begin/end sequence construct.
I had asked for that when the development had started, but probably it was too early and it either got lost or it was forgotten.

This is one of the incompatibilities between VO and X I'm be most afraid of.

Wolfgang