exception和return语句是c#中唯一可能的早期退出吗?

本文关键字:退出 唯一 return 语句 exception | 更新日期: 2023-09-27 18:02:43

例如,我很好奇在下面的代码中,我是否可以确信Foo()Bar()将被执行。

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

我非常熟悉finally块,不需要解释这个特性。

我问

是因为上面的代码将在Java中工作,由于Throwable的存在,这是我发现的艰难的方式。

exception和return语句是c#中唯一可能的早期退出吗?

还有"burn-down-the-house"方法来停止应用程序:

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

为了好玩,这里还有一个:)

[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);
}

出于好奇和刺激,让我们来试驾一下吧!

我们的测试装置:

    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!");
    }

结果!

Burn语句:

Thread.CurrentThread.Abort();
输出:

I AM BAR!
I AM BAZ!

Burn语句:

AppDomain.Unload(AppDomain.CurrentDomain);
输出:

I AM BAR!
I AM BAZ!

Burn语句:

Environment.Exit(-1);
输出:

Nothing! No trace output at all!

Burn语句:

Environment.FailFast("Burn!!!");
输出:

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

就是这样!怎么啦?我漏了一个?

Burn语句:

Splode();

其中"Splode"为:

    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);
        }            
    }
输出:

Application crash! A FatalExecutionEngineError was thrown, 
which was not caught by any block/handler. No trace output.
Crashed Visual Studio while running attached!

Yes…最明显的是await, yield break/yield return, goto, if(false)等,如评论中提到的。但是所有这些语句/表达式都必须由您自己编写,在包含try语句的方法中,因此您不必真正担心它们。

然而,即使没有这些,也是一种不抛出异常或返回(或运行这两个方法中的任何一个)而退出的方法。这是……抛出非异常的内容

c#语言规范规定,您只能抛出类Exception的实例或null字面量(在这种情况下抛出NullReferenceException)。在§8.9.5中发现:

[throw]表达式必须表示类类型为System的值。派生自System的类类型的异常。类型参数类型具有System。异常(或其子类)作为其有效基类。如果表达式的求值产生null,则System。而抛出NullReferenceException。

然而,这个限制只限制c#代码。c#代码被编译成中间语言,不受这种方式的限制。也可以在c#语言规范§8.10中找到:

一些编程语言可能支持不能作为System派生对象表示的异常。异常,尽管这样的异常永远不能由c#代码生成。

为了捕获这些异常,需要使用一般的catch子句,如下所示:

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

注意:此方法仅适用于编译到2.0版本之前的。net框架。从那个版本开始,CLR将抛出的对象包装在RuntimeWrappedException中。谢谢,svick !

此外,其他一些人提到杀死进程,或者抛出StackOverflowException,这两种方法都可以很好地完成这个任务。除了这些,可能还有其他的方法,但我不这么认为(除了突然拔掉电脑插头,但愿不会。)希望这对你有帮助!——Brandon

假设我们有以下代码:

try
{
    /*Breaking statement goes here*/
    Foo();
}
catch (Exception ex)
{
    Bar();
}
finally
{
    Baz();
}

我将中断分为3个常见原因:

  1. 代码流语句:

    1.1。return: Foo(-);酒吧(-);巴兹(+),

    1.2。goto: Foo(-);酒吧(-);巴兹(+),

    1.3。if(false): Foo(-);酒吧(-);巴兹(+),

    1.4。while(true){}: Foo(-);酒吧(-);巴兹(-),

    1.5。yield return,如果方法返回IEnumerable并且yield return出现在try块之前:Foo(-);酒吧(-);巴兹(-),

    1.6。yield break,如果方法返回IEnumerable: Foo(-);酒吧(-);巴兹(-),

    1.7。break,以防代码被循环包装:Foo(-);酒吧(-);巴兹(+),

    1.8。continue,以防代码被循环包装:Foo(-);酒吧(-);巴兹(+),

  2. 过程/域/线程终止。

    2.1。Process.GetCurrentProcess().Kill(): Foo(-);酒吧(-);巴兹(-),

    2.2。Environment.Exit(0): Foo(-);酒吧(-);巴兹(-),

    2.3。Environment.FailFast(""): Foo(-);酒吧(-);巴兹(-),

    2.4。AppDomain.Unload(AppDomain.CurrentDomain): Foo(-);酒吧(+);巴兹(+),

    2.5。Thread.CurrentThread.Abort(): Foo(-);酒吧(+);巴兹(+),

  3. 未处理的异常。

    3.1。.NET 2.0之前的非托管代码中的异常:Foo(-);酒吧(-);巴兹(+),

    3.2。.NET 2.0以后非托管代码中的异常:Foo(-);酒吧(+);巴兹(+),

    3.3。.NET 4.0之后的进程状态异常(<legacyCorruptedStateExceptionsPolicy>HandleProcessCorruptedStateExceptionsAttribute都没有指定):Foo(-);酒吧(-);巴兹(+),

    3.4。指定.NET 4.0或<legacyCorruptedStateExceptionsPolicy>HandleProcessCorruptedStateExceptionsAttribute之前的进程状态异常:Foo(-);酒吧(+);巴兹(+),

    3.5。自。net 2.0和<legacyUnhandledExceptionPolicy>以来,另一个线程中的异常未启用:Foo(-);酒吧(-);巴兹(-),

    3.6。在。net 2.0或<legacyUnhandledExceptionPolicy>之前的另一个线程中启用异常:Foo(+);酒吧(-);巴兹(+),

还有其他几个选项(在The Daily WTF中提到过,当时一家公司认为事务捕获了所有内容),即操作系统关闭,任务被杀死,机器失去电源(不要笑,这种情况确实发生过)。其中一些你可以使用Form_Closing事件来处理,例如(OS关机),但有些你不能。操作系统和其他应用程序(例如,当它崩溃时的VS)通过每隔X分钟保留一个临时状态来绕过这些情况。如果该状态存在,则应用程序被意外终止。如果这种状态不存在,则应用程序干净地退出,数据被正确保存。