Using a variable as an out argument at point of declaration
Asked Answered
D

4

15

When reading a comment to an answer I saw the following construct to declare and initialize a variable:

int variable = int.TryParse(stringValue, out variable) ? variable : 0;

Is this allowed, correct and well defined in C#? What happens under the hood? Is the following what happens?

  1. Is variable first initialized to zero?
  2. then passed to int.TryParse (which assigns a value)?
  3. then optionally read (if int.TryParse return true)?
  4. and then, once again assigned/initialized?
Disapproval answered 10/9, 2015 at 8:40 Comment(2)
1. No (local variables are not assigned by default)Disposable
2. Yes, out means that value will be assignedDisposable
B
8

This is a trick that happens to work because it is simply a rewriting of an ordinary if-statement. This code is equivalent to this:

int variable;
if (int.TryParse(stringVariable, out variable))
    variable = variable;
else
    variable = 0;

The sequence is as follows:

int.TryParse is called, variable is not initialized before this but it doesn't have to either. An out parameter does not require a definite assigned variable. As part of the method execution, the variable will be given a value, and int.TryParse will return true or false.

If the method returns true then the result of the expression will be variable and thus we will execute basically variable = variable.

If the method returns false then the result of the expression will instead be 0, and variable will now be given the value 0 regardless of what it was given as part of int.TryParse. In this case, however, this will not change the variable because int.TryParse has already given the variable a default value when it returns false which also happens to be 0.

This is basically a way to get everything onto one line.

Personally I would've written this code like this:

int variable;
int.TryParse(stringValue, out variable);
Brooklyn answered 10/9, 2015 at 8:47 Comment(1)
Personally I would have written the code as in your last line, adding an extra line to the extent of // If TryParse fails it initializes variable = 0. I am sure that otherwise it would really confuse future me :)Nativity
T
10

Yes you are right for execution. You can also look into MSIL generated here

C# Code

 string stringValue = "5";
 int variable = int.TryParse(stringValue, out variable) ? variable : 0;

MSIL generated

1.  IL_0000:  nop    
2.  IL_0001:  ldstr      "5" // load string
3.  IL_0006:  stloc.0
4.  IL_0007:  ldloc.0
5.  IL_0008:  ldloca.s   variable
6.  IL_000a:  call       bool [mscorlib]System.Int32::TryParse(string, int32&)
7.  IL_000f:  brtrue.s   IL_0014
8.  IL_0011:  ldc.i4.0
9.  IL_0012:  br.s       IL_0015
10. IL_0014:  ldloc.1
11. IL_0015:  stloc.1
12. IL_0016:  ret

Which clarifies what it does behind the scene.

Statement 5 is allocating the variable onto stack. Statement 6 is calling the method. Statement 7,8,9 are actually exeuting the bool expression.

Tiannatiara answered 10/9, 2015 at 8:55 Comment(0)
B
8

This is a trick that happens to work because it is simply a rewriting of an ordinary if-statement. This code is equivalent to this:

int variable;
if (int.TryParse(stringVariable, out variable))
    variable = variable;
else
    variable = 0;

The sequence is as follows:

int.TryParse is called, variable is not initialized before this but it doesn't have to either. An out parameter does not require a definite assigned variable. As part of the method execution, the variable will be given a value, and int.TryParse will return true or false.

If the method returns true then the result of the expression will be variable and thus we will execute basically variable = variable.

If the method returns false then the result of the expression will instead be 0, and variable will now be given the value 0 regardless of what it was given as part of int.TryParse. In this case, however, this will not change the variable because int.TryParse has already given the variable a default value when it returns false which also happens to be 0.

This is basically a way to get everything onto one line.

Personally I would've written this code like this:

int variable;
int.TryParse(stringValue, out variable);
Brooklyn answered 10/9, 2015 at 8:47 Comment(1)
Personally I would have written the code as in your last line, adding an extra line to the extent of // If TryParse fails it initializes variable = 0. I am sure that otherwise it would really confuse future me :)Nativity
D
5

int variable declares variable, and out variable necessarily initializes it. Both of these things must happen before variable is used anywhere, and because of the out declaration, this holds true.

As pointed out by Lasse V. Carlsen, from TryParse's documentation, TryParse will by default assign it the value of 0 if the conversion fails:

When this method returns, [return] contains the 32-bit signed integer value equivalent of the number contained in s, if the conversion succeeded, or zero if the conversion failed. (emph. mine)

If you expand the ternary function out, you'd see:

int variable;
if (int.TryParse(stringValue, out variable))
    variable = variable;
else
    variable = 0;

which is, in and of itself, a legal expression. The two paths are:

  • TryParse assigns the value to variable and returns true, leading to an assignment of variable to itself
  • TryParse initializes variable to 0 and returns false, leading to the assignment of variable as zero by the ternary condition

This isn't particularly clear code, though, and I wouldn't recommend doing it.

Disrepair answered 10/9, 2015 at 8:46 Comment(7)
Actually, if TryParse returns false, it still has to set variable, and in this case it is documented to be initialized to 0: When this method returns, contains the 32-bit signed integer value equivalent of the number contained in s, if the conversion succeeded, or zero if the conversion failed. (my emphasis)Brooklyn
@LasseV.Karlsen Thanks! I've edited that into the answer.Disrepair
@LasseV.Karlsen: Although int.TryParse is documented as always writing the passed-in variable (setting it to zero in case of error), there is in general no guarantee that a function written in another language will write to out parameters before returning. Personally, I think the "try" pattern should have been T TryComputeSomething(params, out SuccessOrFailure success);, with the failure case returning default(T). [I'd have SuccessOrFailure be a class with defined singletons for success or failure, but supporting the ability to add more info as well].Arlenaarlene
Having TryComputeSomething return a value of type T would make it clear that the failure case returns default(T) rather than leaving the argument unmodified, and would also allow for covariance and type inference in ways the out T pattern does not.Arlenaarlene
@Arlenaarlene That is true since the CLR does not have the concept of an out parameter, only a ref parameter, but if a compiler (or programmer) uses a language that allows one to compile a method as one using it as a ref parameter and then insists on tucking on the special attribute that makes C# consider it an out parameter I daresay that is not a bug in the calling code. In C#, after a call to a method with an out parameter, that variable is considered having a value. I'm not sure I like out parameters at all though and would probably rather use a type akin to Nullable<T>.Brooklyn
@LasseV.Karlsen: Code implementing something like IDictionary.TryGetValue in VB.NET will see the signature as Function TryGetValue(key as TKey, ByRef value as TValue) As Boolean Implements IDictionary.TryGetValue. Given that signature, it's hardly obvious that the function should clear value if the key is not found, especially since in many cases it would be more useful [though C# wouldn't allow it because value is an out-parameter] to leave the value unmodified in such cases (there are many cases where similar libraries I've used in other languages...Arlenaarlene
...like Pascal would allow the pattern x:=23; TryReading(x, success); and have TryReading either set x to the value read or leave it set to 23.) I suspect the real difficulty stems from the fact that an argument between vb.net and C# designers about whether .NET should recognize out parameters was never resolved, but instead both sides simply act as though they won.Arlenaarlene
H
2

I haven't opened Visual Studio to try this, but yes, this is allowed.

It all depends on the "stringValue" variable. If that is parsable to an integer, int.TryParse will return true, and [variable] will have an integer value. If not, then variable will be set to 0.

Does this make for readable code? Apparently not.

Hotshot answered 10/9, 2015 at 8:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.