Variables in C#


This article has been excerpted from book "Visual C# Programmer's Guide"

A variable is a programming unit that stores transient data during execution of code. Variables are used in programming for calculating and storing values and for reusability. When a class is loaded, all static fields are initialized to their default values; and when an instance of a class is created, all instance fields are initialized to their default values. It is not possible to observe the value of a field before this default initialization has occurred.

Defining Variables

Variables represent storage locations. Every variable has a type that determines what values the variable can store. C# is a type-safe language, so the C# compiler guarantees that values stored in variables are always of the appropriate type. The value of a variable can be changed through assignment or through use of the ++ and -- operators.

A variable must have a value assigned to it before that value can be obtained. A variable that is assigned initially at declaration has a well-defined initial value and is always considered definitely assigned. An initially unassigned variable has no initial value. For an initially unassigned variable to be considered definitely assigned at a certain location, an assignment to the variable must occur in every possible execution path leading to that location.

As you've already learned from this chapter, variables come in two types: value types, which directly contain the actual data in a variable; and reference types, in which the variable stores a reference to the actual data.

With value types, the variables each have their own copy of the data, and it is not possible for operations on one value type variable to affect another.

With reference types, multiple variables can reference the same object; thus, it is possible for operations on one reference type variable to affect the object referenced by another variable.

Static variables, instance variables of class instances, and array elements are automatically initialized to their default values.

For a variable of type value, the default value is the same as the value computed by the value-type's default constructor. For a variable of type reference, the default value is null.

Initialization to default values is typically done by having the memory manager or garbage collector initialize memory to all-bits-zero before allocating it for use. For this reason, it is convenient for an implementation to use all-bits-zero to represent the null reference.

Variable Scope

All variables have a visibility scope that affects how you access them within the code segments. 62 C# Corner Local variables are declared and initialized inside a function block, and you do not specify a modifier. The variable is created on the stack and destroyed immediately after processing exits the block, as indicated in Listing 5.14.

Listing 5.14: Variable Scope Example


void
Myfunc(int param1)
{

// localint1 is created on the stack
// and destroyed immediately after returning

int
localint1 = 5;
}


Public variables are declared inside the class block and allow you to access the variable from within other classes with no limitations.

When a field declaration includes a static modifier, the fields introduced by the declaration are static fields. When no static modifier is present, the fields introduced are instance fields.

A static field identifies exactly one storage location. No matter how many instances of a class are created, only one copy of a static field is created. A static field comes into existence when the type in which it is declared is loaded, and it ceases to exist when that type is unloaded.

Every instance of a class contains a separate copy of all instance fields of the class. An instance field comes into existence when a new instance of its class is created, and it ceases to exist when there are no references to that instance and the destructor of the instance has executed.

So if you want to use the static function Write of the Console class, you can code it using absolute reference (as in the first example that follows) or relative reference (as in the second example):

System.Console.Write("Hello World"); // absolute referring

using
System; // defined once in the very beginning of the code segment
Console
.Write("Hello World"); // relative referring

Static members are like global members in premature development languages. However, they provide a higher level of encapsulation because they reside in classes and namespaces. Static members are classwide members in that they are shared by and accessible to all objects of that class, because static variables and static functions do not need to be initialized. Static functions are initialized only when the class is defined and called either by using a directive or by directly invoking it through its namespace. Have you noticed the syntax of the Console.Write function? Since the Console namespace has a static Write function, we do not need to create a Console object.

To create an instance of the Console class, we would have used the code in Listing 5.15.

Listing 5.15: Console Class Usage

// the following code snippet is completely useless
// static functions save us
// from an unnecessary temporary object creation
Console myConsole = new Console();
myConsole.Write("I am tough enough");


This code is equivalent to the following:


Console
.Write("I am tough enough");

When a field declaration includes a read-only modifier, assignments to the field introduced by the declaration can only occur as part of the declaration or in a constructor in the same class. The constants section has an example to illustrate appropriate usage of the const and static read-only modifiers.

Variable Accessibility

Access modifiers define the level of access that certain code has to particular class members, such as methods and properties. You must apply the desired access modifier to each member; otherwise, the default access type is implied.

You can apply one of the following class member access modifiers:

  • public-access not limited

  • protected internal-access limited to this program or types derived from the containing class

  • protected-access limited to the containing class or types derived from the containing class

  • internal-access limited to this program

  • private-access limited to the containing type

Please refer to the C# Language Specification for more details and recent updates to the C# language (http://msdn.microsoft.com/net/ecma/).

When a member declaration does not include any access modifiers, the context in which the declaration takes place determines the accessibility declared by default. In declaring accessibility, you should keep the following points in mind:

  • Namespaces implicitly have public accessibility declared. No access modifiers are allowed on namespace declarations.

  • Types declared in compilation units or namespaces can have public or internal accessibility declared but default to internal accessibility.

  • Class members can have any of the five kinds of declared accessibility but default to private accessibility. Note that a type declared as a member of a class can have any of the five kinds of declared accessibility, whereas a type declared as a member of a namespace can have only public or internal accessibility declared.

  • Struct members can have public, internal, or private accessibility declared, and they default to private accessibility. Struct members cannot have protected or protected internal accessibility. Note that a type declared as a member of a struct can have public, internal, or private accessibility declared, whereas a type declared as a member of a namespace can have only public or internal accessibility declared.

  • Interface members implicitly have public accessibility declared. No access modifiers are allowed on interface member declarations.

  • Enumeration members implicitly have public accessibility declared. No access modifiers are allowed on enumeration member declarations.

Let's look at an example (Listing 5.16) in order to comprehend some of these concepts, which are the bricks and mortar of object-oriented programming. You will improve your accessibility design skills after following some of these programming practices. You should initially declare most of your object variables (nonstatic) and class variables (static) to have private accessibility. If you need access to the variables from other objects, then provide public methods or properties to expose them.

Listing 5.16: Variable Accessibility Example 1


    public class A1
    {
        public static int X;
        internal static int Y;
        private static int Z;
    }

    public class
A2
    {
        public int X;
        internal int Y;
        private int Z;
    }


Let's examine the accessibility details of each element in Listing 5.16:

  • The accessibility of A1 and A1.X is unlimited.

  • The accessibility of A1.Z is limited to the program context of class A1.

  • The accessibility of A1.Y is limited to the program context of the containing program.

The accessibility of a member is never larger than that of a containing type. For example, even though all X members here and in Listing 5.16 have publicly declared accessibility, all A.X members have accessibility domains that are constrained by a containing type.

Let's look at another, more complex example in Listing 5.17.

Listing 5.17: Variable Accessibility Example 2


internal
class B1
{

   public
static int X;
   internal
static int Y;
   private
static int Z;

public
class C1
{

   public
static int X;
   internal
static int Y;
   private
static int Z;
}


private
class D1
{

   public
static int X;
   internal
static int Y;
   private
static int Z;
}
}


internal
class B2
{

   public
int X;
   internal
int Y;
   private
int Z;

public
class C2
{

   public
int X;
   internal
int Y;
   private
int Z;
}

private
class D2
{

   public
int X;
   internal
int Y;
   private

}
}
}


Let's examine the accessibility details of each element in Listing 5.17:

  • The accessibility of B1, B1.X, B1.Y, B1.C1, B1.C1.X, and B1.C1.Y is limited to the program text of the containing program.

  • The accessibility of B1.Z and B1.D1 is limited to the program text of B1, including the program text of B1.C and B1.D.

  • The accessibility of B1.C1.Z is limited to the program text of B1.C1.

  • The accessibility of B1.D1.X, B1.D1.Y, and B1.D1.Z is limited to the program text of B1.D1.

The A1, B1, C1, and D1 classes all contain static members, so there is no need to declare objects of each relevant class to access their class members. However, note that you cannot access private and protected members this way.

A2, B2, C2, and D2 classes have ordinary members. Thus, the same rules apply for static members as above, but you have to create concrete class objects instead.

Conclusion

See other articles on the website on .NET and C#.


Similar Articles
C# Corner
C# Corner started as an online community for software developers in 1999.