Short Scoped Try

Topics: C# Language Design
Jul 11, 2014 at 1:48 PM
How it looks like in Use:
Action _click;

...

try _click?.Invoke( );
Background:

First of all we need to declare scoped Exception handler on assembly level, namespace level, class level inheritance level:
[assembly: ExceptionHandler( AnyHandler )]
[assembly: ExceptionHandler( IOHandler )]

[ExceptionHandler( IOHandlerNS )]
namespace NS1.NS2 {...}

private static    void   AnyHandler( Exception ex ){...}
private static    void   IOHandler( IOException ex ){...}
private static    void   IOHandlerNS( IOException ex ){...}
then the code like:
try _click?.Invoke( );
...
namespace NS1.NS2 
{
   ...
   try _click?.Invoke( );
   ...
}
Will be converted by compiler to:
try
{
   _click?.Invoke( );
}
catch( IOException ex )
{
   IOHandler( ex );
}
catch( Exception ex )
{
   AnyHandler( ex );
}

...
namespace NS1.NS2 
{
   ...
   try
   {
      _click?.Invoke( );
   }
   catch( IOException ex )
   {
      IOHandlerNS( ex );
   }
   catch( Exception ex )
   {
      AnyHandler( ex );
   }
   ...
}
Jul 12, 2014 at 11:02 PM
Could you explain why do you think this is necessary (and why would it be a good practice)?

Also, namespaces can't have attributes (probably because on the IL level, namespaces don't really exist, just classes with dots in their names).

And attribute constructor parameters can't be delegates.
Jul 14, 2014 at 9:18 AM
If you really need to wrap individual operations in equivalent error handling, you could implement this in a reusable way with a helper method:
static class ExceptionHandling {
    public static void Try(Action act) {
        try {
            act();
        } /* the various exception handlers */
    }
}

ExceptionHandling.Try(() => _click?.Invoke());
This can be customized to taste with different sorts of exception handling due to optional parameters or different methods. And you could always "go to definition" and find out in plain code which exceptions are handled and how.

Like svick, I'm wondering what the advantages with pulling this exception handling up into some sort of global scope are.
Jul 24, 2014 at 12:23 PM
Could you explain why do you think this is necessary (and why would it be a good practice)?
Often we write fault tolerant systems witch must work even if some module fail.
Most often we see it in Observers (Publisher-Subscriber) i.e. C# events.

If I some object with many events I don't want to crash if some subscriber crashes! This leads into code like this:
try
{
   if( _click != null )
      _click( );
}
catch( Exception ex )
{
   Log( ex );
}
and more I want to notify all subscribers regardless of few of them crash. I don't want have situation where subscriber X will not be notified because of Subscriber Y crash. Subscriber X really don't care is the Subscriber Y exists at all.
If my Application have plugin interface It don't care how many bugs and crash points some new plugin has and we have the same situation.

So the code:
private List<Action> _click;

event Action Click
{
   add{ _click.Add( value ); }
   remove{ _click.Remove( value ); }
}
...
private void RaiseClick( )
{
   if( _click != null )
      foreach( var click in _click )
         try
         {
            click( );
         }
         catch( Exception ex )
         {
            Log( ex );
         }
}
We have this pattern in most RaiseEventX methods in entire solution. If I want to add something or change in try catch I need to go all the places and change code.

With scoped try handling all of this code will be centralized and simple.
Also, namespaces can't have attributes (probably because on the IL level, namespaces don't really exist, just classes with dots in their names).
Those namespace attributes can be converted by compiler into something like this: [assembly:ExceptionHandler( "NS1.NS2", IOHandlerNS )]
And attribute constructor parameters can't be delegates.
this can be solved by converting MethodHandle to full method address string like: MyAssemblt.SomNamespace.SomeClass.HandlerStaticMethod by compiler
ExceptionHandling.Try(() => _click ?.Invoke());
Unfortunately this will allocate garbage and can not be used efficiently for highly loaded, fault tolerant server solutions or in computation heavy applications
Jul 24, 2014 at 6:52 PM
Unfortunately this will allocate garbage and can not be used efficiently for highly loaded, fault tolerant server solutions or in computation heavy applications
One may avoid allocating garbage in such methods by defining, e.g.,
delegate ActionR<T1>(ref T1 p1);
delegate ActionRR<T1, T2>(ref T1 p1, ref T2 p2);

// And then within ExceptionHandling...
void Try<T1>(ActionR<T1> proc, ref T1 p1);
void Try<T1>(ActionRR<T1,T2> proc, ref T1 p1, ref T2 p2);
A call to ExceptionHandling.Try( (ref x)=>x?.Invoke() , ref _click) would then no longer require a closure, and would thus not generate any garbage.