Multiple initialization in C# 'for' loop

How can I (if it is possible at all) initialize multiple variables of different type in a C# for loop? Example:

for (MyClass i = 0, int j = 1; j<3; j++,i++)
107901 次浏览

It can't be done. Put one of the declarations before the loop:

MyClass i = 0;
for (int j = 1; j < 3; j++, i++)

Or for symmetry, both of them:

MyClass i = 0;
int j = 1;
for (; j < 3; j++, i++)

It's also possible that one of the variables is more primary than the other. In that case it might be neater to have one be the loop variable, and deal with the other seperately, like this:

MyClass i = 0;
for (int j = 0; j < 3; j++)
{
...
i++;
}

Note that if i and j were of the same type, then you could declare them both in the for-loop:

for (int i = 0, j = 1; j < 3; j++, i++)

I don't think you can define more than one type inside a for loop. only for(int i=0,j=3; j<7; j++, i++)

Yes, it can be done. You can initialize variables of different types inside a for statement, but you cannot declare variables of different types inside a for statement. In order to initialize variables of different types inside a for statement you must declare all the types before the for loop. For example:

int xx;
string yy;
for(xx=0, yy=""; xx<10; xx++)
{
....
}

[EDIT] Adding more information for completeness. This goes beyond what the OP requested, but may be helpful for others. It is simple to initialize variables of the same type in a for loop, just separate the initialization by commas. You can also have multiple variables changed in the third section. You cannot have multiple comma separated sections in the second, comparison section, but you can use && || and ! to make a complex Boolean section based on multiple variables.

for(int i=0, j=0, k=99; i<10 && k<200; i++, j++, k += 2)

However, it is not a good practice to make a for statement that is so complex that it is difficult to understand what is going on.

You can't define more then one variable in a loop structure. Try the code below:

Option 1: One variable declared before loop and manually incremented in loop once per iteration.

MyClass i = 0;
for (int j = 1; j<3; j++)
{
//do stuff
i++
}

Option 2: both variables set before for loop and one incremented in loop structure, and other in loop manually.

MyClass i = 0;
int j = 1
for (; j<3; j++)
{
//do stuff
i++
}

Option 3: both variables are set before for loop structure, and both variables are incremented in the loop, leaving the loop to only check for a condition, which at this point you could just do a while loop.

MyClass i = 0;
int j = 1
for (; j<3)
{
//do stuff
j++
i++
}

Option 4: write as a while loop

MyClass i = 0;
int j = 1
while (j<3)
{
//do stuff
j++
i++
}
for (initializer; condition; iterator)
{
//body
}

The initializer section sets the initial conditions. The statements in this section run only once, before you enter the loop. The section can contain only one of the following two options.

1)The declaration and initialization of a local loop variable. The variable is local to the loop and can't be accessed from outside the loop.

2)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;

  • Await expression;

As we know the compiler is not designed to accept the way we expect. So the above are the rules have to be followed before writing a initializer section inside a for loop.

http://msdn.microsoft.com/en-us/library/ch45axte.aspx

Is it harmful?

Yes, very much so. A language parser has two important duties. One is the job that everybody is familiar with, convert text into an executable program. But very important as well is that it can detect an invalid program and generate a meaningful diagnostic to the programmer so he can fix his code.

At a very fundamental level, a language parser distinguishes between declarations, statements and expressions. The curly-brace languages do obfuscate that distinction, you can turn any expression into a statement, simply by putting a semi-colon after it. And in some cases accept a declaration inside a statement, the for(;;) statement is a good example. Most plainly, this syntax is perfectly acceptable in the C or C++ languages:

int x = 42;
x;

That's not exactly a good thing, it is nonsense code. The C# language upped the bar on that, it will reject that. But not:

int x = 42;
x++;

A special rule added to the language parser to accept this.

What none of the curly-brace languages will accept is turning a declaration into an expression. That way lies madness, dragons on the end of the map, ship falls off the edge with no good message left to report. The comma operator requires the lefthand and righthand operands to be expressions. A declaration is not an expression, end of story.

This is not particularly my expertise, but here is my brainstorming about the topic:

In programmimng languages theory, a language's syntax must be defined without ambiguity. I cannot go into a very deep detail on it as it has been couple of years since I studied those topics. But you can check Backus-Naur Form that is a notation technique to describe the "grammar" of the language. And that is the only one I am familiar with.

So this description is used while parsing the code. And your parser must be able to relate every part of your code to the "rules" of the grammar. You can see C# Grammar here. It is somewhat in a similar form.

(1) Take a look at for-statement syntax

for-statement:
for(for-initializer;for-condition;for-iterator)
embedded-statement

and then for-initializer syntax

for-initializer:
local-variable-declaration
statement-expression-list

Note that statement-expression-list is only used in for loops. Also it is a comma separated list of statement-expressions.

I will leave some intermediate steps here but you can follow the grammar to get yourself more comfortable with the idea.

Here is a good basic set of slides that demonstrates how complicated things can get even with a trivially simple grammar.

(2) What we observed in 1 is what can we put for initializer part of the for loop. And we know that why your suggestion does not work. To be the bounty hunter let's analyze the reason of this design choice.

First, it is a design choice. You can design or find a language that allows that. It should be possible by changing the grammar, however some syntactical changes might be necessary. Here is why;

If you are going to put multiple declaration statements, you might want to have something like declaration-list. And what you are going to use for seperator, you probably would not want to use ; because a semicolon is used for separating parts of the for loop. So, you can always go for comma, but if you use comma, then declaration-list rule can only be used in for loops, as it would be confusing to have comma separated declarations all over your code.

Second, what is wrong with that? If you ask me, I do not see anything wrong either. If they have designed the language that it should have worked. (I am not saying that the sketch I just made is 100% correct, it can only be used as a starting point of a brainstorming session.)

Then, why did they chose not to? What pushed them to avoid that?

  • How much complication do you introduce by adding that rule to your grammar?
  • Do you also consider how many additional rules do you have to add to make the language unambiguous?
  • Do you realize how complicated can already-existing rules get?
  • How much workload that adds to your parser and then compiler?

After all these and many other considerations,

  • How much do you add to your language's usability, readability?
  • Or do you even lose some readability?
  • Do you consider, how frequently programmers need those types of for loops?
  • Even if they do need it frequently, should you be encouring or discouraging them for that type of coding?

... and so many more questions like these, must be thougt carefully. And by looking at those questions and their analysis, I would say compiler designers' design choice was pretty much the most acceptable approach.

Of course it CAN be done. Just use the dynamic keyword:

public static void Main(string[] args) {
for (dynamic x = 0, y = new MyClass { a = 20, b = 30 }; x < 100; x++, y.a++, y.b--) {
Console.Write("X=" + x + " (" + x.GetType() + "\n" +
"Y.a=" + y.a + ",Y.b=" + y.b + " (" + y.GetType() + "\n");
}
}


class MyClass {
public int a = 0, b = 0;
}

Have a great day!

There is little reason not to have an alternate indexer initialized in-band. It keeps the environment clear of trashy var assignments.

for (int x=0,y = 0; x < 100; x++)
{
if (true) {  y++; }
// ... use y as a conditional indexer
// ... x is always the loop indexer
// ... no overflows
}

I usualy put the declarations before loop, and use additional curly braces to limit the scope of the declarations:

{ //limit the scope: i, count, iDivisibleBy2, iDivisibleBy3, iDivisibleBy5
int i = 0, count = 100;
bool iDivisibleBy2 = true, iDivisibleBy3 = true, iDivisibleBy5 = true;
for( ; i < count; ++i, iDivisibleBy2 = (i % 2 == 0), iDivisibleBy3 = ( i % 3 == 0 ), iDivisibleBy5 = ( i % 5 == 0 ) )
{
//...
}
}

Let's have some fun. I'll leave it up to you to decide whether you should actually use this anywhere... :P

It is possible to (indirectly) declare and initialize as many variables as you want of different types in the for loop initializer without using the dynamic key word. Just use a custom struct for your index variable.

for(var i = new I1<MyClass>(0, 1); i < 3; i++, i.a++) {
MyClass myClass = i.a;
}

Overloaded operators mean you can use "i" like an int everywhere. For a clean syntax, initialize with 0:

for(I1<float> i = 0; i < array.Length; i++) {
i.a += array[i]; // accumulate a float value
}

A few more silly examples:

// Three variables
for(I3<object, string, int> i = 0; i < 100; i++) {
i.a = new object();
i.b = "This is index " + i;
i.c = 100 - i;
}


// A class
for(var i = new I1<SomeClass>(0, new SomeClass()); i < 20; i += 2) {
i.a.someVar1 = "We can have even more variables in here! Woot!";
i.a.DoSomething(i);
}


// An array
for(var i = new I1<string[]>(0, new[] { "Hi", "Mom" }); i < 10; i++) {
for(int j = 0; j < i.a.Length; j++) {
Log(i.a[j]);
}
}

Here are the structs. They do work but are not thoroughly tested so there may be bugs:

public struct I1<T> {


public int index;
public T a;


public I1(int index) {
this.index = index;
this.a = default(T);
}
public I1(int index, T a) {
this.index = index;
this.a = a;
}


public override bool Equals(object obj) {
if(!(obj is I1<T>)) return false;
I1<T> other = (I1<T>)obj;
return index == other.index && EqualityComparer<T>.Default.Equals(a, other.a);
}


public override int GetHashCode() {
int hash = 17;
hash = hash * 29 + index.GetHashCode();
if(typeof(T).IsValueType && !object.ReferenceEquals(a, null)) hash = hash * 29 + a.GetHashCode();
return hash;
}


public override string ToString() {
return index.ToString();
}


public static implicit operator I1<T>(int other) {
return new I1<T>(other);
}


public static implicit operator int(I1<T> other) {
return other.index;
}


// Unary operators


public static int operator +(I1<T> a) {
return +a.index;
}


public static int operator -(I1<T> a) {
return -a.index;
}


public static int operator ~(I1<T> a) {
return ~a.index;
}


public static I1<T> operator ++(I1<T> a) {
a.index++;
return a;
}


public static I1<T> operator --(I1<T> a) {
a.index--;
return a;
}


// Binary operators


public static I1<T> operator +(I1<T> a, int b) {
a.index += b;
return a;
}
public static I1<T> operator +(int a, I1<T> b) {
b.index += a;
return b;
}


public static I1<T> operator -(I1<T> a, int b) {
a.index -= b;
return a;
}
public static I1<T> operator -(int a, I1<T> b) {
b.index = a - b.index;
return b;
}


public static I1<T> operator *(I1<T> a, int b) {
a.index *= b;
return a;
}
public static I1<T> operator *(int a, I1<T> b) {
b.index *= a;
return b;
}


public static I1<T> operator /(I1<T> a, int b) {
a.index /= b;
return a;
}
public static I1<T> operator /(int a, I1<T> b) {
b.index = a / b.index;
return b;
}


public static I1<T> operator %(I1<T> a, int b) {
a.index %= b;
return a;
}
public static I1<T> operator %(int a, I1<T> b) {
b.index = a % b.index;
return b;
}


public static I1<T> operator &(I1<T> a, int b) {
a.index &= b;
return a;
}
public static I1<T> operator &(int a, I1<T> b) {
b.index = a & b.index;
return b;
}


public static I1<T> operator |(I1<T> a, int b) {
a.index |= b;
return a;
}
public static I1<T> operator |(int a, I1<T> b) {
b.index = a | b.index;
return b;
}


public static I1<T> operator ^(I1<T> a, int b) {
a.index ^= b;
return a;
}
public static I1<T> operator ^(int a, I1<T> b) {
b.index = a ^ b.index;
return b;
}


public static I1<T> operator <<(I1<T> a, int b) {
a.index <<= b;
return a;
}


public static I1<T> operator >>(I1<T> a, int b) {
a.index >>= b;
return a;
}


// Comparison operators


public static bool operator ==(I1<T> a, int b) {
return a.index == b;
}
public static bool operator ==(int a, I1<T> b) {
return a == b.index;
}


public static bool operator !=(I1<T> a, int b) {
return a.index != b;
}
public static bool operator !=(int a, I1<T> b) {
return a != b.index;
}


public static bool operator <(I1<T> a, int b) {
return a.index < b;
}
public static bool operator <(int a, I1<T> b) {
return a < b.index;
}


public static bool operator >(I1<T> a, int b) {
return a.index > b;
}
public static bool operator >(int a, I1<T> b) {
return a > b.index;
}


public static bool operator <=(I1<T> a, int b) {
return a.index <= b;
}
public static bool operator <=(int a, I1<T> b) {
return a <= b.index;
}


public static bool operator >=(I1<T> a, int b) {
return a.index >= b;
}
public static bool operator >=(int a, I1<T> b) {
return a >= b.index;
}
}


public struct I2<T1, T2> {


public int index;
public T1 a;
public T2 b;


public I2(int index) {
this.index = index;
this.a = default(T1);
this.b = default(T2);
}
public I2(int index, T1 a) {
this.index = index;
this.a = a;
this.b = default(T2);
}
public I2(int index, T1 a, T2 b) {
this.index = index;
this.a = a;
this.b = b;
}


public override bool Equals(object obj) {
if(!(obj is I2<T1, T2>)) return false;
I2<T1, T2> other = (I2<T1, T2>)obj;
return index == other.index && EqualityComparer<T1>.Default.Equals(a, other.a) && EqualityComparer<T2>.Default.Equals(b, other.b);
}


public override int GetHashCode() {
int hash = 17;
hash = hash * 29 + index.GetHashCode();
if(typeof(T1).IsValueType && !object.ReferenceEquals(a, null)) hash = hash * 29 + a.GetHashCode();
if(typeof(T2).IsValueType && !object.ReferenceEquals(b, null)) hash = hash * 29 + b.GetHashCode();
return hash;
}


public override string ToString() {
return index.ToString();
}


public static implicit operator I2<T1, T2>(int other) {
return new I2<T1, T2>(other);
}


public static implicit operator int(I2<T1, T2> other) {
return other.index;
}


// Unary operators


public static int operator +(I2<T1, T2> a) {
return +a.index;
}


public static int operator -(I2<T1, T2> a) {
return -a.index;
}


public static int operator ~(I2<T1, T2> a) {
return ~a.index;
}


public static I2<T1, T2> operator ++(I2<T1, T2> a) {
a.index++;
return a;
}


public static I2<T1, T2> operator --(I2<T1, T2> a) {
a.index--;
return a;
}


// Binary operators


public static I2<T1, T2> operator +(I2<T1, T2> a, int b) {
a.index += b;
return a;
}
public static I2<T1, T2> operator +(int a, I2<T1, T2> b) {
b.index += a;
return b;
}


public static I2<T1, T2> operator -(I2<T1, T2> a, int b) {
a.index -= b;
return a;
}
public static I2<T1, T2> operator -(int a, I2<T1, T2> b) {
b.index = a - b.index;
return b;
}


public static I2<T1, T2> operator *(I2<T1, T2> a, int b) {
a.index *= b;
return a;
}
public static I2<T1, T2> operator *(int a, I2<T1, T2> b) {
b.index *= a;
return b;
}


public static I2<T1, T2> operator /(I2<T1, T2> a, int b) {
a.index /= b;
return a;
}
public static I2<T1, T2> operator /(int a, I2<T1, T2> b) {
b.index = a / b.index;
return b;
}


public static I2<T1, T2> operator %(I2<T1, T2> a, int b) {
a.index %= b;
return a;
}
public static I2<T1, T2> operator %(int a, I2<T1, T2> b) {
b.index = a % b.index;
return b;
}


public static I2<T1, T2> operator &(I2<T1, T2> a, int b) {
a.index &= b;
return a;
}
public static I2<T1, T2> operator &(int a, I2<T1, T2> b) {
b.index = a & b.index;
return b;
}


public static I2<T1, T2> operator |(I2<T1, T2> a, int b) {
a.index |= b;
return a;
}
public static I2<T1, T2> operator |(int a, I2<T1, T2> b) {
b.index = a | b.index;
return b;
}


public static I2<T1, T2> operator ^(I2<T1, T2> a, int b) {
a.index ^= b;
return a;
}
public static I2<T1, T2> operator ^(int a, I2<T1, T2> b) {
b.index = a ^ b.index;
return b;
}


public static I2<T1, T2> operator <<(I2<T1, T2> a, int b) {
a.index <<= b;
return a;
}


public static I2<T1, T2> operator >>(I2<T1, T2> a, int b) {
a.index >>= b;
return a;
}


// Comparison operators


public static bool operator ==(I2<T1, T2> a, int b) {
return a.index == b;
}
public static bool operator ==(int a, I2<T1, T2> b) {
return a == b.index;
}


public static bool operator !=(I2<T1, T2> a, int b) {
return a.index != b;
}
public static bool operator !=(int a, I2<T1, T2> b) {
return a != b.index;
}


public static bool operator <(I2<T1, T2> a, int b) {
return a.index < b;
}
public static bool operator <(int a, I2<T1, T2> b) {
return a < b.index;
}


public static bool operator >(I2<T1, T2> a, int b) {
return a.index > b;
}
public static bool operator >(int a, I2<T1, T2> b) {
return a > b.index;
}


public static bool operator <=(I2<T1, T2> a, int b) {
return a.index <= b;
}
public static bool operator <=(int a, I2<T1, T2> b) {
return a <= b.index;
}


public static bool operator >=(I2<T1, T2> a, int b) {
return a.index >= b;
}
public static bool operator >=(int a, I2<T1, T2> b) {
return a >= b.index;
}
}


public struct I3<T1, T2, T3> {


public int index;
public T1 a;
public T2 b;
public T3 c;


public I3(int index) {
this.index = index;
this.a = default(T1);
this.b = default(T2);
this.c = default(T3);
}
public I3(int index, T1 a) {
this.index = index;
this.a = a;
this.b = default(T2);
this.c = default(T3);
}
public I3(int index, T1 a, T2 b) {
this.index = index;
this.a = a;
this.b = b;
this.c = default(T3);
}
public I3(int index, T1 a, T2 b, T3 c) {
this.index = index;
this.a = a;
this.b = b;
this.c = c;
}


public override bool Equals(object obj) {
if(!(obj is I3<T1, T2, T3>)) return false;
I3<T1, T2, T3> other = (I3<T1, T2, T3>)obj;
return index == other.index && EqualityComparer<T1>.Default.Equals(a, other.a) &&
EqualityComparer<T2>.Default.Equals(b, other.b) &&
EqualityComparer<T3>.Default.Equals(c, other.c);
}


public override int GetHashCode() {
int hash = 17;
hash = hash * 29 + index.GetHashCode();
if(typeof(T1).IsValueType && !object.ReferenceEquals(a, null)) hash = hash * 29 + a.GetHashCode();
if(typeof(T2).IsValueType && !object.ReferenceEquals(b, null)) hash = hash * 29 + b.GetHashCode();
if(typeof(T3).IsValueType && !object.ReferenceEquals(c, null)) hash = hash * 29 + c.GetHashCode();
return hash;
}


public override string ToString() {
return index.ToString();
}


public static implicit operator I3<T1, T2, T3>(int other) {
return new I3<T1, T2, T3>(other);
}


public static implicit operator int(I3<T1, T2, T3> other) {
return other.index;
}


// Unary operators


public static int operator +(I3<T1, T2, T3> a) {
return +a.index;
}


public static int operator -(I3<T1, T2, T3> a) {
return -a.index;
}


public static int operator ~(I3<T1, T2, T3> a) {
return ~a.index;
}


public static I3<T1, T2, T3> operator ++(I3<T1, T2, T3> a) {
a.index++;
return a;
}


public static I3<T1, T2, T3> operator --(I3<T1, T2, T3> a) {
a.index--;
return a;
}


// Binary operators


public static I3<T1, T2, T3> operator +(I3<T1, T2, T3> a, int b) {
a.index += b;
return a;
}
public static I3<T1, T2, T3> operator +(int a, I3<T1, T2, T3> b) {
b.index += a;
return b;
}


public static I3<T1, T2, T3> operator -(I3<T1, T2, T3> a, int b) {
a.index -= b;
return a;
}
public static I3<T1, T2, T3> operator -(int a, I3<T1, T2, T3> b) {
b.index = a - b.index;
return b;
}


public static I3<T1, T2, T3> operator *(I3<T1, T2, T3> a, int b) {
a.index *= b;
return a;
}
public static I3<T1, T2, T3> operator *(int a, I3<T1, T2, T3> b) {
b.index *= a;
return b;
}


public static I3<T1, T2, T3> operator /(I3<T1, T2, T3> a, int b) {
a.index /= b;
return a;
}
public static I3<T1, T2, T3> operator /(int a, I3<T1, T2, T3> b) {
b.index = a / b.index;
return b;
}


public static I3<T1, T2, T3> operator %(I3<T1, T2, T3> a, int b) {
a.index %= b;
return a;
}
public static I3<T1, T2, T3> operator %(int a, I3<T1, T2, T3> b) {
b.index = a % b.index;
return b;
}


public static I3<T1, T2, T3> operator &(I3<T1, T2, T3> a, int b) {
a.index &= b;
return a;
}
public static I3<T1, T2, T3> operator &(int a, I3<T1, T2, T3> b) {
b.index = a & b.index;
return b;
}


public static I3<T1, T2, T3> operator |(I3<T1, T2, T3> a, int b) {
a.index |= b;
return a;
}
public static I3<T1, T2, T3> operator |(int a, I3<T1, T2, T3> b) {
b.index = a | b.index;
return b;
}


public static I3<T1, T2, T3> operator ^(I3<T1, T2, T3> a, int b) {
a.index ^= b;
return a;
}
public static I3<T1, T2, T3> operator ^(int a, I3<T1, T2, T3> b) {
b.index = a ^ b.index;
return b;
}


public static I3<T1, T2, T3> operator <<(I3<T1, T2, T3> a, int b) {
a.index <<= b;
return a;
}


public static I3<T1, T2, T3> operator >>(I3<T1, T2, T3> a, int b) {
a.index >>= b;
return a;
}


// Comparison operators


public static bool operator ==(I3<T1, T2, T3> a, int b) {
return a.index == b;
}
public static bool operator ==(int a, I3<T1, T2, T3> b) {
return a == b.index;
}


public static bool operator !=(I3<T1, T2, T3> a, int b) {
return a.index != b;
}
public static bool operator !=(int a, I3<T1, T2, T3> b) {
return a != b.index;
}


public static bool operator <(I3<T1, T2, T3> a, int b) {
return a.index < b;
}
public static bool operator <(int a, I3<T1, T2, T3> b) {
return a < b.index;
}


public static bool operator >(I3<T1, T2, T3> a, int b) {
return a.index > b;
}
public static bool operator >(int a, I3<T1, T2, T3> b) {
return a > b.index;
}


public static bool operator <=(I3<T1, T2, T3> a, int b) {
return a.index <= b;
}
public static bool operator <=(int a, I3<T1, T2, T3> b) {
return a <= b.index;
}


public static bool operator >=(I3<T1, T2, T3> a, int b) {
return a.index >= b;
}
public static bool operator >=(int a, I3<T1, T2, T3> b) {
return a >= b.index;
}
}

As of C# 7, use a tuple:

for (var foo = (i:new MyClass(0), j:1); foo.j < 3; foo.i++, foo.j++)) { … }

Since C#7.0, you can use deconstruction syntax :

for (var (i, j) = (0, (MyClass) 1); j < 3; i++, j++)
{
Console.WriteLine(i);
}

And I'm assuming you have defined all the overloads for MyClass

internal class MyClass
{
private MyClass(int i)
{
Value = i;
}


private int Value { get; set; }


public static explicit operator MyClass(int i) => new MyClass(i);
public static implicit operator int(MyClass d) => d.Value;


public static MyClass operator ++(MyClass a)
{
a.Value++;
return a;
}
}