Variables and Types in C#
Learn about variables, data types, type inference, and type conversions in C# with practical Docker-ready examples
C# is a statically, strongly typed language — every variable has a declared type known at compile time, and the compiler enforces type safety throughout your code. This is one of C#’s core strengths: mistakes that would cause runtime crashes in dynamic languages are caught before your program ever runs.
Despite being statically typed, C# is far from verbose. The var keyword lets the compiler infer the type from the assigned value, giving you the safety of static typing with much of the brevity of dynamic languages. Modern C# also has nullable reference types, record types, and pattern matching that make working with data concise and expressive.
In this tutorial you’ll learn C#’s built-in primitive types, how to declare and assign variables, how to use var for type inference, how to work with constants, and how to perform type conversions — both implicit and explicit.
The Complete Example
Create a file named Variables.cs:
| |
Walkthrough
Primitive Types
C# has a rich set of built-in value types that map directly to .NET runtime types. Each has a C# keyword alias (like int) and a full .NET name (like System.Int32) — they are identical.
| Alias | .NET Type | Size | Range |
|---|---|---|---|
int | System.Int32 | 32-bit | −2.1B to 2.1B |
long | System.Int64 | 64-bit | Very large integers |
double | System.Double | 64-bit float | ±1.7×10³⁰⁸ |
float | System.Single | 32-bit float | ±3.4×10³⁸ |
decimal | System.Decimal | 128-bit | High-precision decimals |
bool | System.Boolean | — | true or false |
char | System.Char | 16-bit | Single Unicode character |
string | System.String | ref type | Immutable Unicode text |
Underscores in numeric literals (8_100_000_000L) are purely cosmetic — the compiler ignores them but they greatly improve readability for large numbers.
Type Inference with var
The var keyword lets the compiler deduce the type from the right-hand side of the assignment. The variable is still statically typed — var is just syntax sugar, not dynamic typing.
| |
var is especially useful with verbose generic types:
| |
Constants and Nullable Types
const declares a compile-time constant — the value is inlined by the compiler and cannot change. Use readonly (not shown here) for values that are set once at runtime, typically in a constructor.
Nullable value types (int?, double?, etc.) wrap a value type in Nullable<T>, enabling null assignment. The ?? (null-coalescing) operator provides a concise fallback:
| |
Type Conversions
C# distinguishes between:
- Implicit (widening) — safe, no data loss, done automatically by the compiler (
int→long,int→double). - Explicit casting — may lose precision or overflow; written as
(TargetType)value. Fractional parts are truncated, not rounded. Convertclass — parses strings and converts between many types; throws on failure.TryParse— safe parsing that returns aboolinstead of throwing; preferred for user input.
Running with Docker
| |
Note: The Docker command bootstraps a temporary .NET console project in the current directory, replaces
Program.cswith your file, then compiles and runs it. Your original file is preserved.
Expected Output
── Primitive Types ──
int: 30
long: 8100000000
short: 32000
byte: 255
double: 3.14159265358979
float: 3.14159
decimal: 19.99
bool: True
char: A
string: C# Developer
── Type Inference with var ──
count is Int32: 42
message is String: Hello, C#!
ratio is Double: 3.14
flag is Boolean: True
numbers is List`1 with 5 elements
── Constants ──
Pi = 3.14159265358979
DaysInWeek = 7
AppName = CodeArchaeology
Circumference of r=5 circle: 31.4159
optionalAge is null: True
optionalAge after assignment: 25
definiteAge (using ??): 25
── Type Conversions ──
Implicit conversions:
int → long: 100
int → double: 100
Explicit cast (truncates, does not round):
(int)3.99 = 3
Convert class:
Convert.ToInt32("42") = 42
Convert.ToDouble("3.14") = 3.14
Convert.ToBoolean(1) = True
int.TryParse("123abc") succeeded: False
int.TryParse("999") succeeded: True, value: 999
String representations of 255:
Decimal: 255
Hex: FF
Binary: 11111111
Key Concepts
- Static, strong typing — every variable’s type is fixed at compile time; the compiler rejects type mismatches before the program runs.
varis not dynamic — the compiler infers the type from the initializer; the variable is still statically typed and cannot change type later.- Value types vs reference types — primitives (
int,double,bool,char,struct) are value types stored on the stack;string, arrays, and class instances are reference types on the heap. stringis special — despite being a reference type, strings behave like value types: they are immutable, and equality (==) compares content, not reference identity.decimalfor money — always usedecimal(notdoubleorfloat) for financial calculations; it avoids binary floating-point rounding errors.- Nullable value types (
int?) — value types normally cannot benull; appending?wraps them inNullable<T>, enablingnullassignment and the??null-coalescing operator. - Prefer
TryParseoverParse—int.Parsethrows on invalid input;int.TryParsereturns abooland is safer for user-supplied strings. - Digit separators — underscores in numeric literals (
8_100_000_000L) are purely cosmetic and improve readability of large numbers; the compiler ignores them.
Running Today
All examples can be run using Docker:
docker pull mcr.microsoft.com/dotnet/sdk:9.0
Comments
Loading comments...
Leave a Comment