Programming is ultimately “memorizing and processing values.” C# is a statically typed language, so all variables are typed at compile time. In this section, we discuss the differences between the basic type and the value/reference type.
Learning Objectives
- Know the size and range of basic types
int/long/double/decimal/bool/char/string - Understand the difference between value type and reference type
- Use
var/const/readonlywhere appropriate. - Understand the immutability of strings
- Know what boxing is and why it is slow
Key concepts
1) Built-in types
| Type | size | Scope/Use | Yes |
|---|---|---|---|
int |
4 B | Approximately ±2.1 billion, general integer | int n = 100; |
long |
8 B | Approximately ±9.2×10¹⁸, large integer | long big = 10_000_000_000L; |
double |
8 B | floating point, common real number | double pi = 3.14; |
decimal |
16 B | 28-digit precision real number, for money calculations | decimal won = 1500m; |
bool |
1 B | true / false |
bool ok = true; |
char |
2 B | Unicode single character | char c = 'A'; |
string |
variable | string (reference type) | string s = "hi"; |
For long, add L at the end, and for decimal, add m at the end.
2) Value type vs reference type
- Value type (struct, int, double, bool, char, enum...): The value itself is stored in a variable. Duplicate entire copy when copying.
- Reference type (class, string, array...): There is an object on the heap and the variable only has its address. Only share the address when copying.
int a = 10;
int b = a; //Copy value
b = 20;
//a == 10, b == 20 (independent of each other)
int[] arr1 = [1, 2, 3];
int[] arr2 = arr1; //Copy reference
arr2[0] = 99;
//arr1[0] == 99 (points to the same array)
3) var: Type inference
In var, the compiler infers the type by looking at the right side. Types do not disappear, they are just omitted.
var name = "Alice"; // string
var age = 30; // int
var pi = 3.14; // double
It can only be used in local variables, and cannot be used without initialization (var x; is not possible).
4) const and readonly
const: compile time constant. Initialized upon declaration and cannot be changed afterwards. Available only for basic type andstring.readonly: Can only be assigned once at runtime. Used for the value determined in the constructor.
const double Pi = 3.14159; //A value that never changes
//Pi = 3.14; // compile error
5) Strings are immutable
string is a reference type, but its contents cannot be changed. += or Replace creates and returns a new string.
string s = "Hello";
s += ", World!"; //Creates a new string, s points to a new object
6) Boxing
When a value type is treated as a reference type such as object, it is the operation of creating a copy on the heap. Avoid it as much as possible as it is slow and puts pressure on the GC.
int n = 42;
object o = n; //Boxing: Creating a new object on the heap
int m = (int)o; //Unboxing
See as example
Example 1 — Primitives/Program.cs: Examining the basic types
int i = 100;
long l = 10_000_000_000L;
double d = 3.14;
decimal m = 1500.99m;
bool b = true;
char c = 'A';
string s = "hello";
Console.WriteLine($"int : {i} ({sizeof(int)} B)");
Console.WriteLine($"long : {l} ({sizeof(long)} B)");
Console.WriteLine($"double : {d} ({sizeof(double)} B)");
Console.WriteLine($"decimal: {m}");
Console.WriteLine($"bool : {b}, char: {c}, string: {s}");
Execution result
int : 100 (4 B)
long : 10000000000 (8 B)
double : 3.14 (8 B)
decimal: 1500.99
bool: True, char: go, string: hello
Memo: sizeof(decimal) is omitted because it requires an unsafe context. You can distinguish between digits as _ (10_000_000_000).
Example 2 — ValueVsReference/Program.cs: Comparing copy behavior
namespace CodingNow.Lecture.Basics02;
internal static class Program
{
public static void Main()
{
Point p1 = new(1, 2);
Point p2 = p1; //Copy value
p2.X = 99;
Console.WriteLine($"p1=({p1.X},{p1.Y}) p2=({p2.X},{p2.Y})");
Box b1 = new() { Value = 10 };
Box b2 = b1; //Copy reference
b2.Value = 99;
Console.WriteLine($"b1={b1.Value} b2={b2.Value}");
}
}
internal struct Point
{
public int X;
public int Y;
public Point(int x, int y) { X = x; Y = y; }
}
internal class Box
{
public int Value;
}
Execution result
p1=(1,2) p2=(99,2)
b1=99 b2=99
Memo: struct is a value type, so it is independent, and class is a reference type, so they share the same object.
Example 3 — VarConst/Program.cs : var, const, readonly
var name = "Alice"; //Inference: string
var age = 30; //Inference: int
const double Pi = 3.14159;
const string Greet = "Hello!";
Console.WriteLine($"{name}, {age} years old, {Greet} (Pi={Pi})");
//Pi = 3.0; // compile error
Execution result
Alice, 30 years old, Hi! (Pi=3.14159)
Note: var is used for readability, but if the right-hand side type is not clear, an explicit type is better.
Example 4 — StringBasics/Program.cs: String immutability
string a = "Hello";
string b = a;
a += ", World!"; //Create a new string
Console.WriteLine($"a = {a}");
Console.WriteLine($"b = {b}"); //b is still "Hello"
//Comparison of connection methods
string c1 = string.Concat("Value:", 42);
string c2 = $"value:{42}"; //String interpolation (recommended)
Console.WriteLine(c1);
Console.WriteLine(c2);
Execution result
a = Hello, World!
b = Hello
Value:42
Value:42
Note: Even after a += "...", b still points to the original string. Strings are immutable, so they are safe to share.
Common Mistakes
- Overflow by using
intto exceed theintrange (about ±2.1 billion) — the larger number islong. - Use
doublefor money calculations — floating point error. Must bedecimal. - Using
varwithout initialization likevar x;— compilation error. - Accumulate
stringinto+=in a loop — new object each time. When there are many, it isStringBuilder(refer to Part 16). - Confusion between
constandreadonly—constis a compile-time constant, andreadonlyis a one-time assignment at runtime.
Cleanup
- Integers are
int/long, real numbers aredouble(science) /decimal(money) - Value types are copied in their entirety when copied, and reference types share only the address.
varis convenient for type inference, andconstis absolutely immutable.stringis a reference type, but immutable — can be safely shared- Boxing may occur if you enter a value type as
object→ Avoid it as much as possible.
Try it yourself
cd src/Primitives && dotnet run
cd ../ValueVsReference && dotnet run
cd ../VarConst && dotnet run
cd ../StringBasics && dotnet run