Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why don't .NET exceptions work against an interface rather than a base class?

Tags:

The .Net framework try-catch implementation only allows you to catch types which inherit off the base class "System.Exception". Why could this not have been an interface such as "System.IException"?

Use case

We use a custom base class in each API that inherits off System.Exception. This is only thrown once an exception has been logged and therefore we can easily avoid re-logging by something like:

try
{
    // Do something.
}
catch (LoggedException)
{
    // Already logged so just rethrow.
    throw;
}
catch (Exception ex)
{
    // TODO: Log exception.
    throw new LoggedException("Failed doing something.", ex);
}

This is great until you want a custom exception that inherits off another system exception type such as System.FormatException

The only way to now handle both of these is to have two custom base types and duplicate every catch statement.

Refactoring

If the .net framework just simply looked for something such as System.IException then you could simply have a custom exception interface such as CompanyName.ILoggedException, inheriting System.IException which all your custom exception types implement. Therefore your new catch code would look something like:

try
{
    // Do something.
}
catch (ILoggedException)
{
    // Already logged so just rethrow.
    throw;
}
catch (IException ex)
{
    // TODO: Log exception.
    throw new CustomException("Failed doing something.", ex);
}

Is there a practical reason the framework is implemented like this? Or would this be something to request in a future version of the .Net framework?

like image 941
Daniel Bradley Avatar asked Sep 15 '11 12:09

Daniel Bradley


People also ask

Can an interface throw an exception?

Yes, the abstract methods of an interface can throw an exception.

What is the difference between base class and interface?

Differences between a Class and an Interface:A class can be instantiated i.e, objects of a class can be created. An Interface cannot be instantiated i.e, objects cannot be created. Classes does not support multiple inheritance. Interface supports multiple inheritance.

What are the limitations of interface in C#?

It contains only properties, indexers, methods, delegates and events signature. It cannot contains constants members, constructors, instance variables, destructors, static members or nested interfaces. Members of an interface cannot have any access modifiers even public.

What happens if you do not handle exceptions C#?

Yes. Something "exceptional" has happened, and your program does not know how to handle it, so it must stop execution at that point and "crash". There will be code that is executed after the crash, such as finally blocks, but basically the party is over for your code.


2 Answers

As you probably know that in base class case we only have single inheritance but in case of interface a class can implement many interfaces so if you have something like:

class MyException : IExceptionB, IExceptionA
{
}

try
{
 throw new MyException();
}
catch(IExceptionB b){}
catch(IExceptionA b){}

Now this creates ambiguity regarding how to decide which catch handler to call as the exception implements both interface i.e in terms of hierarchy both are at same level unlike the base class where there won't be two class at same level.

The code is hypothetical showing the problem in case Interface based catch was allowed

like image 167
Ankur Avatar answered Sep 28 '22 11:09

Ankur


One possibly neater workaround that hasn't been mentioned yet is to use extension methods. By harnessing the Exception.Data field, you could neatly discover from a single catch block whether the current exception has been logged yet, and take action as required. This would allow you to construct numerous different company specific exceptions (which are implicitly already logged).

Extension methods required:

private const string ExceptionLoggedKey = "IsExceptionLogged";

public static bool IsLogged(this Exception exception)
{
    if (exception.Data.Contains(ExceptionLoggedKey))
    {
        return (bool)exception.Data[ExceptionLoggedKey];
    }
    return false;
}

public static void SetLogged(this Exception exception)
{
    exception.Data.Add(ExceptionLoggedKey, true);
}

Company exceptions take the following format, setting the IsLogged flag in the constructor:

public class CompanysLoggedException : InvalidOperationException  //Could be any Exception
{
    public CompanysLoggedException()
    {
        this.SetLogged();
    }
}

try/catch usage:

try
{
    throw new ArgumentException("There aren't any arguments.");
}
catch (Exception ex)
{
    if (ex.IsLogged())
        //Nothing additional to do - simply throw the exception
        throw;
    else
        //TODO Log the exception
        throw new CompanysLoggedException();
}

I'd agree that this definitely isn't as neat as the ability to match an exception based on an implemented interface, but I think the pattern is quite concise and readable. Having to remember to add the call to SetLogged() to each new company exception defined is a bit of a flaw though.

like image 40
Alex Avatar answered Sep 28 '22 10:09

Alex