输出参数和异常

发布于 2024-12-27 14:49:38 字数 477 浏览 2 评论 0原文

假设我有以下代码:

    static void Fjuk(out string str)
    {
        str = "fjuk!";
        throw new Exception();
    }

    static void Main(string[] args)
    {
        string s = null;
        try
        {
            Fjuk(out s);
        }
        catch (Exception)
        {
            Console.WriteLine(s ?? "");
        }
    }

当我测试它时,s 已初始化为“fjuk!”当它在 catch 块中使用时。
这是由规范保证的还是依赖于实现? (我搜索了 C# 3 规范,但自己找不到)

Say I have the following code:

    static void Fjuk(out string str)
    {
        str = "fjuk!";
        throw new Exception();
    }

    static void Main(string[] args)
    {
        string s = null;
        try
        {
            Fjuk(out s);
        }
        catch (Exception)
        {
            Console.WriteLine(s ?? "");
        }
    }

When I test it, s has been initialized to "fjuk!" when it's used in the catch block.
Is this guaranteed by specification or is it implementation dependent? (I have searched the C# 3 spec but couldn't find out myself)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

霞映澄塘 2025-01-03 14:49:38

几乎,这就是 out 含义的一个方面;首先,请注意 out 并不真正存在 - 我们只需要考虑 refout 只是 ref > 在编译器上进行一些“明确分配”调整)。 ref 表示“传递此地址” - 如果我们通过地址更改值,则显示立即 - 毕竟,它正在更新内存Main 的堆栈上。它无法抽象这一点(以延迟写入),因为该值可能是一些超大的结构,例如,专门使用 ref 来避免将其复制到堆栈上(一种使用的方法)广泛应用于 XNA 等)。

Pretty much, that is an aspect of what out means; firstly, note that out doesn't really exist - we only really need to consider ref (out is just ref with some "definite assignment" tweaks at the compiler). ref means "pass the address of this" - if we change the value via the address, then that shows immediately - it is, after all, updating the memory on the stack of Main. It can't abstract this (to delay the write) because the value could be, for example, some oversized struct that is using ref specifically for the purpose of avoiding copying it on the stack (an approach used extensively in XNA etc).

左耳近心 2025-01-03 14:49:38

这是“有保证的”,因为 out 参数会使用参数的内存地址更改值。

out 关键字导致参数通过引用传递。这与 ref 关键字类似,只不过 ref 要求变量在传递之前进行初始化。

来自 MSDN

It's "guaranteed" because out parameter change the value with the memory address of the parameter.

The out keyword causes arguments to be passed by reference. This is similar to the ref keyword, except that ref requires that the variable be initialized before being passed.

from MSDN

扶醉桌前 2025-01-03 14:49:38

如果该方法引发异常,则不保证设置输出参数。如果该方法无异常退出,则保证设置输出参数。

在您的情况下,该方法将始终设置输出参数,但编译器不会以这种方式分析该方法的代码。如果该方法因异常退出,则输出参数仍然不被视为已明确设置。

异常处理程序中的代码不依赖于方法调用设置的变量,因为您在创建变量时设置该变量。如果在创建变量时未设置该变量,则异常处理程序将无法使用它,因为不能保证已设置该变量:

string s;
try {
  Fjuk(out s);
  Console.WriteLine(s); // here the variable is guaranteed to be set
} catch (Exception) {
  Console.WriteLine(s); // here it's not, so this won't compile
}

If the method throws an exception, the output parameter is not guaranteed to be set. If the method exits without an exception, the output parameter is guaranteed to be set.

In your case, the method will always set the output parameter, but the compiler doesn't analyse the code of the method in that way. If the method exits with an exception, the output parameter is still not considered to be definitely set.

Your code in the exception handler doesn't rely on the variable being set by the method call, as you are setting the variable when it's created. If you don't set the variable when it's created, the exception handler can't use it, because it's not guaranteed to be set:

string s;
try {
  Fjuk(out s);
  Console.WriteLine(s); // here the variable is guaranteed to be set
} catch (Exception) {
  Console.WriteLine(s); // here it's not, so this won't compile
}
七度光 2025-01-03 14:49:38

这是从 Fjuk 角度保证的,而不是从 Main 角度保证的。

Fjuk中,设置参数后会抛出异常。虽然编译器、抖动和 CPU 可以进行重新排序,但不会发生导致单个线程观察到的顺序发生变化的重新排序。由于单个线程可以“注意到”在抛出异常之前是否未设置该参数,因此保证会设置该参数。

但在 Main 中,我们不知道 Fjuk 的实现细节,因此编译器在分析 Main 时,无法依赖关于这一点。因此,在调用之前我们不为 s 赋值的变体中:

static void Main()
{
    string s;
    try
    {
        Fjuk(out s);
        Console.WriteLine(s ?? "");//fine
    }
    catch (Exception)
    {
        Console.WriteLine(s ?? "");//compiler error
    }
    Console.WriteLine(s ?? "");//compiler error
}

在调用 Fjuk 之后立即尝试使用 s > 很好,因为只有 Fjuk 成功才能到达那里,如果 Fjuk 成功,则必须分配 s。但在第二种和第三种情况下,有可能在 Fjuk 不成功的情况下到达这些行,并且因为无法通过分析 Main 来判断是否可以在 Fjuk 之前抛出异常。如果设置了 code>s,则必须禁止使用 s

It's guaranteed from the perspective of Fjuk but not of Main.

In Fjuk the exception is thrown after the parameter is set. While there can be re-orderings done by compiler, jitter, and CPU, there will not be re-orderings such that the order observed by a single thread changes. Since a single thread could "notice" if the parameter wasn't set before the exception thrown, the parameter is guaranteed to be set.

In Main though, we have no knowledge of the details of Fjuk's implementation, so when the compiler analyses Main, it can't depend on that. Hence in the variation where we don't assign a value to s before the call:

static void Main()
{
    string s;
    try
    {
        Fjuk(out s);
        Console.WriteLine(s ?? "");//fine
    }
    catch (Exception)
    {
        Console.WriteLine(s ?? "");//compiler error
    }
    Console.WriteLine(s ?? "");//compiler error
}

The first attempt to use s immediately after the call to Fjuk is fine, because one can only get there if Fjuk succeeded, and if Fjuk succeeded then s must be assigned. In the second and third case though, it's possible to arrive at those lines without Fjuk succeeding, and since one cannot tell by analysis of Main whether an exception could be thrown before s is set, the uses of s must be prohibited.

深海夜未眠 2025-01-03 14:49:38

来自 C# 语言规范第 5.1.6 节输出参数

在函数成员或委托调用正常完成之后,作为输出参数传递的每个变量都被视为在该执行路径中分配。

换句话说,

  • out 参数总是被分配的。
  • 任何先前的值都会被覆盖。
  • 但如果函数抛出异常,编译器会假设它们没有被分配。

在实践中

  • 你永远不需要担心这个。
  • 如果尝试使用未分配的变量,编译器将生成错误。
  • 因此C# 中的输出参数是完全安全的 - 如果它们编译,它们就可以工作。

From the C# Language Specification section 5.1.6 Output Parameters

Following the normal completion of a function member or delegate invocation, each variable that was passed as an output parameter is considered assigned in that execution path.

In other words

  • out parameters are always assigned.
  • any previous value is overwritten.
  • but if the function throws an exception, the compiler assumes they are not assigned.

In practice

  • You never need to worry about this.
  • If you attempt to use an unassigned variable, the compiler will generate an error.
  • So out parameters in C# are entirely safe - if they compile, they work.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文