简体   繁体   中英

Combining a single custom exception with an enum to easily create several sub-exceptions

I think I am need of writing one or more custom exceptions in C# 5.0. I am probably not, but it seems that the .NET provided exceptions belong to a too-systemic and too-generic domain of exceptions - very much specific to situation of "having a running program on the CLR supporting file I/O". This IS exactly what I have, at least, of course. Still, when trying to develop an application in C# or any other OO-language, a subset (or all) of the new types that you create, should in most circumstances originate from an ontology of concepts, which is nowhere close to such a systemic domain as the CLR or the .NET Framework. That's how I see the "authoring" part of OO development - but that's an entire different question really.

So, on the "problem" of creating custom exceptions, I wanted to hear if the following solution have any drawbacks.

Let's say I create this enum and custom exception:

public enum MyCustomExceptionKind
{
    MyCustomInitializationException,
    MyCustomStartException,
    MyCustomStopException,
    MyCustomFatalException
}

public class MyCustomException: Exception
{
    private MyCustomExceptionKind? m_exceptionKind = null;

    public MyCustomExceptionKind ExceptionKind
    {
        // return the value of the nullable type:
        get { return m_exceptionKind.Value; }
    }

    // let's support only the most-exclicit instance constructor for now:
    public EmployeeListNotFoundException(
        MyCustomExceptionKind myCustomExceptionkind,
        string message,
        Exception inner): base(message, inner)
    {
        m_exceptionKind = myCustomExceptionkind;
    }
}

The idea here, is the use of a built-in enumeration type. Instead of creating many new exception, I have chosen to use an enum for encoding the sub-type on an exception. Note that I also take use of a nullable enum-type by using the question-mark.

Handling such an exception would then behave something like this:

public class SomeType
{
    public void StartUniverse(int num)
    {
        if (num != 42)
        {
            throw new MyCustomException(
                MyCustomExceptionKind.AttemptToStart,
                "It was not possible start the damn thing ...",
                null);
        }
    }

    public bool TryStart(int num)
    {
        tryOK = true;
        try
        {
            StartUniverse(num);
        }
        catch (MyCustomException ex)
        {
            // set the overall state-bool to false:
            tryOK = false;

            // use a switch case for handling sub-types of this exception:
            switch (MyCustomException.ExceptionKind)
            {
                    case MyCustomExceptionKind.MyCustomStartException:
                    Trace.TraceError("oh dear - could not start");
                    break;
            }
        }
        return tryOK;
    }

    static public Main(string[] args)
    {
        var myObj = new SomeType();
        myObj.TryStart(199); // <-- 199 != 42
    }
}

Anything to be aware of in such an implementation? Pros and cons? From where I stand, I see only good things. That's usually an illusion though.

Your approch is okay, as long as all ExceptionKinds are on the same level: all exception can be handled on the same layer and will result in the same handling (like showing a message box or stopping a task). But for instance, when MyCustomFatalException should result in a program termination, then your approach is not the best, cause you would need to catch it twice

void DoStuff()
{
    try
    {
       ...
    }
    catch(MyCustomException inner)
    {
       if (inner.ExceptionKind == MyCustomExceptionKind.MyCustomFatalException)
          throw;

       // handle inner, show message box
    }
}

void DoStuffTwice()
{
   DoStuff();
   DoStuff();
}    


void Main()
{
   try
   {
       DoStuffTwice();
   }
   catch(MyCustomException inner)
   {
       if (inner.ExceptionKind == MyCustomExceptionKind.MyCustomFatalException)
          return;

       // now what??
   }
}

this does not look like a big deal, but on heavy load that may introduce a problem, because catching exceptions costs time, and catching an exception without the need of handling it, and just of the purpose of rethrowing it, can slow down your application.

BTW: I don't understand why you choose to make the ExceptionKind property return nullable value, since it can never be null.

Keep in mind that writing several different exception classes is effort you pay once ; handling the different exception scenarios may come up many times. So try to focus on making this easier for the client that's going to handle these exceptions.

Compare the boilerplate of catching the general exception, peeking inside to check if it's actually relevant, and rethrowing otherwise, versus just catching a specialized exception type that you know you care about. The facilities to catch specific exception types and to derive new exception types work together to make it easier to just use specialized exception types than to use general exception types otherwise marked with special values. Don't work against the grain of the language.

If you anticipate your client wanting to catch all of your custom exceptions at the same site, you can make your custom exceptions inherit from a common MyCustomException class. This is essentially a superset of your enum solution; if someone catches the general MyCustomException class and then for whatever reason needs to know the specific type, they can just say (ex is MyCustomInitializationException). But you've still given your users the option to catch MyCustomInitializationException in the first place if that's what they care about.

If the exceptions are really the same thing with a different code (Like HttpStatus), then it makes sense. Otherwise I would create different Exception classes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM