int x;
.=
: x = 3;
Console.WriteLine(x);
int
, short
, and long
store positive and negative integers of different sizes.uint
, ushort
, and ulong
store only positive (“unsigned”) integers of different sizes.byte
stores single byte integers (0 to 255), but often only used in collections of bytes for storing arbitrary binary data, while sbyte
is its “signed” counterpart (-127 to +128).float
, double
, and decimal
store floating-point values (which allow decimal points, like 3.14
) with 4, 8, and 16 bytes, respectively.char
stores single characters ()char letter = 'C';
) while string
stores longer text.bool
stores truth values, true
, and false
.In this tutorial, we’ll cover one of the most important fundamentals of programming: variables. Variables are how a C# program can store data. Any program that does anything real will use them.
In this tutorial, we’ll discuss how to make and use variables. We’ll also discuss the different types of variables that you can use.
Imagine you are making a game and want to keep track of a player’s score or the number of lives they have left.
A variable is like a little bucket to put stuff in. You can change what thing is currently in the bucket, but it can only hold one item at a time.
Each variable also has a name. The variable’s name will allow you to refer to it throughout your code.
In C#, each variable also has a type. Each piece of data has a specific type, and a variable can only store data if the type is right.
The reason for this is that each type stores its content in memory –the bits and bytes where your variable actually lives–in different ways. If data was stored in memory using one type’s scheme, and then accidentally tried to access it with another type’s scheme, bad things would happen.
(The good news is, there are mechanisms that will let us convert from one type to another when we need to.)
To summarize, each variable will have:
Let’s talk about how to make a variable. This is simple to do, by adding a line like the following in your code:
int score;
This is called a variable declaration
, or you could say that this line declares
a variable. (“I hereby declare that there shall be a variable called score
, and decree that it shall hold things of the type int
!”)
The int
part is the variable’s type–an integer.
The score
part is the variable’s name.
And like most statements in C#, this one ends with a semicolon.
note
An integer is any number that doesn’t have a fractional or decimal part. 0, 1, 2, 3, 4, …, as well as -1, -2, -3, ….
There are lots of types to choose from in C#, and before long, we’ll be defining our own custom types as well. Sometimes, it is a challenge to know which of the many types to pick. That’s just another one of those things that takes some practice to get good at.
Variable names can be made up of any number or letter, or the underscore (_
) character, though you cannot start a variable’s name with a number.
So score
, hamburger
, _aPictureIsWorthA1000Words
are all allowed names, but 1
, taco-poptart
, and 1stTry
are not.
You want to pick a name that will help you remember what it is used for.
With a variable declared, our next task is to put a value in the variable.
This is done with the assignment operator
, which is an =
sign.
On a line below the previous declaration, you could do the following, which puts the integer 0
into the variable score
:
score = 0;
The =
sign is a little different in C# than you might be used to in math.
This is not about equality.
score
does not equal 0
.
This says, “Put the specific value of 0
into the variable called score
”.
If that is confusing, you could “read” the equals sign as “gets assigned” or simply “gets.”
So you might read the line above as, “The variable score
gets assigned a value of 0
.”
That’s not the only value you can assign to score
:
score = 4;
score = 11;
score = -1564;
While you only declare each variable once, you can assign values to a variable as often and whenever you want. (Though you must declare a variable before you can use it.)
The first time you assign a value to a variable, it is called initializing the variable. You must initialize a variable before you can try to retrieve data from it, so you should always initialize your variables to good starting values.
Because you typically want to initialize a variable soon after declaring it, there is a way to do both in a single line:
int theMeaningOfLife = 42;
Storing data is great, and all, but if we don’t ever use the data, there isn’t much point in storing it.
You can retrieve the value currently in a variable by simply using its name. To illustrate:
int number = 4;
Console.WriteLine(number);
Referencing a variable by name will always retrieve the current value out of the variable. Consider what output this code might have:
int number = 4;
Console.WriteLine(number);
number = 8;
Console.WriteLine(number);
number = 12;
Console.WriteLine(number);
This will display the following:
4
8
12
With that last sample, you might be wondering why we’d even use variables. For example, why couldn’t we have simplified that last sample to simply this?
Console.WriteLine(4);
Console.WriteLine(8);
Console.WriteLine(12);
If that was truly all we were trying to accomplish, we definitely could have simplified it in that way.
But, of course, there is a lot more we can do with variables.
One key ingredient in C# programs is an expression
.
An expression is a little chunk of code that the computer can evaluate to compute a value.
You’ve seen expressions before, in the math world.
2 + 2
is an expression, for example, and so is x - 1
.
While we’ll look at mathematical expressions in more detail in a future tutorial, you can start to imagine how you can build expressions that use variables to do some complex operations in your program.
In fact, for our next example, let’s introduce our second variable type: string
.
A string
is just a fancy programmer word for text.
So you can declare the following variable that is designed to store text:
string name = "RB";
You can then use it in an expression like this:
Console.WriteLine("Hello, " + name);
That "Hello, " + name
is an expression.
It will combine two pieces of text together into a single, longer piece of text (string).
The combination is what is given to WriteLine
to display.
If we change the variable, we get different output.
In fact, fixed values–called literals –are also expressions. They’re the simplest flavor of expressions.
Naming a single variable, such as a plain old name
, is an expression as well.
Meanwhile, an expression with a +
is sort of a compound or composite expression, which lets you combine two smaller expressions together.
So in the code "Hello, " + name
, The literal string "Hello, "
is an expression, the variable reference name
is an expression, and the whole thing ("Hello, " + name
) is an expression.
You can combine lots of things together into more and more complex expressions. For example:
Console.WriteLine("Hello, " + name + "! " + "How are you?");
Expressions can grow quite large. If they start to get too hard to understand quickly, you might want to break them up into smaller pieces instead, to make them easier to understand.
string name = "RB";
string greeting = "Hello, " + name + "!";
string theQuestion = "How are you?";
Console.WriteLine(greeting + " " + theQuestion);
Let’s take a moment and look at the basic types that are available in C#.
Keep in mind that these are just the primitive
types–the most foundational types.
There are lots of other types that exist, and we’ll get to them later.
(Even Console
is a type!)
int
, short
, and long
The types int
, short
, and long
store integers, but use different numbers of bytes and with different ranges.
int
: 4 bytes, and numbers in roughly the range of -2 billion to +2 billion.short
: 2 bytes, and numbers in roughly the range -32,000 to +32,000.long
: 8 bytes, and numbers in roughly the range -9 quintillion to +9 quintillion.Most C# programmers tend to default to int
, unless they know it isn’t big enough, or there isn’t much memory, in which case, they might pick one of the other types.
You might be asking, “Why is the range of integers limited?”
Good question!
In short, the more bits or bytes you use, the more possible values you can represent. With just one bit (a single 0 or 1), you can represent only two possibilities. With two bits, you can represent four. Each bit you add doubles the number of possible options you can represent.
But unless you’re using a scheme to add more bits and bytes as needed as the number gets bigger, you will always have limits to how big you can make things.
note
Technically, there is a way you can grow to numbers that are arbitrarily large–as big as you need. Most things don’t need this capability, and a computer’s hardware–the circuitry itself–is just not designed to deal with such numbers. It is rarely needed, and much slower, and so it isn’t the typical approach in any programming language, including C#.
For all three of these, you can make a literal of these types in the exact same way you make a literal for the int
type:
short a = 14;
When you run your program, if the number isn’t in the right range for the type you are trying to assign it to, your program won’t compile, and it will tell you that it is because the number isn’t in the range supported by the type.
uint
, ushort
, and ulong
The int
, short
, and long
types are all signed
types, meaning they have a positive or negative sign on them.
Since these types must represent both positive and negative values, they must reserve half of the range for negative values.
There are situations where negative values just aren’t needed, and you’d prefer to use the extra range to count higher.
That is where uint
, ushort
, and ulong
come in.
These are unsigned
types.
They can’t store negative values, but their maximum is twice as high as their signed counterparts:
uint
: 4 bytes, and numbers from 0 to roughly 4 billion.ushort
: 2 bytes, and numbers from 0 to roughly 65,000.ulong
: 8 bytes, and numbers from 0 to roughly 19 quintillion.Most C# programmers will typically prefer the signed types instead of these unsigned types, unless there’s a very strong reason for using these. So they don’t see nearly as much use.
Literal values for these three types is also done like an int
, and the compiler will check its range for you (including stopping you from accidentally using a negative value):
ulong bigNumber = 10000000000000;
byte
and sbyte
There is also a type that represents just a single byte: the byte
type.
The byte
type can represent numbers in the range 0 to 255.
In that sense, you could think of it as an unsigned, very tiny integer type.
But in practice, it is more often used in big collections to represent arbitrary piles of binary data.
It has a signed counterpart: sbyte
, which can store numbers from -127 to +128, though sbyte
is not used very often.
Making a literal of the byte
and sbyte
types works the same as int
:
byte b = 10;
float
, double
, and decimal
Let’s broaden our perspective to go beyond just integers. The so-called floating-point numbers store numbers that are allowed to have fractional or decimal parts. The way they represent numbers allows them to get much, much bigger, while also not being able to accurately represent every single number perfectly.
warning
The fact that floating-point numbers can’t perfectly represent all numbers in its range is important. It means there will be rounding errors and calculation errors. It is something to keep in mind when using these types–they are very different from the integer types.
float
: 4 bytes, and numbers with up to 38 digits (but only with seven digits of accuracy at a time).double
: 8 bytes, and numbers with up to 308 digits total (but only 15 or 16 digits of accuracy at a time).decimal
: 16 bytes, and numbers with up to 28 digits (but with only 28 digits of accuracy at a time).Those accuracies also apply when defining super tiny numbers as well.
For example, 0.00000003456
.
In general, few numbers really need more than what float
provides, and in game programming, most things that are not integers will be float
.
(In truth, decimal
rarely has any practical value outside of monetary and financial calculations, where the numbers don’t get giant, but accuracy rules the day.)
Making a literal value of the double
type is as simple as putting a decimal point in a number you write out:
double d = 3.14;
If you want a literal float
value, you must stick an f
or F
on the end:
float f = 3.14f;
For a decimal
literal, you stick an m
or M
on the end:
decimal m = 3.0m;
char
an string
The char
and string
types are used for text.
char
represents a single character, though it uses two bytes and can represent any letter you’ve ever even heard of, not just English letters.
The string
type is basically a sequence of char
values, and can be used to represent words and sentences.
A char
literal is made by using single quotes, while a string
literal is formed with double quotes:
char letter = 'c';
string text = "C is for cookie.";
bool
The bool
type is used to store truth values: true
and false
.
This might seem a little strange or useless at first glance, but truth values are an important part of programming, as we’ll see soon.
A bool
is one byte in size, and literals are made by simply using the true
or false
keywords:
bool canHasCheezburger = true;
var
You will sometimes also see variables declared with the var
type.
This is a type that says, “Compiler (and humans), I think you can figure this out on your own. Please determine the type from the rest of the code around it.”
var canHasCheezburger = true;
In this case, the compiler will see that it is being assigned a value of true
, so it knows that canHasCheezburger
must clearly have the same type.
warning
Just because the compiler can infer the right type does not mean a human will!
The var canHasCheezburger = true;
example above is pretty straightforward, but it isn’t always so obvious.
And if the human thinks the type is one thing, and the compiler picks something else, you’re going to have a bad time.
Use var
with caution.
You can only use var
when the compiler can actually figure out what type you’re referring to, which is often, but not always.
tip
I recommend new programmers pass on var
for the short term.
Get used to the different types, and thinking about them and how they work.
Types matter a lot in C#, and var
tends to push the type into the shadowy corners.
Start by using specific types, not var
.
After a while, you’ll get more comfortable with types, and can start using var
when it makes sense.
danger
The var
type is not a flexible type that can change over time.
It still has a specific type, you just don’t write it out.
It is a common misconception for new C# programmers to think of var
as having a flexible type, especially if their previous experience is with programming languages whose variables don’t have fixed types.
Because var
can make it harder to figure out what’s going on–especially in online, partial code snippets, missing a lot of the context–I’m going to generally avoid var
on this site.
If you prefer var
, feel free to use it.