Basic Data Types (C#)
An overview of the basic data types (primitive types) in C#, including the difference between value types and reference types, type inference with var, and implicit/explicit type conversion. Choosing the right type helps improve memory efficiency and prevent bugs.
Basic Data Types
| Type name | Alias | Size | Range / Description |
|---|---|---|---|
System.Int32 | int | 32-bit | -2,147,483,648 to 2,147,483,647. The most commonly used integer type. |
System.Int64 | long | 64-bit | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807. Use when the value does not fit in int. |
System.Double | double | 64-bit | Approximately ±1.7×10³⁰⁸. Double-precision floating-point. The default type for floating-point literals. |
System.Single | float | 32-bit | Approximately ±3.4×10³⁸. Single-precision floating-point. Widely used in Unity and similar environments. Literals require the f suffix. |
System.Decimal | decimal | 128-bit | 28–29 significant decimal digits. Use where rounding errors are unacceptable, such as financial calculations. Literals require the m suffix. |
System.Char | char | 16-bit | A single Unicode character (U+0000 to U+FFFF). Enclosed in single quotes. |
System.Boolean | bool | 1-bit equivalent | true or false only. The only type that can be used directly in a condition expression. |
System.String | string | Variable length | A Unicode string. A reference type, but immutable — it can be used intuitively like a value type. |
Value Types vs. Reference Types
| Value Type | Reference Type | |
|---|---|---|
| Storage location | Stack (or inside a struct field). | Heap (the variable holds a reference to the heap object). |
| Assignment behavior | The value itself is copied. | The reference (address) is copied. |
| Default value | 0 / false / '\0', etc., depending on the type. | null (no reference). |
| Representative types | int / long / double / float / decimal / char / bool / struct | string / arrays / classes / object |
string is classified as a reference type, but strings are immutable, so assigning a different string to a variable does not affect the original string object. This is why string can be used intuitively, much like a value type.
Type Inference with var
When you use var, the compiler automatically infers the type from the right-hand side expression at compile time. The type is still statically determined — it does not change at runtime. Using var can improve readability when the type name would be long, but when the type is not obvious from the right-hand side, an explicit type name is clearer.
var power = 9000; // Inferred as int. var name = "Son Goku"; // Inferred as string. var ratio = 1.5; // Inferred as double. var isAlive = true; // Inferred as bool. // When the type is not obvious from the right-hand side, use an explicit type name. int hp = 0; string title = "Super Saiyan";
Sample Code
BasicTypes.cs
A sample that declares and prints each basic type.
using System;
class BasicTypes {
static void Main() {
int power = 9000; // Power level (fits within int range).
int maxPower = int.MaxValue; // Check the maximum value of int.
Console.WriteLine("power = " + power);
Console.WriteLine("int.Max = " + maxPower); // Outputs 2147483647.
// Expressed with a long literal (append the L suffix).
long frieza = 530_000_000L; // Underscores improve readability of large numbers.
Console.WriteLine("Frieza power = " + frieza);
double multiplier = 1.5; // Super Saiyan multiplier (assumed value).
double result = power * multiplier;
Console.WriteLine("Super Saiyan power = " + result); // Outputs 13500.
float speed = 9.8f; // Movement speed (float).
Console.WriteLine("speed = " + speed);
// Use where rounding errors are unacceptable, such as monetary calculations.
decimal price = 1980.50m; // Item price (before tax).
decimal taxRate = 0.10m;
decimal taxPrice = price * (1 + taxRate); // Calculate price including tax.
Console.WriteLine("Price with tax = " + taxPrice); // Outputs 2178.5500.
char initial = 'G'; // First letter of the name.
Console.WriteLine("initial = " + initial);
Console.WriteLine("charCode = " + (int)initial); // Check the Unicode code point.
bool isSaiyan = true;
bool hasTail = false;
Console.WriteLine("Saiyan: " + isSaiyan + ", Has tail: " + hasTail);
string hero = "Son Goku";
string transformed = hero + " (Super Saiyan)"; // Concatenation creates a new string object.
Console.WriteLine(transformed); // The original hero variable is unchanged.
Console.WriteLine("hero = " + hero); // Still "Son Goku".
}
}
Run the following command:
dotnet script BasicTypes.cs power = 9000 int.Max = 2147483647 Frieza power = 530000000 Super Saiyan power = 13500 speed = 9.8 Price with tax = 2178.5500 initial = G charCode = 71 Saiyan: True, Has tail: False Son Goku (Super Saiyan) hero = Son Goku
TypeConversion.cs
A sample demonstrating implicit conversion, explicit casts, and the Convert class.
using System;
class TypeConversion {
static void Main() {
// Converting from a smaller type to a larger type happens automatically (no data loss).
int intPower = 9000;
long longPower = intPower; // int → long: converted implicitly.
float floatPower = intPower; // int → float: converted implicitly.
double doublePower = intPower; // int → double: converted implicitly.
Console.WriteLine("=== Implicit conversion ===");
Console.WriteLine("int → long: " + longPower);
Console.WriteLine("int → float: " + floatPower);
Console.WriteLine("int → double: " + doublePower);
// Main implicit conversion directions: int → long → float → double
// int → float has about 7 significant digits, so large values may lose precision.
int bigInt = 123456789;
float bigFloat = bigInt; // Precision may be reduced.
Console.WriteLine("\nbigInt = " + bigInt);
Console.WriteLine("→ float = " + bigFloat); // Rounded to something like 123456790.
// Converting from a larger type to a smaller type, or when precision is lost,
// requires the cast operator (type name).
Console.WriteLine("\n=== Explicit cast ===");
double vegeta = 8500.9;
int castedPower = (int)vegeta; // double → int: the fractional part is truncated.
Console.WriteLine("double: " + vegeta + " → int: " + castedPower); // Becomes 8500.
long frieza = 530_000_000L;
int castedFrieza = (int)frieza; // long → int: be careful the value fits in int range.
Console.WriteLine("long: " + frieza + " → int: " + castedFrieza);
// Casting a value that exceeds int.MaxValue causes overflow (no exception by default).
long overflowVal = 3_000_000_000L; // Exceeds int's maximum (~2.1 billion).
int overflowed = (int)overflowVal;
Console.WriteLine("Overflow example: long " + overflowVal + " → int " + overflowed);
// char and int can be cast to each other.
char initial = 'G';
int code = (int)initial;
char restored = (char)code;
Console.WriteLine("\n'G' → int: " + code + " → char: '" + restored + "'");
// Unlike a cast, the Convert class throws an exception on overflow.
// It also handles string ↔ numeric conversions.
Console.WriteLine("\n=== Convert class ===");
string powerStr = "9000";
int parsed = Convert.ToInt32(powerStr); // string → int.
string backStr = Convert.ToString(parsed); // int → string.
Console.WriteLine("string → int: " + parsed);
Console.WriteLine("int → string: " + backStr);
double piVal = 3.14159;
int piInt = Convert.ToInt32(piVal); // double → int (rounded).
Console.WriteLine("double " + piVal + " → int (rounded): " + piInt);
}
}
The following example demonstrates this:
dotnet script TypeConversion.cs === Implicit conversion === int → long: 9000 int → float: 9000 int → double: 9000 bigInt = 123456789 → float = 123456790 === Explicit cast === double: 8500.9 → int: 8500 long: 530000000 → int: 530000000 Overflow example: long 3000000000 → int -1294967296 'G' → int: 71 → char: 'G' === Convert class === string → int: 9000 int → string: 9000 double 3.14159 → int (rounded): 3
Common Mistakes
Assigning a literal to decimal without the m suffix
When assigning a numeric literal to a decimal variable, forgetting the m suffix causes a compile error. Numeric literals default to double, and implicit conversion from double to decimal is not allowed.
decimal price = 1980.50; // Error: cannot implicitly convert type 'double' to 'decimal'. decimal price = 1980.50m;
Similarly, float requires the f suffix.
float speed = 9.8; // Error: cannot implicitly convert type 'double' to 'float'. float speed = 9.8f;
Precision errors with float / double arithmetic
float and double store values in binary, so some decimal values cannot be represented exactly. Using double for monetary calculations can produce unexpected results.
double a = 0.1 + 0.2; Console.WriteLine(a == 0.3); // False (the result is 0.30000000000000004) // Use decimal for monetary calculations. decimal b = 0.1m + 0.2m; Console.WriteLine(b == 0.3m); // True
Summary
Among C#'s basic data types, int / long / double / float / decimal / char / bool are value types (the value itself is stored on the stack), while string is a reference type (the actual data is stored on the heap). string is a reference type, but because it is immutable, "modifying" a string internally creates a new string object. The original string assigned to a variable does not change, so you can work with strings as safely as with value types.
For floating-point numbers, use double (the default) or float (commonly used in Unity) depending on your needs. For financial calculations or any situation where rounding errors are unacceptable, always use decimal. Because float and double store values in binary, expressions like 0.1 + 0.2 can produce results such as 0.30000000000000004.
For type conversion, converting from a smaller type to a larger type (e.g., int → long) is done implicitly, but converting from a larger type to a smaller type (e.g., double → int) requires an explicit cast operator (type name). Casting a value that exceeds the int range causes overflow and produces an unexpected result. When you need a safe conversion, use the Convert class (which throws an exception on overflow) or a checked block. For conversions between string and numeric types, see also Convert.ToInt32(), int.Parse(), and int.TryParse().
If you find any errors or copyright issues, please contact us.