Out parameters and exceptions
Asked Answered
E

5

42

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)

Embarrassment answered 18/1, 2012 at 7:51 Comment(4)
I don't know about the specification, but it is certainly what I'd expect. I'd expect that the initialisation of member variables, properties etc would also still be available in your catch block.Polacre
Where's Eric Lippert when you need him... :)Fadil
@Fadil What's wrong with MSDN?Start
@Start why read from a second-hand textbook when I can learn from the teacher himself?Fadil
P
32

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).

Poolroom answered 18/1, 2012 at 7:57 Comment(0)
S
8

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

Start answered 18/1, 2012 at 7:55 Comment(0)
B
6

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
}
Barry answered 18/1, 2012 at 8:10 Comment(1)
Yes, I considered it obvious that it doesn't inspect the code in the called method, but didn't make that clear in my example. Thanks for pointing this out!Embarrassment
S
3

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.

Satchel answered 18/1, 2012 at 9:24 Comment(0)
C
1

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.
Clinkstone answered 13/3, 2018 at 14:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.