When writing robust software, there is often a need to perform a series of retrievable operations. In order to make the system robust, each operation in the series can be coded so it is independent of the status of the previous operation. As a concrete example, consider a file processing pipeline.
The first step in the pipeline may be to poll a file server and download any newly detected files. The next step would be to parse those files and convert them into a more usable format. A third step would be to import the converted files into a database for further processing.
If the first step fails after downloading some of the files, the application as a whole shouldn’t abort. Rather, it should go onto the next step and start parsing the files that were successfully downloaded. This is acceptable because the missed files can simply be picked up during the next cycle.
To implement this pattern in C# one usually has to use a series of try-catch blocks.
try
{
DownloadFiles();
}
catch (Exception ex)
{
//log errors
}
try
{
ParseAndConvertFiles();
}
catch (Exception ex)
{
//log errors
}
try
{
ImportFiles();
}
catch (Exception ex)
{
//log errors
}
With the Deferred Error Handling proposal, much of this boilerplate code can be removed.
#exception mode deferred
DownloadFiles();
ParseAndConvertFiles();
ImportFiles();
if (Exception.LastException != null)
{
//log errors
Exception.ClearLastException();
}
#exception mode structured
In order to use deferred error handling, a new compiler directive called “exception mode” is used. This switches the current function between structured exception handling and the new deferred mode.
When using the deferred mode, the Exception.LastException property can be used to determine if an error has occurred. This stores only the most recent error, so if multiple errors occurred, all but the last will be lost.
This has caused some concern, as it would mean one should check LastException after each line, which would be contrary to the goal of reducing the amount of code needed.
To address this, an amendment to the proposal is to replace LastException with a stack is under consideration. This would allow the developer to see all of the exceptions that were thrown in reverse chronological order.
Another option being considered is the ability to specify a jump target.
#exception mode deferred error_handler
[...]
return;
error_handler:
//log error
#exception resume next
If the “next” modifier is omitted, the application will retry the failed statement rather than continuing onto the next line.
The use of both structured and deferred error handling in the same function can be problematic from a compiler standpoint. Deferred mode fundamentally changes the way the code is compiled, much like how C# implements closures and async/await without CLR support. In order to simplify the changes needed for the compiler, another syntax is being considered. This would use the same type of syntax we see with unsafe and checked blocks.
deferred
{
DownloadFiles();
ParseAndConvertFiles();
ImportFiles();
}
Due to the magnitude of the change, this proposal is unlikely to be adopted before C# 9.