views:

238

answers:

6

I have a couple of variables that need to be assigned inside a for loop. Apparently, when the loop exits, C# ignores whatever happened in there, and the variables are returned to their original status. Specifically, I need them to be the last and next-to-last elements of a List. Here's the code:

int temp1, temp2;
for (int i = 0; i < toReturn.Count; i++) {
     if (i == toReturn.Count - 2) { // Next-to-last element
         temp1 = toReturn[i];
     } else if (i == toReturn.Count - 1) { // Last element
         temp2 = toReturn[i];
     }
}
// At this point, temp1 and temp2 are treated as uninitialized

Note: Nevermind the bad variable names, they're really temporary variables. Anything more complex would confuse things.

Now, there are two ways (that I know of) to solve this: one is figuring out how to make the variables live after the loop exits, the other is to do something like in Python, where you can do temp = my_list[-1] to get the last element of a list. Is any of these possible in C#?

Edit: When I try to compile, I get a "use of unassigned local variable 'temp1'" error. This code isn't even run, it's just sitting inside of a method that never gets called. If this helps, I'm trying to use the variables inside another loop.

+5  A: 

If toReturn.Count is 0, the loop never runs and temp1 and temp2 are never initialized.

Michael
That's guaranteed not to happen.
Javier Badia
Are you seeing a compiler error or warning? Or are you saying that the observed values indicate it hasn't been set? For a compiler warning, the compiler can't tell that toReturn.Count is always greater than 0.
Michael
It doesn't matter as far as compiler is concerned, since it cannot reasonably prove that it never happens, which is why it complains about variables being _potentially_ uninitialized. Just initialize them (e.g. assign 0) when you declare them, and it will compile and work.
Pavel Minaev
Well, that makes sense. I still like the other method better.
Javier Badia
"That's guaranteed not to happen." The compiler doesn't believe you. And you haven't been around long enough if you've never seen an error message with the text, "This can never happen."
Brian
+11  A: 

Why not just do...

int temp1 = 0;
int temp2 = 0;
    if (toReturn.Count > 1)
        temp1 = toReturn[toReturn.Count - 2];
    if (toReturn.Count > 0)
        temp2 = toReturn[toReturn.Count - 1];
Chris Arnold
Wow, I never thought of that. Thank you.
Javier Badia
+1 for thinking :)
Mark Schultheiss
+1  A: 

What's this do?

if (toReturn.Count > 1) {
    temp1 = toReturn[toReturn.Count - 2]
    temp2 = toReturn[toReturn.Count - 1]
}
Gordon Bell
A: 

try to give temp1 and temp2 an initial value i.e 0 or whatever is appropriate because they might never be initialized

Yassir
To expand: *you* know temp1 and temp2 will be set. Your code more-or-less guarantees that. But because the assignments happen inside if-statements, the *compiler* has no understanding that temp1/temp2 will be set. As far as it is concerned, it is possible that the If statements will never be true, and the assignment will never happen.
abelenky
A: 
int temp1 = 0; // Or some other value. Perhaps -1 is appropriate.
int temp2 = 0; 

for (int i = 0; i < toReturn.Count; i++) {
     if (i == toReturn.Count - 2) { // Next-to-last element
         temp1 = toReturn[i];
     } else if (i == toReturn.Count - 1) { // Last element
         temp2 = toReturn[i];
     }
}

The compiler requires that temp1 and temp2 are definitely assigned before trying to read their values. The compiler doesn't know your for-loop will assign the variables. It doesn't know if the for loop ever runs at all. It also doesn't know if your if-conditions will ever be true.

The above code makes sure that temp1 and temp2 have been assigned to something. If you want to be sure whether temp1 and temp2 have been assigned in the loop, consider keeping track of this:

int temp1 = 0;
int temp2 = 0;
bool temp1Assigned = false;
bool temp2Assigned = false;

for (int i = 0; i < toReturn.Count; i++) {
     if (i == toReturn.Count - 2) { // Next-to-last element
         temp1 = toReturn[i];
         temp1Assigned = true;
     } else if (i == toReturn.Count - 1) { // Last element
         temp2 = toReturn[i];
         temp2Assigned = true;
     }
}
Joren
In this case Joren I'd recommend using nullable ints rather than an 'Assigned' bool.
Erik Forbes
You're right, that's a way better choice. I don't use nullables nearly enough. :)
Joren
A: 

If you want a default value:

int count = toReturn.Count;
int temp1 = count > 1 ? toReturn[count - 2] : 0;
int temp2 = count  > 0 ? toReturn[count - 1] : 0;

If you don't care about default value and have previous checks for count in place:

int count = toReturn.Count;
int temp1 = toReturn[count - 2];
int temp2 = toReturn[count - 1];
eglasius