Which is the right approach and why?
string initializeme = string.Empty;
StringBuilder AppendToMe = new StringBuilder();
for(int i=0; i < 10; i++)
{
initializeme = string.Empty; //Is this the right place to initialize?
if(expressionThatEvalsTrue)
initializeme = SomeMethodReturningString();
if(!string.IsNullOrEmpty(initializeme)
AppendToMe.Append(initializeme);
}
or
string initializeme = string.Empty;
StringBuilder AppendToMe = new StringBuilder();
for(int i=0; i < 10; i++)
{
if(expressionThatEvalsTrue)
initializeme = SomeMethodReturningString();
if(!string.IsNullOrEmpty(initializeme)
AppendToMe.Append(initializeme);
initializeme = string.Empty; //Is this the right place to initialize?
}
There are three factors here:
Are you going to capture the variable in the loop using a lambda expression or anonymous method? If so, do you want to capture a single variable, or a separate one per iteration?
Do you need the value after the loop?
Do you need the current value in the next iteration of the loop?
I generally declare variables in the smallest possible scope, and try to initialize them immediately:
for(int i=0; i < 10; i++)
{
string initializeme = expression ? SomeMethodReturningString() : "";
Console.WriteLine(initializeme);
}
I prefer this:
for(int i=0; i<10; i++)
{
var initializeme = expression
? SomeMethodReturningString()
: string.Empty;
Console.WriteLine(initializeme);
}
The reason I prefer this style is that it is self-contained: the variable is only set in one place. Doing it inside the loop reduces the scope of the variable as well, which is also to be preferred. I like the use of the extra variable in this case because using the ternary operator as a parameter, IMO, makes the function call less readable.
Modern compilers convert code to static single assignment form, in which every variable is assigned exactly once, so there's no excuse not to declare new variables whenever convenient, as in each iteration of a loop.
The best way to do it is like this:
for (int i = 0; i < 10; i++)
{
string initializeme = string.Empty;
if (expressionThatEvalsFalse)
initializeme = SomeMethodReturningString();
Console.WriteLine(initializeme);
}
If you use initializeme outside the loop, you'll need to declare it outside the loop.
I would declare the variable inside the loop and assume that the compiler is smart enough not to create slow code from this:
for(int i=0; i<10; i++)
{
string initializeme = string.Empty;
if(expressionThatEvalsFalse)
initializeme = SomeMethodReturningString();
Console.WriteLine(initializeme);
}
Will you be using this variable after the loop completes? The only difference I can see is that if you need to retain the last value of that variable, the first does while the second does not.
in the code snippets i can see no reason to initialize in the first place. The two snipperts will differ after the loop where the initializeme is either potentially initialized to a value (snippet one) or empty string (snippet two. So the premis for the below code is that you only need the initializeme in the loop
StringBuilder AppendToMe = new StringBuilder();
for(int i=0; i < 10; i++)
{
if(expressionThatEvalsTrue)
AppendToMe.Append(SomeMethodReturningString());
}
Neither in my opinion. In both cases I would do the following.
Replace
initializeme = string.Empty; //Is this the right place to initialize?
if(expressionThatEvalsTrue)
initializeme = SomeMethodReturningString();
With
initializeme = expressionThatEvalsTrue ?
SomeMethodReturningString() : string.Empty
This way if your expressionThatEvalsTrue is true you get the string from the method, othewise it will be set to blank.
Related
Is it possible to concatenate inside a C# loop? Below is my sample code:
for (int i = 0; i <= metricCount; i++)
{
if (m.metrictNumber == i)
{
aggrgt.Add(new PlainBrgDataSummaryChartAggrgt
{
scoreWk6 = scoresPerDuration.scoresPerDuration.scoreWk6.metricScore1,
scoreWk5 = scoresPerDuration.scoresPerDuration.scoreWk5.metricScore1,
scoreWk4 = scoresPerDuration.scoresPerDuration.scoreWk4.metricScore1,
scoreWk3 = scoresPerDuration.scoresPerDuration.scoreWk3.metricScore1,
scoreWk2 = scoresPerDuration.scoresPerDuration.scoreWk2.metricScore1,
scoreWk1 = scoresPerDuration.scoresPerDuration.scoreWk1.metricScore1
});
}
}
What I want is to concatenate metricScore1.
Here's what I tried:
scoreWk6 = scoresPerDuration.scoresPerDuration.scoreWk6.metricScore + i,
Is that possible?
You cannot. If you see numbers in variable names, it's probably wrong. Numbering your variables means you should have used a container type. Either an array or a list or anything else that supports going through it with a loop index or foreach.
So the answer is no. No, you cannot. You should change the types holding your data.
Why this code doesn't work?
private void Function(int starts , int ends)
{
int i = starts;
int z = ends;
for(i; i < z; i++)
{
[...]
}
}
It's say: Error 3 Only assignment, call, increment, decrement, and new object expressions can be used as a statement.
What to do to make code work?
What to do to make code work?
How about
for (; i < z; i++)
{
[...]
}
OR why creating an extra variable 'z' when you can do
for (int i = starts; i < ends; i++)
{
[...]
}
Why your code doesn't not work!!
Because syntax of for loop is
for (initializer; condition; increment or decrement)
{
}
You were not initializing i in initializer. You could initialize it (as my second snippet) or remove initializer if you are initializing it in some earlier statement(as my first snippet).
i in and of itself is not a statement, it's an expression. In the same way that you can-not simply write i; somewhere in your code. The initialization-part of a for-loop needs to be initialization; however, if (as is the case here) the loop is already initialized (i is already set), you can just omit it as Nikhil showed. Simply do for(;i<z;i++).
The first part of the for loop takes initialization statements. i is not a statement. If you don't want to initialize anything, you can leave that part empty, as in Nikhil's answer:
for(; i < z; i++)
{
[...]
}
Never use outside-defined variables as indexers in for loops.
private void Function(int starts , int ends)
{
// int i = starts; // don't do it. you're exposing yourself to headaches if/when you lose control of the value of i
int z = ends;
for(int i = starts; i < z; i++) //first statement of for loops initializes the indexer
{
[...]
}
}
I think this is the better answer is it not?
private void Function(int starts , int ends)
{
for(int i = starts; i < ends; i++)
{
[...]
}
}
I get the following error in Visual Studio 2008:
Error 1 A local variable named 'i' cannot be declared in this scope because it would give a different meaning to 'i', which is already used in a 'child' scope to denote something else
This is my code:
for (int i = 0; i < 3; i++)
{
string str = "";
}
int i = 0; // scope error
string str = ""; // no scope error
I understand that str ceases to exist once the loop terminates, but I also thought that the scope of i was confined to the for loop as well.
So i has the same scope as a variable declared just outside of the for loop?
Edit:
Just to be clear, I am using C#. I am debating removing the "C" tag. However, since the correct answer explains the difference between the two, I figure it makes sense to leave both tags.
I had an error in my code comment above:
for (int i = 0; i < 3; i++)
{
string str = "";
}
int i = 0; // scope error
string str = ""; // also scope error,
// because it's equivalent to declaring
// string str =""; before the for loop (see below)
I think you're all confusing C++ and C#.
In C++, it used to be that the scope of a variable declared in a for expression was external to the block that followed it. This was changed, some time ago, so that the scope of a variable declared in a for expression was internal to the block that followed it. C# follows this later approach. But neither has anything to do with this.
What's going on here is that C# doesn't allow one scope to hide a variable with the same name in an outer scope.
So, in C++, this used to be illegal. Now it's legal.
for (int i; ; )
{
}
for (int i; ; )
{
}
And the same thing is legal in C#. There are three scopes, the outer in which 'i' is not defined, and two child scopes each of which declares its own 'i'.
But what you are doing is this:
int i;
for (int i; ; )
{
}
Here, there are two scopes. An outer which declares an 'i', and an inner which also declares an 'i'. This is legal in C++ - the outer 'i' is hidden - but it's illegal in C#, regardless of whether the inner scope is a for loop, a while loop, or whatever.
Try this:
int i;
while (true)
{
int i;
}
It's the same problem. C# does not allow variables with the same name in nested scopes.
The incrementor does not exist after the for loop.
for (int i = 0; i < 10; i++) { }
int b = i; // this complains i doesn't exist
int i = 0; // this complains i would change a child scope version because the for's {} is a child scope of current scope
The reason you can't redeclare i after the for loop is because in the IL it would actually declare it before the for loop, because declarations occur at the top of the scope.
Yea. Syntactically: The new scope is inside the block defined by the curly strings. Functionally: There are cases in which you may want to check the final value of the loop variable (for example, if you break).
Just some background information: The sequence doesn't come into it. There's just the idea of scopes - the method scope and then the for loop's scope. As such 'once the loop terminates' isn't accurate.
You're posting therefore reads the same as this:
int i = 0; // scope error
string str = ""; // no scope error
for (int i = 0; i < 3; i++)
{
string str = "";
}
I find that thinking about it like this makes the answers 'fit' in my mental model better..
Recently, in a book i read this code. Can you define the means of this code and how this code works.
int i = 0;
for (; i != 10; )
{
Console.WriteLine(i);
i++;
}
It loops.
Since you already set i=0 above, they have omitted that section of the for loop. Also, since you are incrementing the variable at the end, they have omitted that as well.
They basically just turned a for loop into a while loop.
It would probably be more elegant as:
int i = 0;
while( i != 10 )
{
Console.WriteLine(i);
i++;
}
The for statement is defined in the C# spec as
for (for-initializer; for-condition; for-iterator) embedded-statement
All three of for-initializer, for-condition, and for-iterator are optional. The code works because those pieces aren't required.
For the curious: if for-condition is omitted, the loop behaves as if there was a for-condition that yielded true. So, it would act as infinite loop, requiring a jump statement to leave (break, goto, throw, or return).
If it's easier to see in normal form, it's almost the equivalent of this:
for (int i = 0; i != 10; i++)
{
Console.WriteLine(i);
}
With the exception that it leaves i available for user after the loop completes, it's not scoped to just the for loop.
You are using "for", like a "while"
It's the same as this loop:
for (int i = 0; i != 10; i++) {
Console.WriteLine(i);
}
Except, the variable i is declared outside the loop, so it's scope is bigger.
In a for loop the first parameter is the initialisation, the second is the condition and the third is the increment (which actually can be just about anything). The book shows how the initalisation and increment are moved to the place in the code where they are actually executed. The loop can also be shown as the equivalent while loop:
int i = 0;
while (i != 10) {
Console.WriteLine(i);
i++;
}
Here the variable i is also declared outside the loop, so the scope is bigger.
This code could be rewritten, (in the context of your code snippet - it is not equivalent as stated.) as:
for (int i = 0; i != 10; i++)
Console.WriteLine(i);
Basically, the initializing expression and the increment expression have been taken out of the for loop expression, which are purely optional.
It is same as:
for (int i = 0; i != 10; i++) {
Console.WriteLine(i);
}
Please don't write code like that.
It's just ugly and defeats the purpose of for-loops.
As int i was declared on top so it was not in the for loop.
this is quite like
for(int i = 0; i!=10; i++)
{
/// do your code
}
for (int i = 0; i < 10; i++)
{
Foo();
}
int i = 10; // error, 'i' already exists
----------------------------------------
for (int i = 0; i < 10; i++)
{
Foo();
}
i = 10; // error, 'i' doesn't exist
By my understanding of scope, the first example should be fine. The fact neither of them are allowed seems even more odd. Surely 'i' is either in scope or not.
Is there something non-obvious about scope I don't understand which means the compiler genuinely can't resolve this? Or is just a case of nanny-state compilerism?
By my understanding of scope, the first example should be fine.
Your understanding of scope is fine. This is not a scoping error. It is an inconsistent use of simple name error.
int i = 10; // error, 'i' already exists
That is not the error that is reported. The error that is reported is "a local variable named i cannot be declared in this scope because it would give a different meaning to i which is already used in a child scope to denote something else"
The error message is telling you what the error is; read the error message again. It nowhere says that there is a conflict between the declarations; it says that the error is because that changes the meaning of the simple name. The error is not the redeclaration; it is perfectly legal to have two things in two different scopes that have the same name, even if those scopes nest. What is not legal is to have one simple name mean two different things in nested local variable declarations spaces.
You would get the error "a local variable named i is already defined in this scope" if instead you did something like
int i = 10;
int i = 10;
Surely 'i' is either in scope or not.
Sure -- but so what? Whether a given i is in scope or not is irrelevant. For example:
class C
{
int i;
void M()
{
string i;
Perfectly legal. The outer i is in scope throughout M. There is no problem at all with declaring a local i that shadows the outer scope. What would be a problem is if you said
class C
{
int i;
void M()
{
int x = i;
foreach(char i in ...
Because now you've used i to mean two different things in two nested local variable declaration spaces -- a loop variable and a field. That's confusing and error-prone, so we make it illegal.
Is there something non-obvious about scope I don't understand which means the compiler genuinely can't resolve this?
I don't understand the question. Obviously the compiler is able to completely analyze the program; if the compiler could not resolve the meaning of each usage of i then how could it report the error message? The compiler is completely able to determine that you've used 'i' to mean two different things in the same local variable declaration space, and reports the error accordingly.
It is because the declaration space defines i at the method level. The variable i is out of scope at the end of the loop, but you still can't redeclare i, because i was already defined in that method.
Scope vs Declaration Space:
http://csharpfeeds.com/post/11730/Whats_The_Difference_Part_Two_Scope_vs_Declaration_Space_vs_Lifetime.aspx
You'll want to take a look at Eric Lippert's answer (who by default is always right concerning questions like these).
http://blogs.msdn.com/ericlippert/archive/2009/08/03/what-s-the-difference-part-two-scope-vs-declaration-space-vs-lifetime.aspx
Here is a comment from eric on the above mentioned post that I think talks about why they did what they did:
Look at it this way. It should always
be legal to move the declaration of a
variable UP in the source code so long
as you keep it in the same block,
right? If we did it the way you
suggest, then that would sometimes be
legal and sometimes be illegal! But
the thing we really want to avoid is
what happens in C++ -- in C++,
sometimes moving a variable
declaration up actually changes the
bindings of other simple names!
From the C# spec on local variable declarations:
The scope of a local variable declared
in a local-variable-declaration is the
block in which the declaration occurs.
Now, of course, you can't use i before it is declared, but the i declaration's scope is the entire block that contains it:
{
// scope starts here
for (int i = 0; i < 10; i++)
{
Foo();
}
int i = 10;
}
The for i variable is in a child scope, hence the collision of variable names.
If we rearrange the position of the declaration, the collision becomes clearer:
{
int i = 10;
// collision with i
for (int i = 0; i < 10; i++)
{
Foo();
}
}
Yea, I second the "nanny-state compilerism" comment. What's interesting is that this is ok.
for (int i = 0; i < 10; i++)
{
}
for (int i = 0; i < 10; i++)
{
}
and this is ok
for (int i = 0; i < 10; i++)
{
}
for (int j = 0; j < 10; j++)
{
var i = 12;
}
but this is not
for (int i = 0; i < 10; i++)
{
var x = 2;
}
var x = 5;
even though you can do this
for (int i = 0; i < 10; i++)
{
var k = 12;
}
for (int i = 0; i < 10; i++)
{
var k = 13;
}
It's all a little inconsistent.
EDIT
Based on the comment exchange with Eric below, I thought it might be helpful to show how I try to handle loops. I try to compose loops into their own method whenever possible. I do this because it promotes readability.
BEFORE
/*
* doing two different things with the same name is unclear
*/
for (var index = 0; index < people.Count; index++)
{
people[index].Email = null;
}
var index = GetIndexForSomethingElse();
AFTER
/*
* Now there is only one meaning for index in this scope
*/
ClearEmailAddressesFor(people); // the method name works like a comment now
var index = GetIndexForSomethingElse();
/*
* Now index has a single meaning in the scope of this method.
*/
private void ClearEmailAddressesFor(IList<Person> people)
{
for (var index = 0; index < people.Count; index++)
{
people[index].Email = null;
}
}
In the first example, the declaration of i outside of the loop makes i a local variable of the function. As a result, it is an error to have another variable name i declared within any block of that function.
The second, i is in scope only during the loop. Outside of the loop, i can no longer be accessed.
So you have seen the errors, but there is nothing wrong with doing this
for (int i = 0; i < 10; i++)
{
// do something
}
foreach (Foo foo in foos)
{
int i = 42;
// do something
}
Because the scope of i is limited within each block.
Or is just a case of nanny-state
compilerism?
Exactly that. There is no sense in "reusing" variable names in the same method. It's just a source of errors and nothing more.
Me thinks that the compiler means to say that i has been declared at the method level & scoped to within the for loop.
So, in case 1 - you get an error that the variable already exists, which it does
& in case 2 - since the variable is scoped only within the for loop, it cannot be accessed outside that loop
To avoid this, you could:
var i = 0;
for(i = 0, i < 10, i++){
}
i = 10;
but I can't think of a case where you would want to do this.
HTH
you need to do
int i ;
for ( i = 0; i < 10; i++)
{
}
i = 10;
class Test
{
int i;
static int si=9;
public Test()
{
i = 199;
}
static void main()
{
for (int i = 0; i < 10; i++)
{
var x = 2;
}
{ var x = 3; }
{ // remove outer "{ }" will generate compile error
int si = 3; int i = 0;
Console.WriteLine(si);
Console.WriteLine(Test.si);
Console.WriteLine(i);
Console.WriteLine((new Test()).i);
}
}
}