Variables and Data Types
Variables store state in memory.
Introduction
Variables store state in memory. C#'s static type system assigns every variable a compile-time type — int, decimal, string, or custom types — catching bugs before deployment. Understanding value types vs reference types is the single most important concept for interview success.
Modern C# adds var for local type inference, required members, records for immutable data, and nullable reference annotations. Financial and healthcare systems rely on decimal for money — never float — and explicit DateTimeOffset for audit trails.
This lesson covers primitives, strings, var rules, const vs readonly, and boxing pitfalls that still appear in senior .NET interviews.
The story
A digital wallet app stores each account snapshot with a unique ID, a balance in dollars and cents, and the exact moment the snapshot was taken. Using decimal for money avoids the rounding errors you get with double — a bug that has caused real financial discrepancies in production systems.
When the user receives a $100.50 bonus, the app creates a new snapshot rather than mutating an old one, preserving an audit trail regulators expect from banking software.
Understanding the topic
Key concepts
- Value types (int, bool, struct) live on the stack or inline in objects; copying copies data.
- Reference types (class, string, array) hold references to heap objects; copying copies the reference.
- var requires initializer — compiler infers type; locals still strongly typed.
- decimal is 128-bit floating point ideal for currency; double for scientific math.
- Nullable value types (int?) and nullable reference types (string?) express absence.
- const compile-time constants; readonly set once at construction.
classDiagramclass ValueTypes {intdoubleboolstruct}class ReferenceTypes {stringobjectclassarray}
Step-by-step explanation
- Declare: type name = value; or var name = value;
- Compiler verifies assignments match declared type.
- Value type assignment duplicates bits; reference assignment aliases same object.
- Boxing wraps value types in object on heap — avoid in hot loops.
- string is immutable — concatenation in loops uses StringBuilder.
- Default values: 0, false, null (references), default struct fields zeroed.
Practical code example
Banking-style variables with decimal, DateTimeOffset, record, and nullable annotations:
namespace TechLearningPro.Variables;public record AccountSnapshot(Guid Id, decimal Balance, DateTimeOffset AsOf);public class VariableDemo{public static AccountSnapshot CreateSnapshot(decimal openingBalance){const string Currency = "USD";var accountId = Guid.NewGuid();decimal balance = openingBalance;DateTimeOffset asOf = DateTimeOffset.UtcNow;balance += 100.50m; // m suffix = decimal literalreturn new AccountSnapshot(accountId, balance, asOf) with { };}}
Line-by-line code explanation
public record AccountSnapshot(...)defines an immutable data shape with value-based equality — ideal for DTOs.Guid Idstores a globally unique identifier for the account snapshot.decimal Balanceholds money precisely — never usedoublefor currency.DateTimeOffset AsOfcaptures when the snapshot was taken with timezone awareness.const string Currency = "USD"declares a compile-time constant that cannot change at runtime.var accountId = Guid.NewGuid()generates a fresh unique ID for each new snapshot.decimal balance = openingBalancecopies the starting balance into a local variable you can modify.balance += 100.50madds a deposit — themsuffix marks a decimal literal.DateTimeOffset.UtcNowrecords the current UTC timestamp for API consistency.return new AccountSnapshot(...) with { }creates a record copy with updated field values.
Key takeaway: Always suffix decimal literals with m. Use DateTimeOffset.UtcNow for APIs storing timestamps. Records give value-based equality for DTOs.
Real-world use
Where you'll use this in production
- Ledger systems storing decimal amounts with invariant culture formatting.
- Patient vitals as structs for low-allocation telemetry buffers.
- Configuration DTOs as records passed through ASP.NET Core pipelines.
- Desktop apps binding ViewModel properties with INotifyPropertyChanged.
Best practices
- Use decimal for money; never float or double for currency.
- Prefer DateTimeOffset over DateTime for APIs and logs.
- Enable nullable reference types; use ? and ! consciously.
- Use var when type is obvious from right-hand side.
- Prefer readonly fields over mutable public fields.
- Use records for immutable data transfer objects.
Common mistakes
- Using float for money — rounding errors in payroll.
- Assuming string concatenation is cheap in tight loops.
- Uninitialized local variables — compile error CS0165.
- Confusing == on boxed value types with reference equality.
- Using var without initializer — CS0818.
Advanced interview questions
Q1BeginnerValue type vs reference type?
Q2BeginnerWhy decimal for financial calculations?
Q3IntermediateWhat is boxing?
Q4IntermediateDifference between const and readonly?
Q5AdvancedExplain nullable reference types and their compile-time benefit.
Summary
C# variables are statically typed — compiler enforces correctness. Value vs reference semantics affect copying, equality, and GC pressure. decimal and DateTimeOffset are enterprise defaults for money and time. var infers types; nullable annotations prevent null bugs. Next: operators for arithmetic, comparison, and logical expressions.