Variables and Data Types

Explore how to store and manipulate data using variables, primitive types, and arrays in C#.

Applications need to store and process information. Variables provide a way to do this.

Variables

Variables are used to store data within a program. They represent a named area of computer memory that holds a value of a particular type. The type determines what kind of information a variable can store.

The basic syntax for declaring a variable is as follows:

variable_type variable_name;

Valid variable names must meet the following requirements:

  • The name can contain an underscore and any number and letter, while the first character in the name must be a letter or underscore.

  • The name can’t contain punctuation marks or spaces.

  • We can’t use a C# keyword as a variable name (unless we prefix it with @, though this is rare).

Although we can give any name to our C# variables, it’s highly recommended to use descriptive names. For instance, if we want to store a person’s first name, we can declare a variable as follows:

string name;

In the example above, we create a variable called name that has the string type. The string type represents text.

Case sensitivity

C# is case-sensitive. This means that depending on the character case, the same names can represent different classes, methods, and variables.

For instance, in the following code, we have a string variable named name. We can create another string variable just below and name it Name.

string name;
string Name;

Note: While the code below is valid, naming two variables name and Name is confusing and considered bad practice. We use it here strictly to demonstrate how the compiler treats case.

We’ve declared a variable, but it doesn’t contain any information yet. To assign a value to a variable, we use the assignment (=) operator.

name = "John";

We can combine declaration and assignment in one line. This operation is called initialization.

string lastName = "Doe";

Assigning a value to a variable that doesn’t exist

We get an error during compilation if we try to assign a value to a variable that wasn’t declared.

C# 14.0
age = 24;
Console.WriteLine(age);
  • Line 1: We attempt to assign the integer 24 to age. However, age has not been declared with a type (like int) yet. This causes a generic compile-time error: The name 'age' does not exist in the current context.

  • Line 2: This line would print the value, but the program will not run due to the error on line 1.

We also get an error if we try to assign a value of the wrong type:

string notANumber;
notANumber = 20; // error
int aNumber = 20; // correct

Creating and using variables

Use the exercise below to practice variable creation. Initialize two variables needed for correct compilation and meaningful output.

C# 14.0
// TODO: Create suitable variables below to get meaningful output
Console.WriteLine($"Hello, my name is {name} and I am {age} years old");

A variable can have a different value at different stages of the program. That is, we can reassign a variable to have another value.

int length = 29; // Create a variable
length = 56; // Assigned a different value

Types

Like many other programming languages, C# has its own data type system. The data type defines the internal representation of the data, the set of values that an object can take, and the set of actions that can be performed on the object.

Primitive types

Primitive types are the basic building blocks of C#. The following table lists the most commonly used types:

Data type

Description

bool

Can either be true or false.

int

A whole number (4, 42, 70) represented by four bytes of memory.

byte

This is also a whole number, but because it's represented by only one byte of memory, it can only hold values from zero to 255. Unlike int, it can’t hold negative values.

long

This is a whole number represented by eight bytes of memory. To create a literal of the

long type, add an appropriate suffix at the end of the number: 30L.

float

A single-precision floating-point number that can have values ranging from -3.43.4×1038-3.4\times10^{38} to 3.4×10383.4\times10^{38}. A float uses four bytes of memory. Literal: 17.5f.

double

This is a double-precision floating-point number that uses eight bytes of memory. All real number literals (23.27, 78.9) are of the double type by default.

decimal

A 16-byte decimal number optimized for financial calculations. Literal: 1009.621m.

char

This represents a single Unicode character and uses two bytes of memory. A char literal comes between (single quotes) '': ('l', '2', 'g').

string

This stores a series of Unicode characters. The amount of memory used depends on the length of the string. String literals are enclosed in double quotes ("I am a string literal.").

object

This is the base type in .NET. All types are derived from this object type.

Type inference

In modern C#, we do not always have to explicitly state the data type when initializing a variable. Instead, we can use the var keyword to instruct the compiler to infer the data type from the assigned value.

var age = 25;
var city = "Seattle";
Using type inference with the var keyword
  • Line 1: The compiler sees the integer 25 and automatically assigns the int type to the age variable.

  • Line 2: The compiler sees the text "Seattle" and infers the string type for the city variable.

Using var is common practice as it reduces visual clutter, but it requires the variable to be initialized on the same line so the compiler has enough information to determine the type.

The var keyword instructs the compiler to infer the type from the value assigned on the right side of the operator.

var age; // Not allowed

We cannot use the var keyword without assigning a value

Arrays

An array is a data structure that holds a fixed number of elements of the same type. An array declaration is similar to a variable declaration, except that we put square brackets ([]) after the type:

string[] students;

After defining an array variable, we can create an array object and assign it to the variable:

string[] students = new string[10];

In the code above, we declare a students array that holds values of the string type. The new keyword allocates the amount of memory required for this array and fills the array with the type’s default value.

Visual representation of an array
Visual representation of an array

In this case, we declare that we need enough memory to hold 10 string objects and initialize the array with default values for the string type. The default value for string is null, which signifies the absence of a value.

Array initialization

Some other ways to initialize an array are as follows:

C# 14.0
string[] students1 = new string[3] { "Aidil", "John", "Patrick" };
string[] students2 = new string[] { "Aidil", "John", "Patrick" };
var students3 = new[] { "Aidil", "John", "Patrick" };
string[] students4 = { "Aidil", "John", "Patrick" };
Console.WriteLine($"Array 1 length is {students1.Length}");
Console.WriteLine($"Array 2 length is {students2.Length}");
Console.WriteLine($"Array 3 length is {students3.Length}");
Console.WriteLine($"Array 4 length is {students4.Length}");
  • Line 1: Explicitly specifies the size (3) and the type (string) along with the elements.

  • Line 3: Specifies the type (string) but lets the compiler infer the size based on the number of elements provided (3 elements).

  • Line 5: Uses the var keyword alongside an implicitly typed array new[]. The compiler infers that this is an array of strings with a length of 3.

  • Line 7: The most concise syntax. It allows us to omit new and the type completely when initializing the variable at the point of declaration.

  • Lines 9–12: We print the Length property of each array to prove they are identical in structure.

Run the code above to confirm that, in all cases, an array with a length of 3 is created.

Accessing elements

To refer to an element inside an array, we use the element’s index, which is its position inside the array. Keep in mind that indexing starts at 0. In other words, to get the first element of an array, we do the following:

C# 14.0
string[] students1 = new string[3] { "Aidil", "John", "Patrick" };
Console.WriteLine(students1[0]);

If we have an array of length 3, like in the previous example, we can’t access an element with index 5, for instance. To check what happens if we try to go out of an array’s boundaries, run the following code.

C# 14.0
string[] students1 = new string[3] { "Aidil", "John", "Patrick" };
Console.WriteLine(students1[3]);
  • Line 3: We try to access the fourth element (index 3). Since the array only has 3 items, index 3 is out of bounds. This throws a runtime exception: System.IndexOutOfRangeException.

Common type system

The .NET family of languages shares a common type system (CTS). The common type system (CTS) enables language interoperability in .NET. When we create a System.String object in C#, it’s essentially the same System.String object in Visual Basic and F#, although different aliases could exist in different languages.

For example, string in C# is an alias for the System.String type, while int is an alias for System.Int32. Therefore, when we do something like this:

int myAge = 25;
System.Int32 yourAge = 30;

We essentially create two variables of the same type. Run the code below to confirm.

C# 14.0
System.Int32 firstNumber = 56;
int secondNumber = 54;
Console.WriteLine($"firstNumber is of type {firstNumber.GetType()}");
Console.WriteLine($"secondNumber is of type {secondNumber.GetType()}");
  • Line 1: We declare firstNumber using the full .NET type name System.Int32.

  • Line 2: We declare secondNumber using the C# keyword int.

  • Lines 4–5: We use the .GetType() method to inspect the type at runtime. Both will output System.Int32, proving they are aliases for the exact same underlying type.

In Visual Basic, Integer serves as an alias for System.Int32, while strings can be created using the String alias.