3.0 What This Teaches
This tutorial covers how C# handles variables and types:
- Declaring variables with explicit types and with
var
- Value types: integers, floats, booleans, characters
- Reference types: strings and objects
const and readonly
- Nullable types and the
?? operator
- Type conversions
3.1 Declaring Variables
C# is statically typed - every variable has a fixed type decided at compile time.
Declare a variable with an explicit type or with var:
int count = 10; // explicit type
double ratio = 3.14; // explicit type
var name = "Alice"; // compiler infers string
var active = true; // compiler infers bool
var is not dynamic typing - the type is still fixed at compile time.
It just lets the compiler infer it from the right-hand side. Use var
when the type is obvious from context; write the type explicitly when it aids clarity.
3.2 Value Types
Value types hold their data directly. Assigning one to another copies the data.
Common value types:
| Type | Size | Range / Notes |
| int | 32-bit | -2,147,483,648 to 2,147,483,647 |
| long | 64-bit | Very large integers |
| double | 64-bit | ~15 significant decimal digits |
| float | 32-bit | ~7 significant decimal digits |
| decimal | 128-bit | Exact decimal; use for money |
| bool | 1 byte | true or false |
| char | 16-bit | A single UTF-16 code unit; single quotes: 'A' |
3.3 Reference Types
Reference types store a reference to data on the heap. Assigning one to another
copies the reference, not the data - both variables point to the same object.
string greeting = "Hello"; // immutable string
string copy = greeting; // copy points to same string object
object anything = 42; // object holds any type
string is special - it is a reference type but behaves like a value type
because strings in C# are immutable. Every modification creates a new string object.
3.4 const and readonly
Use const for values fixed at compile time. Use readonly
for values set once at runtime (in a constructor or field initializer):
const double Pi = 3.14159265358979;
const int MaxRetries = 3;
readonly DateTime startTime = DateTime.Now; // set at runtime, then fixed
const values are inlined by the compiler - they never occupy memory at
runtime. readonly fields occupy memory but cannot be reassigned after
the constructor finishes.
3.5 Nullable Types
By default, value types cannot be null. Append ? to allow null:
int? age = null; // nullable int
string? name = null; // nullable reference type (requires Nullable enabled)
int displayAge = age ?? 0; // ?? returns right side if left is null
string displayName = name ?? "Guest";
int length = name?.Length ?? 0; // ?. short-circuits on null
With Nullable enabled in the project, the compiler warns when you
dereference a nullable reference without a null check.
3.6 Type Conversions
Implicit conversions happen automatically when no data is lost. Explicit
casts require your instruction because data may be lost:
int i = 42;
long l = i; // implicit: int always fits in long
double d = i; // implicit: int always fits in double
double pi = 3.14;
int truncated = (int)pi; // explicit cast: truncates to 3
string s = "123";
int parsed = int.Parse(s); // throws if invalid
bool ok = int.TryParse(s, out int result); // safe: returns false if invalid
3.7 Example - All Together
// Variables - demonstrates C# types, var, nullable, and conversion.
int score = 95;
double average = 88.5;
bool passed = score >= 60;
char grade = passed ? 'A' : 'F';
string? comment = null;
Console.WriteLine($"Score: {score}, Average: {average:F1}");
Console.WriteLine($"Passed: {passed}, Grade: {grade}");
Console.WriteLine($"Comment: {comment ?? "none"}");
const int MaxScore = 100;
double percent = (double)score / MaxScore * 100;
Console.WriteLine($"Percent: {percent:F1}%");
Expected output:
Score: 95, Average: 88.5
Passed: True, Grade: A
Comment: none
Percent: 95.0%
3.8 Exercise
Exercise
- Declare an
int for your age, a double for your height
in meters, a string for your name, and a bool for whether
you are a student. Print all four.
- Declare an
int? set to null. Use ??
to display 0 as a default when printing.
- Parse the string
"42" to an int using
int.TryParse. Print whether it succeeded and the result.
3.9 Common Mistakes
Integer overflow
int max = int.MaxValue;
int overflow = max + 1; // wraps to int.MinValue in unchecked context
int overflows silently by default. Use long when values
might exceed 2,147,483,647, or wrap in a checked block to catch it.
Using double for money
double price = 0.1 + 0.2;
Console.WriteLine(price); // 0.30000000000000004
Binary floating-point cannot represent all decimal fractions exactly.
Use decimal for financial calculations.
NullReferenceException on uninitialized reference
Calling a method on a null reference throws
NullReferenceException. With nullable types enabled, the compiler warns
about potential null dereferences. Heed those warnings.
3.10 Key Terms
| Term | Meaning |
| value type | Type whose data lives directly in the variable (int, double, bool, struct) |
| reference type | Type whose variable holds a reference to heap data (class, string, array) |
| var | Keyword that lets the compiler infer the type; still statically typed |
| const | Compile-time constant; inlined by the compiler |
| readonly | Runtime constant; set once in a constructor or initializer |
| nullable (int?) | Value type extended to allow null values |
| ?? operator | Returns the right operand when the left is null |
| decimal | 128-bit exact decimal type; use for money and financial math |