3

Supposing I have a string which I want to convert to an integer, I would do

int i;
int.TryParse(someString, out i);

Now I would like to do the same in a Linq query:

int i;
var numbers =
   from s in someStrings
   where int.TryParse(s, out i)
   select i;

But this refuses to compile with the error

CS0165 Use of unassigned local variable 'i'

It compiles and works as intended when I initialize i to an arbitraty value. But why do I have to?

  • few alternatives http://stackoverflow.com/questions/1297231/convert-string-to-int-in-one-line-of-code-using-linq/37033140#37033140 – Slai Sep 04 '16 at 15:35
  • This is great, because LINQ functions are not supposed to create side effects (i.e. change state outside their own method scope). C#, not being a strict functional programming language, can't enforce that, but at least this is one example where it happens to do so. To make this work tidily, you'll have to use an alternative that declares `i` in the delegate (for the `Where` method) itself. – Gert Arnold Sep 04 '16 at 15:50

3 Answers3

6

The query expression is translated into:

var numbers = someStrings.Where(s => int.TryParse(s, out i))
                         .Select(s => i);

Now, we know that the delegate created from the lambda expression for the Where call will be executed before the delegate created from the lambda expression for the Select call, but the compiler doesn't. And indeed, you could easily write your own extension method that didn't obey that:

public static IEnumerable<T> Where<T>(
    this IEnumerable<T> source,
    Func<T, bool> predicate)
{
    // I can't be bothered to check the predicate... let's just return everything
    return source;
}

At that point, with the normal Select call, your delegate returning i would be executed without every assigning a value to it.

Basically, the definite assignment rules are deliberately quite conservative, avoiding making any assumptions about what methods do etc.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • What would be the value of i if the Where selected no values ? I think thats what the compiler is complaining about. – PhillipH Sep 04 '16 at 15:30
  • @PhillipH: With normal LINQ, if the `Where` predicate always returned `false`, the `Select` projection would never be called - so that wouldn't be a problem. The problem is the opposite - that the compiler doesn't know that the `Where` predicate *will* always be called before `Select`. – Jon Skeet Sep 04 '16 at 15:33
2

This compiles to a pair of lambda expressions that are passed to Where() and Select().

The compiler does not know what Where() and Select() do, and cannot prove that Where()'s lambda will always run before Select().

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
-1

The compiler cannot determine until runtime whether or not there will be any records returned. So in order for i to have a determined value, one must be explcitly assigned. Just think "what would i be if my Query returned no rows ?"

PhillipH
  • 6,182
  • 1
  • 15
  • 25
  • Then the sequence would be empty, and the `s => i` lambda expression would never be executed. – Jon Skeet Sep 04 '16 at 15:33
  • But .net cannot determine at compile time whether it ever will be executed, and this would lead to the select statement, which may be executed (as far as the compiler knows), operating on an undefined variable. – PhillipH Sep 04 '16 at 17:02
  • But that wouldn't be the case if the sequence is empty, that's my point - the situation you've suggested would be a case where it *wasn't* a problem, whereas it's more useful to show a case which *would* be a problem. – Jon Skeet Sep 04 '16 at 17:25
  • @JonSkeet - I'm not being clear, or am not understanding your point - either way a good thread. If someStrings is empty then the Where function never gets called. In this case "i" is undefined, so cannot be assigned in the Select. The compiler cannot tell whether or not someStrings will be empty, so sees "i" as potentially unassigned (since its assignation is dependent on some externality). Its the same as "float i; if(z == y) t = 5; float j = t;" The assignment of "t" is dependent, so is potentially undefined at the point of usage. A definite assignment is required. – PhillipH Sep 05 '16 at 08:46
  • If the delegate passed to `Where` never gets called, why would the delegate passed to `Select` get called? What would you expect its input to be? Again, you're focusing on a corner case which actually *wouldn't* be a problem. It's better to give examples where it *would*, as I tried to in my answer. – Jon Skeet Sep 05 '16 at 08:47
  • It doesn't matter whether it is or isnt called. This is not a runtime error - its a compile time error. The value of "i" cannot be said to be definitely assigned because its dependent on the number of items in someStrings - so correctly the compiler flags this. – PhillipH Sep 05 '16 at 08:48
  • No, the value of `i` cannot be said to be definitely assigned because that would require the compiler to know what `Where` did, and that the number of times the delegate was called depends on the number of items in `someStrings` - if it knew that much information, it could probably also know that if `someStrings` is empty than the *use* of `i` wouldn't be reachable anyway. The special case of `someStrings` being empty is simply a red herring here - I don't think it helps anything. – Jon Skeet Sep 05 '16 at 08:50
  • @JonSkeet - but thats why the compiler is protesting with a "CS0165" becuase the code is structued such that there is no definite execution of the Where code block. You marked down my answer; but it explains why the OP was getting the compiler error - because the code is written in such a way that it cannot determine whether the assignment ever gets executed. We seem to be agreeing with each other here - anyhow SO tells me we are out of comment thread space, :-) – PhillipH Sep 05 '16 at 09:31
  • Your answer talks about whether any records will be returned as the cause of the problem. That's *not* the cause of the problem - I don't believe anyone reading just your answer would understand the real issue. – Jon Skeet Sep 05 '16 at 09:36