Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# variable scoping is not consistent

Tags:

scope

c#

C# is quite nitpicky when it comes to variable scoping. How is it possible that it accepts the following code?

class Program
{
    int x = 0;

    void foo()
    {
        int x = 0;
        x = 1;
        
        Console.WriteLine(x);
    }
}

If you ask me, that's an obvious naming conflict. Still the compiler (Visual Studio 2010) accepts it. Why?

like image 470
l33t Avatar asked Feb 17 '12 15:02

l33t


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is C in C language?

What is C? C is a general-purpose programming language created by Dennis Ritchie at the Bell Laboratories in 1972. It is a very popular language, despite being old. C is strongly associated with UNIX, as it was developed to write the UNIX operating system.

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.

Is C language easy?

Compared to other languages—like Java, PHP, or C#—C is a relatively simple language to learn for anyone just starting to learn computer programming because of its limited number of keywords.


5 Answers

The rules for C# name hiding are quite complex. The language allows the case you mention, but disallows many similar cases. See

Simple names are not so simple, part one

for some information on this complicated subject.

To address your specific question: the compiler certainly could detect that conflict. In fact, it does detect that conflict:

class P
{
    int x;
    void M()
    {
        x = 123; // The author intends "this.x = 123;"
        int x = 0;
    }
}

The equivalent C++ program would be legal C++, because in C++ a local variable comes into scope at the point of its declaration. In C#, a local variable is in scope throughout its entire block, and using it before its declaration is illegal. If you try compiling this program you get:

error CS0844: Cannot use local variable 'x' before it is declared.
The declaration of the local variable hides the field 'P.x'.

See: the local declaration hides the field. The compiler knows it. So why in your case is it not an error to hide a field?

Let's suppose for the sake of argument that it should be an error. Should this also be an error?

class B
{
    protected int x;
}

class D : B
{
    void M()
    {
        int x;
    }
}

The field x is a member of D via inheritance from B. So this should also be an error, right?

Now suppose you have this program produced by Foo Corporation:

class B
{
}

and this program produced by Bar Corporation:

class D : B
{
    void M()
    {
        int x;
    }
}

That compiles. Now suppose Foo Corp updates their base class and ships a new version out to you:

class B
{
    protected int x;
}

You're telling me that every derived class that contains a local variable named x should now fail to compile?

That would be horrid. We have to allow local variables to shadow members.

And if we're going to allow locals to shadow members of base classes, it would seem awfully strange to not allow locals to shadow members of classes.

like image 74
Eric Lippert Avatar answered Nov 02 '22 06:11

Eric Lippert


This is not a naming conflict: in C#, local variables take precedence over instance variables with the same name, because their scope is narrower.

When the compiler matches a name reference to a name declaration, it uses the matching declaration with the narrowest scope

See documentation on Reference Matching for detailed information on the subject.

like image 38
Sergey Kalinichenko Avatar answered Nov 02 '22 06:11

Sergey Kalinichenko


That’s normal.

In constructors I often use the same.

public Person(string name) {
  this.name = name;
}

Else it would be not possible to declare method parameters which are named like member variables.

like image 3
BlueM Avatar answered Nov 02 '22 07:11

BlueM


There is no naming conflict. The compiler always takes the nearest/least scope variable.

In this case, that’s the x variable you declare in foo. Every variable can be accessed in a specific way, so there isn't any naming conflict.

If you want to access the outer x you can use this.x.

like image 1
poitroae Avatar answered Nov 02 '22 07:11

poitroae


The C# 4.0 specification says this about scope hiding through nesting:

3.7.1.1 Hiding through nesting

Name hiding through nesting can occur as a result of nesting namespaces or types within namespaces, as a result of nesting types within classes or structs, and as a result of parameter and local variable declarations. In the example

class A {
    int i = 0;

    void F() {
        int i = 1;
    }

    void G() {
        i = 1;
    }
}

within the F method, the instance variable i is hidden by the local variable i, but within the G method, i still refers to the instance variable.

When a name in an inner scope hides a name in an outer scope, it hides all overloaded occurrences of that name.

In the example

class Outer {
    static void F(int i) {}
    static void F(string s) {}

    class Inner
    {
        void G() {
            F(1);           // Invokes Outer.Inner.F
            F("Hello");     // Error
        }

        static void F(long l) {}
    }
}

the call F(1) invokes the F declared in Inner because all outer occurrences of F are hidden by the inner declaration. For the same reason, the call F("Hello") results in a compile-time error.

like image 1
Tesserex Avatar answered Nov 02 '22 06:11

Tesserex