Variables & Basic Types

Course 1 · Ch 2
Variables & Basic Types
Java draws the primitive/reference line for you — C# hands you the pen

java1-2 drew a hard line: eight built-in primitives, everything else a reference type, no way for a programmer to add to either side. C# keeps the same two-category split — but genuinely lets you choose which side a new type belongs on.

Value Types vs. Reference Types

A value type holds its data directly — assigning or passing it copies the actual data. A reference type holds a reference to data stored elsewhere — assigning or passing it copies the reference, not the underlying data. This is C#'s own version of java1-2's primitive-vs-reference split, but with a genuine structural difference: in Java, only the eight built-in primitives are ever value-like — every programmer-defined type is automatically a reference type, no exceptions. In C#, a programmer can define a brand-new value type of their own.

struct vs. class

struct Point { // a value type — copied by value public int X, Y; } class Person { // a reference type — copied by reference, same as every java1-4 class public string Name; }

struct declares a value type; class declares a reference type — otherwise, the two look almost identical to write. This is the real, structural choice java1-4's own classes never offered: in Java, every custom type is unconditionally a reference type, full stop.

Built-in Types Are Just Structs

int x = 5; x.ToString(); // legal — int genuinely IS a struct (System.Int32), a real object underneath 5.ToString(); // also legal, for the same reason

Here's the real reveal: int, double, and bool aren't magic keywords divorced from the type system — they're built-in aliases for real structs (System.Int32, System.Double, System.Boolean). Because they're genuinely structs, they can call methods directly, no wrapping required. This is a real, structural contrast with java1-2's own primitives, which aren't objects at all — Java's int can never call a method on itself; it must first be boxed into an Integer.

var Type Inference

var count = 5; // inferred as int at compile time var name = "Alice"; // inferred as string at compile time // count = "text"; // still a compile error — var is NOT dynamic typing

var lets the compiler infer a variable's type from its initializer — the type is fixed permanently at compile time, exactly as if it had been spelled out explicitly. This is genuinely still static typing, in the same spirit as C++'s auto, not a loophole into dynamic typing.

Nullable Value Types

int count = null; // compile error — a plain value type can never be null int? count = null; // legal — opts in via Nullable<int> underneath

A value type genuinely can't be null by default — it's real data sitting directly in the variable, not a reference that could point to nothing. The ? suffix opts a value type into nullability through Nullable<T>, a wrapper struct. This is a separate, earlier feature from Course 2's own nullable reference types chapter, which addresses the opposite problem — reference types, which could always be null by default until that later feature opted them out of it.

AspectJava (java1-2)C#
Who can define a value typenobody — only 8 built-insany programmer, via struct
Is int a real object?no — must box to Integer firstyes — int genuinely is System.Int32
Calling a method on a literalnot possible directly5.ToString() works directly
Can a value type be nulln/a — primitives have no null conceptonly if explicitly marked nullable (int?)
Reach for struct for small, immutable, frequently-copied data
A small value type like a coordinate pair avoids heap allocation and garbage-collector pressure entirely — a genuine performance-relevant choice, not just a style preference, when a type is small and copied often.
A large struct copied often can hurt, not help, performance
Value-type semantics mean a struct is copied in full on every assignment and every parameter pass — for a large struct, that copying cost can exceed the cost of copying a reference to a class instance. struct is a genuine tradeoff, not a free performance win in every case.

Coding Challenges

Challenge 1

Write a struct Point with X and Y int fields, and a class Wallet with a decimal Balance field, create one instance of each, assign each to a second variable, modify the second variable's fields, and print both original and copy to show which one changed and why.

📄 View solution
Challenge 2

Write code that calls a method directly on an int literal (e.g. 42.ToString()) and explain in a comment why this is legal in C# but would not be legal on a raw int in Java without boxing.

📄 View solution
Challenge 3

Declare an int? variable, assign it null, then attempt the same with a plain int. Show the resulting compile error on the plain int and explain the difference in terms of value-type semantics.

📄 View solution

Chapter 2 Quick Reference

  • struct declares a value type (copied by value); class declares a reference type (copied by reference) — unlike Java, where only 8 built-ins are ever value-like
  • C#'s int/double/bool are real structs (System.Int32, etc.) — they can call methods directly, unlike Java's non-object primitives
  • var infers a type at compile time — still fully static typing, not dynamic typing
  • A plain value type can never be null; int? opts in via the Nullable<T> wrapper struct
  • struct is a genuine tradeoff — great for small, immutable, frequently-copied data, costly for large types copied often
  • Next chapter: operators and control flow — switch expressions with pattern matching from day one