Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are Exceptions and return statements the only possible early exits in C#?

Tags:

c#

.net

For example, I'm curious if in the following code, I can be assured that either Foo() or Bar() will be executed.

try {
    ... // Assume there is no return statement here
    Foo();
} catch (Exception e) {
    Bar();
}

I am quite familiar with finally blocks, and do not need an explanation of that feature.

I ask because the code above would not work in Java due to the existence of Throwable, which I discovered the hard way.

like image 783
pamphlet Avatar asked Mar 06 '13 22:03

pamphlet


People also ask

Is there try and except in C?

The try-except statement is a Microsoft extension to the C language that enables applications to gain control of a program when events that normally terminate execution occur. Such events are called exceptions, and the mechanism that deals with exceptions is called structured exception handling.

Does throwing an exception stop execution C++?

throw usually causes the function to terminate immediately, so you even if you do put any code after it (inside the same block), it won't execute. This goes for both C++ and C#.

Does c language have try catch?

Yes, it is limited to one try-catch in the same function.

Under what circumstances should you implement exception handling?

Exceptions should be used for situation where a certain method or function could not execute normally. For example, when it encounters broken input or when a resource (e.g. a file) is unavailable. Use exceptions to signal the caller that you faced an error which you are unwilling or unable to handle.


2 Answers

There are also the "burn-down-the-house" ways of stopping an application:

Environment.Exit(int code);
Environment.FailFast(string message);
Thread.CurrentThread.Abort();
AppDomain.Unload(AppDomain.CurrentDomain);

For fun, here's another :)

[DllImport("kernel32.dll",SetLastError = true)]
static extern bool WriteProcessMemory(
      IntPtr hProcess, 
      IntPtr lpBaseAddress, 
      byte [] lpBuffer, 
      uint nSize, 
      out UIntPtr lpNumberOfBytesWritten);

var myProcess = Process.GetCurrentProcess();
var hProcess = myProcess.Handle;
var rnd = new Random();
while(true)
{
    var writeTo = new IntPtr((int)rnd.Next(0, int.MaxValue));
    var toWrite = new byte[1024];
    UIntPtr written;
    WriteProcessMemory(
        hProcess, 
        writeTo, 
        toWrite, 
        (uint)toWrite.Length, 
        out written);
}

Out of curiosity and prodding, let's take them for a test drive!

Our test rig:

    static void Main(string[] args)
    {
        Trace.Listeners.Add(new ConsoleTraceListener());
        AppDomain.CurrentDomain.UnhandledException += OnNoes;
        try
        {
            // INSERT BURN STATEMENT
            Foo();
        }
        catch (Exception e)
        {
            Bar();
        }
        finally
        {
            Baz();
        }
    }

    static void Foo()
    {
        Trace.WriteLine("I AM FOO!");
    }
    static void Bar()
    {
        Trace.WriteLine("I AM BAR!");
    }
    static void Baz()
    {
        Trace.WriteLine("I AM BAZ!");
    }
    static void OnNoes(object sender, UnhandledExceptionEventArgs e)
    {
        Trace.WriteLine("OhNoes!");
    }

The results!

The Burn Statement:

Thread.CurrentThread.Abort();

Output:

I AM BAR!
I AM BAZ!

The Burn Statement:

AppDomain.Unload(AppDomain.CurrentDomain);

Output:

I AM BAR!
I AM BAZ!

The Burn Statement:

Environment.Exit(-1);

Output:

Nothing! No trace output at all!

The Burn Statement:

Environment.FailFast("Burn!!!");

Output:

Application crash! A FatalExecutionEngineError was thrown, 
which was not caught by any block/handler. No trace output.

So there you go! What? I missed one?

The Burn Statement:

Splode();

Where "Splode" is:

    static void Splode()
    {
        var myProcess = Process.GetCurrentProcess();
        var hProcess = myProcess.Handle;
        var rnd = new Random();
        while (true)
        {
            var writeTo = new IntPtr((int)rnd.Next(0, int.MaxValue));
            var toWrite = new byte[1024];
            UIntPtr written;
            WriteProcessMemory(
                hProcess,
                writeTo,
                toWrite,
                (uint)toWrite.Length,
                out written);
        }            
    }

Output:

Application crash! A FatalExecutionEngineError was thrown, 
which was not caught by any block/handler. No trace output.
Crashed Visual Studio while running attached!
like image 95
JerKimball Avatar answered Sep 18 '22 09:09

JerKimball


Yes... the most obvious ones are await, yield break/yield return, goto, if(false), etc., as mentioned in comments. But all of these statements/expressions have to be written by yourself, in the method containing your try statement, so you don't really have to worry about them.

However, even apart from these, there is a way to exit without throwing an exception or returning (or running either of those two methods). That is... throwing something that is not an exception.

The C# language specification states that the only things you can throw are either instances of the class Exception, or the null literal (in which case a NullReferenceException is thrown.) Found in §8.9.5:

The [throw] expression must denote a value of the class type System.Exception, of a class type that derives from System.Exception or of a type parameter type that has System.Exception (or a subclass thereof) as its effective base class. If evaluation of the expression produces null, a System.NullReferenceException is thrown instead.

However, this restriction only restricts C# code. C# code is compiled into Intermediate Language, which is not restricted in this way. Also found in the C# language specification, §8.10:

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.

In order to catch these exceptions, you need to use a general catch clause, like so:

try
{
    //...
    Foo();
}
catch
{
    Bar();
}

NOTE: This method only applies if you compile to a .NET framework before version 2.0. Starting in that version, the CLR wraps the thrown object in a RuntimeWrappedException. Thanks, svick!

In addition, a few other people mentioned killing the process, or throwing a StackOverflowException, both of which will work well to accomplish this task. There may be other ways apart from these, but I don't think so (apart from suddenly unplugging the computer, heaven forbid.) Hope this helps! --Brandon

like image 28
leviathanbadger Avatar answered Sep 20 '22 09:09

leviathanbadger