18

Like for instance:

if ( this.IsValid )
{
    Matrix matrix = new Matrix();
}

Matrix matrix = new Matrix();

The compiler warns me saying:

"A local variable named 'matrix' cannot be declared in this scope because it would give a different meaning to 'matrix', which is already used in a 'child' scope to denote something else.

Aren't these variables in different scopes, so I wouldn't be able to access the first matrix from outside the if statement anyway?

Joan Venge
  • 315,713
  • 212
  • 479
  • 689
  • Even if the question is eligible, this prevents you from doing careless mistakes. – Tim Schmelter Jan 10 '11 at 18:16
  • over here [http://bytes.com/topic/c-sharp/answers/659303-already-used-child-scope-denote-something-else] you can find an explanation from Jon Skeet. And here http://stackoverflow.com/questions/296755/child-scope-cs0136 also by Jon Skeet. – Vijay Sirigiri Jan 10 '11 at 18:19
  • @Tim: Yeah but then one needs to make unnecessary new variable names, every time, something similar must be used, just to satisfy the compiler. IMO if the code is clear, this wouldn't cause any issue. I can see what each matrix meant without doing: matrixTemporary, matrixReal, etc. – Joan Venge Jan 10 '11 at 18:20
  • @Joan Venge: If the variable would be unnecessary you would not use two separate variables but the same. – Tim Schmelter Jan 10 '11 at 18:22
  • @Tim: I didn't say the variables would be unnecessary but the variable names being different. – Joan Venge Jan 10 '11 at 18:24
  • Standard C# team wisdom, it's a fertile source of bugs. No other reason. – Hans Passant Jan 10 '11 at 19:35
  • Note that this is a duplicate of http://stackoverflow.com/questions/2693138/variable-scope-in-statement-blocks/2693418#2693418 – Eric Lippert Jan 10 '11 at 20:49

5 Answers5

17

UPDATE: The answer below from 2011 is correct for earlier versions of C#; in more recent versions, the rule described the answer has been removed from C#. The design team determined that the rule caused more confusion amongst developers leading to questions like this one than the buggy programs prevented would warrant, even after I greatly improved the error messages to more clearly diagnose the problem.


The answers given so far are very confusing. The correct analysis of the problem starts by reading the error message. The error message is telling you what is actually wrong:

"A local variable named 'matrix' cannot be declared in this scope because it would give a different meaning to 'matrix', which is already used in a 'child' scope to denote something else.

Read that carefully. It is telling you precisely which rule of C# is being violated, namely that you are not allowed to use the same name to refer to two different things in the same scope. (Actually, the error message is slightly wrong; it should say "local variable declaration space" where it says "scope", but that is pretty wordy.)

This rule is documented in the C# 4.0 specification, section 7.6.2.1: Simple names, Invariant meaning in blocks.

(It is also illegal to have two local variables of the same name in overlapping declaration spaces. The compiler could be reporting that error as well, but it reports the more general error in this case.)

Aren't these variables in different scopes, so I wouldn't be able to access the first matrix from outside the if statement anyway?

Yes. That statement is true but irrelevant. The error here is that the same simple name has been used to refer to two different things in the same local variable declaration space.

Consider this scenario:

class C 
{
    int x;
    void M()
    {
        x = 10; // means "this.x"
        for(whatever)
        {
            int x = whatever;
        }
    }
 }

Same deal. The error here is that the simple name "x" was used in the outer declaration space to refer to this.x, and was used in the inner declaration space to mean "local variable". Using the same simple name to refer to two different things in the same declaration space -- remember, the inner declaration space is a part of the outer one -- is both confusing and dangerous, and is therefore illegal.

It is confusing for obvious reasons; one has a reasonable expectation that a name will mean the same thing everywhere throughout the declaration space in which it is first used. It is dangerous because small code edits are prone to changing the meaning:

class C 
{
    int x;
    void M()
    {
        int x;
        x = 10; // no longer means "this.x"
        for(whatever)
        {
            x = whatever;
        }
    }
 }

If the declaration spaces in which the simple names are first used are not overlapping then it is legal for the simple names to refer to different things:

class C 
{
    int x;
    void M()
    {
        {
            x = 10; // means "this.x"
        }
        for(whatever)
        {
            int x = whatever; // Legal; now the 
        }
    }
 }

For more information, and an amusing story about fried food, see

http://blogs.msdn.com/b/ericlippert/archive/tags/simple+names/

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • @Eric: Thanks Eric. I don't understand how in your last example, second int x is valid. Is it because you encapsulated x = 10 with {}s? – Joan Venge Jan 10 '11 at 20:47
  • 1
    @Joan: Correct. With the x=10 in a block of its own, now the local variable declaration space which encloses it does not *overlap* with the other local variable declaration space that contains the other usage of x. – Eric Lippert Jan 10 '11 at 20:50
  • @Eric: Thanks Eric. I got it now. I have seen some languages when written like in your first example, the second x would hide the first, so you think this is a bad design? Lastly you said in your comment "... which encloses it does not overlap ...", do you know a place I can learn about the overlapping concept in C#? I didn't hear it before. – Joan Venge Jan 10 '11 at 20:56
  • 1
    @Joan: see the section of the spec I referred to. The relevant clause is "within the local variable declaration space immediately enclosing that occurrence" -- the *immediately* means that you don't look at more-outer blocks, and the *within* means you do look at more-inner blocks. – Eric Lippert Jan 10 '11 at 22:04
  • 2
    @Joan: To answer your question: I once spent almost an entire day tracking down a bug which turned out to be me accidentally using "i" to mean two different things in a very long, complicated C++ routine. It was completely not obvious that as I stepped through it in the debugger, that suddenly "i" in the debugger was referring to a *different variable* (sometimes with the same value!) as I stepped around. It was a boneheaded stupid bug to introduce, but a very easy bug to introduce, and the C++ compiler was no help to me whatsoever. The rules in C# are much more likely to promote good code. – Eric Lippert Jan 10 '11 at 22:06
  • @Eric: Thanks Eric, for sure your judgement helps millions of programmers out there. It's good to know the reasoning, so I am more informed about these. – Joan Venge Jan 10 '11 at 22:21
  • @Eric: must say that being topped by one of my idols was very very cool! :) – Bruno Brant Jan 11 '11 at 14:20
7

It is my belief that this is done in order to avoid obscure mistakes or code that's hard to read.

Using the same name of variable between a method scope and a child scope can lead to code that's very hard to read, since the variable type and, worse, meaning, can change and the only hint to the reader will be type declaration keyword before the variable.

However, I can also tell you that the IL generated for methods by the C# compiler will stick all variable declarations at the top, so maybe this decision driver was to simplify the variable parsing tree for the compiler.

In fact, you can find this at MSDN:

The scope of a name is the region of program text within which it is possible to refer to the entity declared by the name without qualification of the name. Scopes can be nested, and an inner scope may redeclare the meaning of a name from an outer scope. (This does not, however, remove the restriction imposed by Section 3.3 that within a nested block it is not possible to declare a local variable with the same name as a local variable in an enclosing block.) The name from the outer scope is then said to be hidden in the region of program text covered by the inner scope, and access to the outer name is only possible by qualifying the name.

Emphasis added.

And, from Section 3.3:

Each block or switch-block creates a different declaration space for local variables and constants. Names are introduced into this declaration space through local-variable-declarations and local-constant-declarations. If a block is the body of an instance constructor, method, or operator declaration, or a get or set accessor for an indexer declaration, the parameters declared in such a declaration are members of the block's local variable declaration space. The local variable declaration space of a block includes any nested blocks. Thus, within a nested block it is not possible to declare a local variable with the same name as a local variable in an enclosing block.

Emphasis added.

So, the thing is that while the scopes are different, the variable space is the same.

Community
  • 1
  • 1
Bruno Brant
  • 8,226
  • 7
  • 45
  • 90
6

You can always do this...

void YourMethod() 
{
    if ( this.IsValid ) 
    {    
        Matrix matrix = new Matrix();
    }

    {
        Matrix matrix = new Matrix(); 
    }
}

...Each set of braces {} allows you to nest another level of scope. The issue you are having is the fact that nested scopes include the scope of their parents. If you declare a siblng scope it will be able to resuse variables within the same parent. But as others have pointed out this may become confusing later.

Joan Venge
  • 315,713
  • 212
  • 479
  • 689
Matthew Whited
  • 22,160
  • 4
  • 52
  • 69
  • Thanks but I can't. The second matrix and statements following it must always execute (whether this.IsValid or not) – Joan Venge Jan 10 '11 at 18:23
  • 1
    To the downvoter. Why is this wrong? You might try it. You will find that you are the one that is wrong. The second set of braces declares a secondary scope that is always executed. It's not one or the other. – Matthew Whited Jan 10 '11 at 23:47
  • @Joan, it will execute both. – Matthew Whited Jan 10 '11 at 23:48
  • Thanks Matthew, I thought there was an else there, now makes sense. Will upvote. – Joan Venge Jan 11 '11 at 18:14
  • This is a rare yet beautiful aspect of C# in my opinion :D I love how I can create my custom sub-scopes. I came to this question to find out if this is a common practice among C# coders. – Efe Zaladin Sep 08 '21 at 19:07
  • I wouldn't say this is common. Many people would probably frown upon this as it's not obvious what is going on... but it will work. debatably better way would be to give the variables better/unique names or just define a single variable and reuse it as needed in the function. – Matthew Whited Sep 09 '21 at 21:39
2

Imagine a human being trying to read this code.

From the point of view of another developer trying to read your code, can you see how confusing it would be to have two different variables with the same name? Even if they are representing the same thing, it's just too hard to deal with two things with the same name.

DOK
  • 32,337
  • 7
  • 60
  • 92
  • I don't think it's confusing, because it clearly shows that the matrix inside the if is meant to be temporary, so could be created to do a certain calculation, etc. – Joan Venge Jan 10 '11 at 18:22
1
Matrix matrix = new Matrix();

if ( this.IsValid ) 
{
    Matrix matrix = new Matrix(); 
} 

Imagine it instead written like that, it is a bit more obvious I would think why this is not allowed since the second instance should obviously be considered a clash. Not being able to access the outer variables within child scopes would be bad.

From MSDN "A: This is correct behavior, and is covered in section 3.7 of the language spec. It says, “The scope of a local variable declared in a local-variable-declaration (8.5.1) is the block in the which the declaration occurs”." ... "This behavior is inteded to make incorrect re-use of variable names (such as in a cut and paste) less likely." (http://blogs.msdn.com/b/csharpfaq/archive/2004/05/18/why-can-t-i-use-the-same-variable-as-an-inner-loop-does.aspx)

ferr
  • 1,137
  • 3
  • 12
  • 28
  • That seems more counter-intuitive IMO, but even in that case, I would expect the compiler to hide the outer scope variable and create a new one. But in my example, one would think the first matrix is already in another scope, so wouldn't be able to affect the second matrix variable, like it's accessibility doesn't reach beyond the if statement it's contained within. – Joan Venge Jan 10 '11 at 18:30