Applying Scope
You have seen in some of the examples that you can create a variable inside a method. The variable comes into existence at the point where a statement defines it, and subsequent statements in the same method can then use the variable. In other words, a variable can be used only after it has been created. After the method has finished, the variable disappears completely.
If a variable can be used at a particular location in a program, the variable is said to be in scope at that location. To put it another way, the scope of a variable is simply the region of the program in which that variable is usable. Scope applies to methods as well as variables. The scope of an identifier (of a variable or method) is linked to the location of the declaration that introduces the identifier into the program, as you'll now learn.
Defining Local Scope
The opening and closing curly braces that form the body of a method define a scope. Any variables you declare inside the body of a method are scoped to that method; they disappear when the method ends and can be accessed only by code running within that method. These variables are called local variables because they are local to the method in which they are declared; they are not in scope in any other method. This arrangement means that you cannot use local variables to share information between methods. Consider this example:
class Example { void firstMethod() { int myVar; ... } void anotherMethod() { myVar = 42; // error – variable not in scope ... } }
This code would fail to compile because anotherMethod is trying to use the variable myVar which is not in scope. The variable myVar is available only to statements in firstMethod.
Defining Class Scope
The opening and closing curly braces that form the body of a class also create a scope. Any variables you declare inside the body of a class (but not inside a method) are scoped to that class. The proper C# name for the variables defined by a class is fields. Unlike local variables, you can use fields to share information between methods. Here is an example:
class Example { void firstMethod() { myField = 42; // ok ... } void anotherMethod() { myField = 42; // ok ... } int myField = 0; }
The variable myField is defined within the class, but outside of the methods firstMethod and anotherMethod. Therefore, myField has class scope and is available for use by all methods in the class.
There is one other point to notice about this example. In a method, you must declare a variable before you can use it. Fields are a little different. A method can use a field before the statement that defines the field—the compiler sorts out the details for you!
Overloading Methods
If two identifiers have the same name and are declared in the same scope, they are said to be overloaded. Often an overloaded identifier is a bug that gets trapped as a compile-time error. For example, if you declare two local variables with the same name in the same method, you get a compile-time error. Similarly, if you declare two fields with the same name in the same class or two identical methods in the same class, you also get a compile-time error. This fact might seem hardly worth mentioning, given that everything so far has turned out to be a compile-time error. However, there is a way that you can overload an identifier, and that way is both useful and important.
Consider the WriteLine method of the Console class. You have already used this method for outputting a string to the screen. However, when you type WriteLine in the Code and Text Editor window when writing C# code, you will notice that IntelliSense gives you 19 different options! Each version of the WriteLine method takes a different set of parameters; one implementation takes no parameters and simply outputs a blank line, another implementation takes a bool parameter and outputs a string representation of its value (true or false), yet another implementation takes a decimal parameter and outputs it as a string, and so on. At compile time, the compiler looks at the types of the arguments you are passing in and then calls the version of the method that has a matching set of parameters. Here is an example:
static void Main() { Console.WriteLine("The answer is "); Console.WriteLine(42); }
Overloading is primarily useful when you need to perform the same operation on different data types. You can overload a method when the different implementations have different sets of parameters; that is, when they have the same name but a different number of parameters, or when the types of the parameters differ. This capability is allowed so that, when you call a method, you can supply a comma-separated list of arguments, and the number and type of the arguments is used by the compiler to select one of the overloaded methods. However, note that although you can overload the parameters of a method, you can't overload the return type of a method. In other words, you can't declare two methods with the same name that differ only in their return type. (The compiler is clever, but not that clever.)