Why must a type thrown or caught derive from Syste

2019-04-04 00:40发布

问题:

So just out of curiosity I wanted to see what was special about the exception class that allowed it to be used with the keyword Throw while a standard class is not.

All I found is that the Exception class implemented the following

public class Exception : System.Object, System.Runtime.Serialization.ISerializable, System.Runtime.InteropServices._Exception
{
}

So I tried implementing those same interfaces and attempting to throw my own custom exception that did not derive from System.Exception to no avail. I was simply advised that

The type caught or thrown must be derived from System.Exception

So is there any specific reason for this? I assume there is as few choices in managed languages seem to be arbitrary.

回答1:

I think your premise is mistaken. It is possible that an object is thrown that is not derived from System.Exception. You just can't throw it in C# or examine the object in a catch clause. From section 8.10 of the C# spec (v4.0):

Some programming languages may support exceptions that are not representable as an object derived from System.Exception, although such exceptions could never be generated by C# code. A general catch clause may be used to catch such exceptions. Thus, a general catch clause is semantically different from one that specifies the type System.Exception, in that the former may also catch exceptions from other languages.

An example of a general catch:

try
{
}
catch (Exception) { } // 'specific' catch
catch { } // 'general' catch

In particular, this is important when calling unmanaged code.

Some types always seem to get special treatment in every language. Mostly because they are so fundamental to the system. System.Exception, System.ValueType, System.Delegate are all special types in C# that are tightly bound to language keywords and the CLR, so it is not surprising that you can't just implement classes that take over their roles.



回答2:

Design Guidelines for Exceptions

Exceptions are the standard mechanism for reporting errors. Applications and libraries should not use return codes to communicate errors. The use of exceptions adds to a consistent framework design and allows error reporting from members, such as constructors, that cannot have a return type

throw (C# Reference)

The thrown exception is an object whose class is derived from System.Exception, as shown in the following example.

class MyException : System.Exception {}
// ...
throw new MyException();

Exceptions Overview

In the .NET Framework, an exception is an object that inherits from the Exception Class

So, your exception must derive from System.Exception, but it's up to you, how you organize it within.



回答3:

It is an arbitrary choice of the designers of the CLS. Presumably they made this choice for reasons of consistency. C# follows the CLS; the requirement is enforced by the compiler for this reason, not for any technical reason related to the implementation of the Exception type.

The CLI can actually throw any object. See http://jilc.sourceforge.net/ecma_p3_cil.shtml#_Toc524462405.



回答4:

The language uses System.Exception as a base for all exceptions. This essentially means that any throwable or catchable exception shouldn't error out if you do (Exception)myExc. This is probably because the definition of the System.Exception class is used so that all exceptions adhere to the same interface. Because of the consistent interface, exceptions arrive with a stack trace and a meaningful message (for example), which is invaluable for logging.



回答5:

One reason why every exception needs to have a universal base class is so you can catch every type of exception in a single catch block.

If I have this:

try
{
    ...
}
catch(Exception ex)
{
    // Handle somehow
}

That will catch ALL exceptions, and will allow me to display what it is (by using ex.Message).

If you could throw anything, then how would you have a catch that would catch everything and still give you access to the object thrown?

You could have this, which will catch absolutely everything:

try
{
    ...
}
catch
{
    // Handle somehow
}

But you have 'lost' the thing that was thrown.