Share via


Exception Filter

C# does not support exception filter. However, VB and IL support it. To add exception filter to C#, we can build a function in VB or IL, then call it in C#.

The example below is one way we may implement the function to support exception filter. It asks the caller to provide four delegates, and call them at the right places.

Imports Microsoft.VisualBasic

Namespace Utility

    Public Delegate Sub TryDelegate()

    Public Delegate Function FilterDelegate(ByVal exception As System.Exception) As Boolean

    Public Delegate Sub CatchDelegate(ByVal exception As System.Exception)

    Public Delegate Sub FinallyDelegate()

    Public Class ExceptionFilter

        Inherits System.Object

        Public Shared Sub TryFilterCatch(ByVal tryDelegate As TryDelegate, ByVal filterDelegate As FilterDelegate, ByVal catchDelegate As CatchDelegate, ByVal finallyDelegate As FinallyDelegate)

            Try

                tryDelegate()

            Catch ex As System.Exception When catchDelegate <> Nothing And filterDelegate(ex)

                catchDelegate(ex)

            Finally

               If (finallyDelegate <> Nothing) Then

                    finallyDelegate()

                End If

            End Try

        End Sub

    End Class

End Namespace

 

In C# we can call Utility.TryFilterCatch as the following:

using System;

using Utility;

namespace ExceptionFilterUsage

{

    class Program

    {

        static void Main(string[] args)

        {

            TryDelegate tryDelegate = delegate()

            {

                Console.WriteLine("In try.");

                throw new ApplicationException();

            };

            FilterDelegate filterDelegate = delegate(Exception e)

            {

                Console.WriteLine("In exception filter.");

                if (e is ApplicationException)

                {

                    Console.WriteLine("ApplicationException is thrown, which should never happen.");

                    return false;

                }

                return true;

            };

            CatchDelegate catchDelegate = delegate(Exception e)

            {

                Console.WriteLine("In Catch: Exception: {0}", e);

            };

            FinallyDelegate finallyDelegate = delegate()

            {

                Console.WriteLine("In finally.");

            };

          ExceptionFilter.TryFilterCatch(tryDelegate, filterDelegate, catchDelegate, finallyDelegate);

        }

    }

}

Comments

  • Anonymous
    December 08, 2008
    Now this might be a silly question, but I wonder if there is a way by which you can specify exception handling a la AOP sytle and let the handlers get kicked off by convention rather than code. I know C# has made some rapid advances compared to language like Java, but there is still background noise in the code.

  • Anonymous
    December 18, 2008
    The comment has been removed

  • Anonymous
    December 19, 2008
    David, The fault handler feels not as useful with filter/finally handler. You can run the code in either the filter handler or the finally handler.

  • Anonymous
    December 19, 2008
    Thanks for trying out the IL code. I tend to agree that it's not as useful when combined with the try-filter-catch-finally I think a better usage for a fault handler is in isolation from the standard try-catch-finally. I'd prefer to see a language syntax so that you can use just a fault handler, as in try-fault, and then you can place it where it's needed. I think there is a need for a fault handler because I've seen the following pattern used extensively. try {  CodeThatCanThrow(); } catch( Exception ) {  if ( someCondition )    Cleanup();  throw; } A variation is when it's an empty catch (no exception type specified) so that it catches non-CLSCompliant exceptions. The pattern that emerges from this is that the catch clause does not even look at the exception and does not care what type of error that occurred, it must perform some cleanup regardless. It's not that this does not work, it's that because the language does not provide a try-fault, the author had to use some other mechanism. I have several issues with doing this: first, the intent is not clear. You have to parse the statements in the catch clause to understand that it's not really doing anything with the exception, not even logging it. Second, catches have side effects, such as running more deeply nested finally blocks and unwinding the stack, before the catch handler executes. Third, I have seen this implemented incorrectly, where a "throw ex" is used, that discards the original stack trace - it's too easy to get it wrong. Fourth, for myself, as a general rule of thumb, if it's worth the effort of catching it and it needs to be rethrown, then it ought to be wrapped inside another exception that adds meaningful content and context. I'd rather see: try {  CodeThatCanThrow(); } fault {  if ( someCondition )    Cleanup(); } To me the intent of this is much clearer. You can still nest this within a try-catch if other handling is needed. An example of this pattern is in WindowsBase.dll. If you use Reflector to look at method MS.Internal.IO.Zip.ZipArchive.OpenOnFile(String,FileMode,FileAccess,FileShare,Boolean), you will see this this code try {  stream = new FileStream(path, mode, access, share, 0x1000, streaming);  ValidateModeAccessShareStreaming(stream, mode, access, share, streaming);  archive = new ZipArchive(stream, mode, access, streaming, true); } catch {  if (stream != null)  {    stream.Close();  }  throw; } The usage here is that if the operation got far enough along to allocate a stream object, and the operation failed after that point, then close the stream and abort. It does not matter why it failed, and no corrective action could be taken. But if no errors occurred, then do nothing and leave the stream open. I've seen this pattern used extensively. I wrote an FxCop rule that discovers similar patterns and that's how I found this one. Regards, Dave

  • Anonymous
    December 20, 2008
    catch and re-throw is a bad pattern, as once the exception is caught, the original exception context is gone, and the exception is no longer debuggable. For the example, I will either do the clean up in finally, or in the filter handler. use the finally as an example: bool success = false; try {    DoSomeWork();    success = true; } finally {    if (!success)    {        Cleanup();    } }

  • Anonymous
    April 09, 2009
    Somebody posted a comment up my blog which contained something along the lines of&#160; “and VB .NET

  • Anonymous
    April 09, 2009
    Somebody posted a comment up my blog which contained something along the lines of&#160; “and VB .NET