C# Data Types, Variables and Casting
- Introduction to Programming
- What is C#?
- Your First Console Application in C#
- Introducing Methods and the Main() Function in C#
- Introducing C# Classes and Structs
- C# Data Types, Variables and Casting
- C# Program Flow Control and Entry Points
- Passing Parameters to Methods and Return Values in C#
- C# Access Modifiers and Scope
- C# Interfaces and Classes
- Using Namespaces in C#
- C# Conditional Statements
- Looping and Iteration in C#
- Using Arrays and Lists in C#
- C# Constants and Read-Only Variables
- Error and Exception Handling in C#
- Using Recursion in C#
- C# Operator List
- Class Inheritance in C#
- C# Class and Method Attributes Explained
- C# Class Constructors and Destructors
- C# Generics Variables
- XML Serialization and Deserialization
- C# String Formatting Examples
C#, unlike some languages such as PHP, is a type-safe programming language which means that each variable must be declared, and once declared a variable of a given data type cannot be changed. This allows the compiler to check your code, before it is compiled, for some errors that will result in a program crash.
In PHP you can assign a variable an integer value, then at a later date assign a string to the same variable.
- $var = 1234;
- $var = "some string";
The .Net Framework does not allow this kind of behaviour and the compiler will throw an exception. The reason is that it is good programming practice to use a variable for one given "task" and to create a new variable for a different task. It also enforces code maintainability and debuggability by guaranteeing that a variable's data type is constant.
A list of the basic data types is available, and shown below are examples of the most common data types being instantiated. Declaration is by specifying the data type, followed by the variable name. You can optionally include an access modifier, which in the example below is the default private.
- int myNumber;
- string myString;
- decimal accountBalance;
You can also give a variable an initial value by adding an assignment to the declaration as shown in the example below.
- int myNumber = 1;
- string myString = "Some test string";
- decimal accountBalance = 1.0M;
Variable names, like method names, have rules that govern how they should be named. Variables and methods must:
- Start with a letter or underscore
- Contain only letters, numbers and the underscore character
- Not be reserved words or keywords
As well as those rules, there are some recommendations as well, for good practice and readability. Some of these are listed below:
- Variable names should always be meaningful, but not too verbose.
- You should not use single letter identifiers except for loop control (where you should use i,j and k) or for mathematical equations (e.g. x = x * y^2).
- You should always use the data type most appropriate for its use. For example, a variable to hold day of month would never rise above 31, so a byte would be a better data type over an int or decimal.
Assignment and Initialisation
Variables declared as above will be given default values as described in the cheat sheet, but you may wish to initialise your variables to something more meaningful or appropriate. You can do this one of two ways.
When you declare the variable:
- int myNumber = 12345;
- string myString = "Hello World";
- decimal accountBalance = 34.87323;
Or you can perform a regular variable assignment after the variable has been declared:
- int myNumber;
- string myString;
- decimal accountBalance;
- myNumber = 12345;
- myString = "Hello World";
- decimal accountBalance 34.87323;
Because C# is a type-safe language, you will not be able to assign a string to a variable.
- int myNumber = "Hello World"; //This will not compile
You can also assign a variable the value of another variable of the same type.
- int x = 5;
- int y;
- y = x;
Variable assignment assigns the values of x to y. X will still retain its original value; Y is just given the value of X.
Types of type
In the .Net world, there are two different categories that a data type can be: value types and reference types.
A value type directly contains its data. A variable of type int contains a value (e.g. 12345) and only a value. Each variable of a value type has its own copy of the data. An operation on one variable does not affect another. Value types are stored in a memory location known as the stack.
Examples of Value types are int, float, struct, and enum.
Reference types contain a reference, or memory location, of their data. The data is stored as an object on the heap. Unlike value types, two or more reference types can point to the same memory location, thus an operation on one variable can affect another. Reference types are instantiated with the new keyword.
Examples of Reference types are string, arrays, classes and delegates.
Some data types can be assigned to a different type if they can be implicitly cast. This means that a small number can be assigned to a large number, but not vice versa.
Let's have a look at two data types, the byte and the long. As you can see from the data types guide, a byte can hold whole numbers between 0 and 255 and a ulong can hold whole numbers between 0 and 9,223,372,036,854,775,807. Think of these as a shot glass and a pint glass.
- byte shotGlass;
- ulong pintGlass;
In these analogies, please try and imagine that when we "pour" one glass into another glass, the original glass does not lose any of its contents. Rather, an equal amount of fluid is added to the other glass, while the original retains its contents.
It makes sense that you can pour the contents of the shot glass into the pint glass; there really is no danger of it overflowing, so the compiler will allow this to happen, without any warnings. This is called an implicit typecast and is done by the compiler automatically each time you assign a variable to another variable.
- pintGlass = shotGlass;
The pintGlass now contains the contents of the shotGlass.
So what happens if you want to pour the contents of the pint glass into the shot glass? It may be possible, it depends on how much is in the pint glass. The compiler will see this as dangerous as there is a good chance that the shot glass will overflow, so it will flag an error and prevent the program from compiling.
- shotGlass = pintGlass;
Cannot implicitly convert type 'ulong' to 'byte'. An explicit conversion exists (are you missing a cast?)
It is possible however to say to the compiler, "I know what I am doing, please let me pour the contents of my pint glass into my shot glass." This is called an explicit typecast and is performed using the data type you are converting to in brackets just before the variable.
- shotGlass = (byte) pintGlass;
In this case, the compiler assumes we know what we are doing and allows it.
If the shot glass does overflow, the value of the shotGlass will roll over and start from 0, so if you try and put a value of 256 into the shotGlass, the value would be 0, a value of 257 would be 1, 258 would be 2 and so on.
You should only explicitly typecast when you are certain that the values will fit, and it is sensible to test for this condition before performing the conversion.
Floating-point numbers are able to store values with a decimal fraction as well as a value, for example, 2.5632. You can implicitly cast an int to a float or decimal type, but you cannot implicitly cast a decimal or float to an int as an int will not be able to hold the data after the decimal point. You can explicitly cast, however, you will lose any decimal data, i.e. 2.5632 cast as an int will become just 2.