|
You’ve already seen how all the exception types are derived from the System::Exception class. If you can’t find one that suits your needs in the standard exception hierarchy, you can easily derive your own class from Exception and use it in your code. The following exercise shows you how to derive a new exception class and how to use it in code.
Start Visual Studio .NET, and open a new Visual C++ Console Application (.NET) project named OwnExcept.
Add the following class definition immediately after the using namespace System; line:
// User-defined exception class __gc class MyException : public System::Exception { public: int errNo; MyException(String* msg, int num) : Exception(msg), errNo(num) {} };
This custom exception class is a managed class that inherits from System::Exception, and it extends Exception by adding a single field to hold an error number. The class constructor takes a message and a number, and passes the message string back to the base class.
Note | I’ve made the errNo field public. Although you’re normally advised to make all data members of classes private, you can make a case for having public data members in certain circumstances. Once you’ve created an Exception object and passed it back to the client, do you care what the client does with it? Exceptions are “fire and forget” objects, and you’re normally not concerned with the integrity of their state once they leave your code in a throw statement. |
Add the following function definition immediately after the class definition:
void func(int a) { try { if (a <= 0) throw new System::ArgumentException( S"Negative argument"); } catch(System::ArgumentException* pex) { Console::WriteLine(S"Caught ArgumentException" S"in func()"); throw new MyException(pex->Message, 1000); } }
The function checks its argument and throws a System::ArgumentException if it finds a negative value. This exception is caught locally, and a message is printed. Now I decide that I really want to handle the exception elsewhere, so I create a new MyException object and rethrow it, initializing it with the message from the original ArgumentException.
Test the exception handling by calling the function in the program’s _tmain routine.
int _tmain() { Console::WriteLine(S"Custom Exceptions"); try { func(0); } catch(MyException* pex) { Console::WriteLine(S"Caught MyException in main()"); Console::WriteLine(S"Message is : {0}", pex->Message); Console::WriteLine(S"Error number is : {0}", __box(pex->errNo)); } return 0; }
Calling the function with a 0 value triggers the exception, which is handled in the function itself, and the exception is then rethrown to be handled in the _tmain function. You can see from the following figure how the exception has been caught in both in places.
In the preceding code, notice that it’s necessary to box the error number before it can be used in a call to WriteLine because the formatted overloads to WriteLine need a list of Object* pointers, and boxing lets you use a built-in type as an object. See Chapter 25 for more information on boxing.
In the preceding example, you implemented the custom exception as a managed class (declared using the __gc keyword), but it’s also possible to use value types (declared with the __value keyword) as custom exceptions. The difference comes when you want to use value types as exceptions because you need to throw a pointer to a managed type.
Getting the pointer is not a problem with __gc classes because you always create them using new and you always get a pointer returned. Value types are created on the stack, and you operate on them directly, rather than through pointers. If you want to get a pointer to a value type, you have to box it. Boxing wraps, or boxes, the value type in an object and returns a pointer to it. Here’s an example of how you’d use a value type as an exception:
__value struct MyExceptionStruct { int errNo; }; void SomeFunction() { // I want to throw a struct... MyExceptionStruct mes = { 1000 }; throw __box(mes); }
The call to the __box function wraps the struct in an object box and returns a pointer to the object, which can then be thrown.
|