在不设置参数的情况下抛出异常时的行为是什么



在 C# 中定义的行为是什么,当在设置 out 参数的值之前引发异常,然后您尝试访问该参数时?

public void DoSomething(int id, out control)
{
    try
    {
        int results = Call(Id);
        control = new CallControl(results);
    }
    catch(Exception e)
    {
      throw new Exception("Call failed", e);
    }
}
//somewhere else
DoSomething(out Control control)
{
    try
    {
       DoSomething(1, out control);
    }
    catch()
    {
    // handle exception
    }
}
// later
Control control;
DoSomething(out control)
control.Show();

编译器通常会抱怨在设置 out 参数之前退出方法。这似乎比保护我自己更聪明。

在设置 out 参数的值之前引发异常,然后尝试访问该参数时,C# 中定义的行为是什么?

你不能这样做。该变量仍然不会被明确分配,除非它在方法调用之前被明确分配。

如果变量在方法调用之前被明确赋值,那么它仍将被明确赋值 - 但除非在抛出异常之前赋值的方法,否则变量的值将保持不变:

class Test
{
    static void JustThrow(out int x)
    {
        throw new Exception();
    }
    static void Main()
    {
        int y = 10;
        try
        {
            JustThrow(out y);
        }
        catch
        {
            // Ignore
        }
        Console.WriteLine(y); // Still prints 10
    }
}

关系。如果抛出DoSomething,则不执行control.Show()

无论如何,您都无法胜过编译器,因为它知道控制流何时可以绕过初始化。

考虑以下代码,它类似于您提出的代码:

using System;
namespace Demo
{
    internal class Program
    {
        private void run()
        {
            int a;
            try
            {
                DoSomething(out a);
            }
            catch {} // Evil empty catch.
            Console.WriteLine(a); // Compile error.
        }
        public void DoSomething(out int x)
        {
            test();
            x = 0;
        }
        private static void test()
        {
            throw new InvalidOperationException("X");
        }
        private static void Main()
        {
            new Program().run();
        }
    }
}

它给出了编译错误,因为您在DoSomething()周围放置了一个 try/catch,因此编译器假定a可能尚未初始化。

(请注意,如果未显式初始化所有堆栈变量,则会将其初始化为其默认值,但这不会影响编译器的控制流分析。编译器关心的是明确的赋值,而不是 CLR 执行的默认初始化。无论如何,C# 标准不保证此初始化。

如果将空捕获更改为抛出的捕获,则编译错误将消失:

catch { throw new Exception("Test"); } // Compile error goes away.

因为现在如果发生异常,您将无法访问使用 a 的代码。

最新更新