When you do stuff like:
for (int i = 0; i < collection.Count; ++i )
is collection.Count called on every iteration?
Would the result change if the Count property dynamically gets the count on call?
Yes Count will be evaluated on every single pass. The reason why is that it's possible for the collection to be modified during the execution of a loop. Given the loop structure the variable i should represent a valid index into the collection during an iteration. If the check was not done on every loop then this is not provably true. Example case
for ( int i = 0; i < collection.Count; i++ ) {
collection.Clear();
}
The one exception to this rule is looping over an array where the constraint is the Length.
for ( int i = 0; i < someArray.Length; i++ ) {
// Code
}
The CLR JIT will special case this type of loop, in certain circumstances, since the length of an array can't change. In those cases, bounds checking will only occur once.
Reference: http://blogs.msdn.com/brada/archive/2005/04/23/411321.aspx
Count would be evaluated on every pass. If you continued to add to the collection and the iterator never caught up, you would have an endless loop.
class Program
{
static void Main(string[] args)
{
List<int> intCollection = new List<int>();
for(int i=-1;i < intCollection.Count;i++)
{
intCollection.Add(i + 1);
}
}
}
This eventually will get an out of memory exception.
Yes count is checked at every call from the first iteration after the initialization of i to the last iteration where the check fails and the for loop is exited. You can modify the collections count if you want but realize you could end up in an endless loop.
Like the other answers here: Yes, in principal.
There is (at least) one noticeable exception, array.Length. In
for (int i = 0; i < a.Length; i++) a[i] = ...;
The Length property will only be evaluated once. This is a optimization that is hardwired into the compiler. There might be others like that (in the future) but only if it is guaranteed not to make a difference in observable behavior.
Side note, this is NOT checked for every interation in VB.
Unlike C#, VB caches the result of the collection.Count.
EDIT:
The literal VB version of the C# for loop is:
Dim i = 0
Do While i < collection.Count
'code goes here
i+=1
Loop
Related
I was following a tutorial for how to make an insertion sort and came across this line of code:
for(; j >= 0 && tempVar < insertionArray[j]; j--)
My question is, what does the single semicolon mean after the first bracket?
I know that a double semicolon in a for loop defines an infinite loop,
for(;;)
but what does a single one mean?
There are 3 parts to a for loop, each separated by semicolons:
initialisation; evaluation ; iteration
for ( int i = 0 ; i < someValue ; i++ )
If you have already initialized the variable that you want to evaluate and iterate through, you don't need to do it in the loop:
int i = 0;
for(; i < someValue ; i++ )
The first part of a for loop is to declare and initialize the counter variable (j in this case). Since it was declared and initialized before the loop, we don't do it again.
This just the initializer section
The statements in the initializer section are executed only once, before entering the loop. The initializer section is either of the following:
The declaration and initialization of a local loop variable, which can't be accessed from outside the loop.
Zero or more statement expressions from the following list, separated by commas:
assignment statement
invocation of a method
prefix or postfix increment expression, such as ++i or i++
prefix or postfix decrement expression, such as --i or i--
creation of an object by using new keyword
await expression
It divides the line into different blocks.
The first part is one time statement, the second is the condition of the loop and the third is a operation to do after/before the loop's code.
I have a class that contains four EnumerableRowCollections, which all point to the same DataTable. The main one will need different combinations of the other three filtered out in different class instances. Since three of them are related, I put them in an array.
EnumerableRowCollection<DataRow> valid;
EnumerableRowCollection<DataRow>[] pending;
All of these collections are defined in the class constructor, but evaluated later due to LINQ's lazy evaluation.
I also have an array of Booleans, which are used to determine which "pending" collections are filtered out of the "valid" collection. These are also assigned in the constructor, and are never changed.
Boolean[] pendingIsValid;
The "valid" collection is filtered like this:
for (var i = 0; i < pending.Length; i++)
if (pendingIsValid[i] && pending[i].Count() > 0)
valid = valid.Where(r => !pending[i].Contains(r));
This also occurs in the constructor, but the Where clause is evaluated lazily, as expected.
This works most of the time, however, in a few cases I got a weird exception when the collection evaluation took place down the road.
I get an IndexOutOfRange because of the local iterator variable, i, in my for loop above is set to 3.
Questions:
Can I make "Where" evaluate the array indexer (or other sub-expressions) non-lazily?
How does the iterator get incremented to 3 at all? Does this lazy evaluation count as "re-entering" the loop?
!?!?
Change it to this:
for (var i = 0; i < pending.Length; i++)
if (pendingIsValid[i] && pending[i].Count() > 0)
{
var j = i;
valid = valid.Where(r => !pending[j].Contains(r));
}
For question #1 - you can make it not lazy by adding .ToList() at the end. However, with the above fix, you can keep it lazy.
Have a read of this: Captured variable in a loop in C# for the explanation
Excellent, Rob. I also figured out this while I was waiting for a response, but yours looks a bit cleaner.
for (var i = 0; i < pending.Length; i++) {
var p = pending[i];
if (pendingIsValid[i] && p.Count() > 0)
valid = valid.Where(r => !p.Contains(r));
}
Having problem with the method down below. The current error I get is "it does not return any value" If I put the return num outside of the brackets, it does not exist in the current context.
If I remove the brackets all together I get:
Embedded statement cannot be a declaration or labeled statement
The name 'rnd' does not exist in the current context
I've done multiple methods before without this issue. What am I missing?
public static int generateNumber(int timesUserWantsToGuess)
{
for (int i = 0; i <= timesUserWantsToGuess; i++)
{
Random rnd = new Random();
int num = rnd.Next(1, 50);
return num;
}
}
First off you have a mismatch of semantics. You are returning a single int, but are executing and returning within a loop.
If you actually wanted to return a collection of ints, you could do something like this:
public static IEnumerable<int> generateNumber(int timesUserWantsToGuess)
{
//Optional, but good error checking
if (timesUserWantsToGuess <= 0)
throw new ArgumentException("Invalid guess count");
Random rnd = new Random();
for (int i = 0; i < timesUserWantsToGuess; i++)
{
int num = rnd.Next(1, 50);
yield return num;
}
}
Note the use of yield to return the next object in the sequence. You also shouldn't instantiate Random in a tight loop, since it will cause the same values to be generated (same seed). Also, using <= in the for conditional will yield one more iteration than the passed value, so I switched to <.
If you didn't want a loop at all, then just remove it and your problem will be solved. At the moment, the loop semantic and the rest of your code mismatch, which is causing you the problem.
The issue is that since the return statement exists inside a loop, the compiler assumes there's a chance it could execute without ever hitting the block, and thus won't return. This could logically happen if timesUserWantsToGuess is less than 0 - the loop never enters in this case. The reason this code compiles if the return statement is outside the block is that by being outside the block, the execution guarantees it'll hit the return (or throw an exception) at some point.
If you remove the brackets on a block in C# (if, for, foreach, lock, and while all do this to my knowledge) you are saying that the block contains only the next line (which is indented by convention) and nothing more. Since variable declaration on a one-line ("embedded") statement can't be used anywhere else syntactically, it doesn't make sense to do it, so the compiler forbids it altogether. Be noted it is also possible to declare a variable in a block without using it, as long as the block is bracketed.
Finally, there's a huge semantic mismatch in the logic of returning a single value during a loop like this: see #BradleyDotNET's answer on yield return statements
Is there an equivalent method of performing the job of redo in C#? i.e. going back to the top of the loop and re-execute without checking conditions or increasing the loop counter. Thanks.
for (int i = 0; i < 100; i++)
{
do
{
DoYourStuff();
} while (ShouldWeDoThatAgain());
}
Do...while is like a standard while loop, except instead of checking its conditional before each iteration, it checks after. That way, the code inside the loop will always execute at least once. Stick that inside a for or foreach loop, and that should get you the behavior your want. This is a bit simpler than Simon's answer, as it doesn't require an extra variable, doesn't use continue, and doesn't mess with the loop counter at all.
Why not simply:
Although goto is not really everyone's favourite, it's quite readable in this case...
for(...)
{
redo:
//...
if (...)
goto redo;
}
No. The closest you'll get is something like this:
bool redoCalled = false:
for (int i = 0; i < 10; i++) {
if (redoCalled) {
i--;
redoCalled = false;
}
// other stuff here
if (redoWanted) {
redoCalled = true;
continue;
}
}
I'm trying to run a basic loop that will find a specific value in a dataview grid. I cannot figure out whats going on with the code, since the for loop exits before evaluating its basic condition.
private void SearchDataViewGrid(string FileName)
{
//finds the selected entry in the DVG based on the image
for (int i = 0; i == dataPartsList.Rows.Count ; i++)
{
if(FileName == dataPartsList.Rows[i].Cells[3].Value.ToString())
{
dataPartsList.Rows[i].Selected = true;
}
}
}
The program doesn't crash, but i get an error on my 'i' variables declaring that it has been optimised away. Tried a few easy fixes i found online but nothing seems to keep it.
I have verified that the string i am passing is the correct one, and my 'dummy' DVG returns a value of 14 for the number of rows contained. Even if i remove the 'if' statement inside of the for loop, i still get the same error.
The condition cond in the middle of for(init; cond; update) is not an until condition but a while condition.
So you need to change it to
for (int i = 0; i < dataPartsList.Rows.Count ; i++)
The problem is your conditional is i == dataPartsList.Rows.Count so the body will only execute when these two values are equal. This guarantees your loop will never execute. You need to change your conditional to be < instead of ==
for (int i = 0; i < dataPartsList.Rows.Count ; i++) {
...
}