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?
Community
  • 1
  • 1
dalle
  • 18,057
  • 5
  • 57
  • 81

4 Answers4

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.

D J
  • 6,908
  • 13
  • 43
  • 75
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);
Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
  • 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 :) – CompuChip Sep 10 '15 at 15:34
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.

  • 2
    Actually, if `TryParse` returns `false`, it still has to set `variable`, and in this case it is [documented](https://msdn.microsoft.com/en-us/library/f02979c7(v=vs.110).aspx) 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)* – Lasse V. Karlsen Sep 10 '15 at 08:50
  • @LasseV.Karlsen Thanks! I've edited that into the answer. –  Sep 10 '15 at 08:53
  • @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]. – supercat Sep 10 '15 at 16:01
  • 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. – supercat Sep 10 '15 at 16:05
  • @supercat 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`. – Lasse V. Karlsen Sep 10 '15 at 17:23
  • @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... – supercat Sep 10 '15 at 18:22
  • ...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. – supercat Sep 10 '15 at 18:27
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.

Liam
  • 27,717
  • 28
  • 128
  • 190
chrisl08
  • 1,658
  • 1
  • 15
  • 24