C# Data Types
Introduction
Data types are a fundamental concept in programming that define what kind of data a variable can hold. In C#, as a strongly-typed language, every variable must have a specific data type that determines the size, range, and operations that can be performed on that variable. Understanding data types is crucial as it helps in efficient memory management, prevents errors, and makes your code more predictable.
In this tutorial, we'll explore the various data types available in C#, how to use them effectively, and best practices for working with them in real-world applications.
Built-in Data Types in C#
C# provides a rich set of built-in data types that can be categorized into:
- Value Types
- Reference Types
- Pointer Types (used mainly in unsafe code)
Let's dive into each category:
Value Types
Value types directly contain their data and are stored on the stack. When you assign a value type to another variable, a copy of the value is created.
Numeric Types
C# offers several numeric data types to handle different ranges of numbers:
Integer Types
// Integer types
byte myByte = 255; // 0 to 255 (unsigned 8-bit)
sbyte mySByte = -128; // -128 to 127 (signed 8-bit)
short myShort = -32768; // -32,768 to 32,767 (16-bit)
ushort myUShort = 65535; // 0 to 65,535 (unsigned 16-bit)
int myInt = 2147483647; // -2,147,483,648 to 2,147,483,647 (32-bit)
uint myUInt = 4294967295; // 0 to 4,294,967,295 (unsigned 32-bit)
long myLong = 9223372036854775807; // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 (64-bit)
ulong myULong = 18446744073709551615; // 0 to 18,446,744,073,709,551,615 (unsigned 64-bit)
// Output each value
Console.WriteLine($"byte value: {myByte}");
Console.WriteLine($"sbyte value: {mySByte}");
Console.WriteLine($"short value: {myShort}");
Console.WriteLine($"ushort value: {myUShort}");
Console.WriteLine($"int value: {myInt}");
Console.WriteLine($"uint value: {myUInt}");
Console.WriteLine($"long value: {myLong}");
Console.WriteLine($"ulong value: {myULong}");
Output:
byte value: 255
sbyte value: -128
short value: -32768
ushort value: 65535
int value: 2147483647
uint value: 4294967295
long value: 9223372036854775807
ulong value: 18446744073709551615
Floating-Point Types
Floating-point types are used to represent real numbers:
float myFloat = 3.14F; // 7-digit precision (32-bit) - note the 'F' suffix
double myDouble = 3.14159265359; // 15-16 digit precision (64-bit)
decimal myDecimal = 3.14159265359m; // 28-29 significant digits (128-bit) - note the 'm' suffix
Console.WriteLine($"float value: {myFloat}");
Console.WriteLine($"double value: {myDouble}");
Console.WriteLine($"decimal value: {myDecimal}");
Output:
float value: 3.14
double value: 3.14159265359
decimal value: 3.14159265359
When working with financial calculations, always use the decimal
type instead of float
or double
to avoid rounding errors that can occur in binary floating-point representations.
Boolean Type
The bool
type can hold one of two values: true
or false
:
bool isActive = true;
bool isComplete = false;
Console.WriteLine($"Is active: {isActive}");
Console.WriteLine($"Is complete: {isComplete}");
Output:
Is active: True
Is complete: False
Character Type
The char
type represents a Unicode UTF-16 character:
char myChar = 'A';
char unicodeChar = '\u0041'; // Unicode for 'A'
Console.WriteLine($"Character: {myChar}");
Console.WriteLine($"Unicode character: {unicodeChar}");
Output:
Character: A
Unicode character: A
Struct Types
Structs are value types that can contain multiple fields and methods:
struct Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
public override string ToString()
{
return $"({X}, {Y})";
}
}
// Using the struct
Point myPoint = new Point(10, 20);
Console.WriteLine($"Point coordinates: {myPoint}");
Output:
Point coordinates: (10, 20)
Enum Type
Enums define a set of named constants:
enum DaysOfWeek
{
Sunday = 0,
Monday = 1,
Tuesday = 2,
Wednesday = 3,
Thursday = 4,
Friday = 5,
Saturday = 6
}
DaysOfWeek today = DaysOfWeek.Wednesday;
Console.WriteLine($"Today is {today}");
Console.WriteLine($"Today's numeric value is {(int)today}");
Output:
Today is Wednesday
Today's numeric value is 3
Reference Types
Reference types store a reference to the memory location where the actual data is kept, typically on the managed heap.
String Type
The string
type represents a sequence of characters:
string greeting = "Hello, World!";
string emptyString = string.Empty; // Same as ""
string nullString = null;
string interpolatedString = $"The greeting is: {greeting}";
Console.WriteLine(greeting);
Console.WriteLine($"Length of greeting: {greeting.Length}");
Console.WriteLine(interpolatedString);
Output:
Hello, World!
Length of greeting: 13
The greeting is: Hello, World!
Object Type
object
is the base class for all other types in C#:
object myObject = 42; // Boxing an int
object stringObject = "text"; // Reference to a string
object dateObject = DateTime.Now;
Console.WriteLine($"Integer object: {myObject}");
Console.WriteLine($"String object: {stringObject}");
Console.WriteLine($"Date object: {dateObject}");
Output:
Integer object: 42
String object: text
Date object: 5/20/2023 2:15:30 PM (varies based on current date/time)
Array Type
Arrays can hold multiple values of the same type:
// Declaration and initialization of arrays
int[] numbers = { 1, 2, 3, 4, 5 };
string[] fruits = new string[3] { "Apple", "Banana", "Cherry" };
// Accessing array elements
Console.WriteLine($"First number: {numbers[0]}");
Console.WriteLine($"Second fruit: {fruits[1]}");
// Iterating through an array
Console.WriteLine("All numbers:");
foreach (int num in numbers)
{
Console.Write($"{num} ");
}
Output:
First number: 1
Second fruit: Banana
All numbers:
1 2 3 4 5
Class Type
Classes are the primary reference types in C#:
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
public override string ToString()
{
return $"{Name}, {Age} years old";
}
}
// Creating and using a class instance
Person person = new Person("John", 30);
Console.WriteLine(person);
Output:
John, 30 years old
Type Conversion
C# provides several ways to convert between data types:
Implicit Conversion
Occurs automatically when there's no risk of data loss:
int myInt = 100;
long myLong = myInt; // Implicit conversion from int to long
float myFloat = myInt; // Implicit conversion from int to float
Console.WriteLine($"Original int: {myInt}");
Console.WriteLine($"Converted to long: {myLong}");
Console.WriteLine($"Converted to float: {myFloat}");
Output:
Original int: 100
Converted to long: 100
Converted to float: 100
Explicit Conversion (Casting)
Required when there's potential for data loss:
double myDouble = 9.78;
int myInt = (int)myDouble; // Explicit conversion, fractional part is truncated
Console.WriteLine($"Original double: {myDouble}");
Console.WriteLine($"Converted to int: {myInt}");
Output:
Original double: 9.78
Converted to int: 9
Using Conversion Methods
C# provides built-in methods for type conversion:
string numberString = "123";
int parsedInt = int.Parse(numberString);
int convertedInt = Convert.ToInt32(numberString);
double potentialDouble = 123.45;
string doubleString = potentialDouble.ToString();
Console.WriteLine($"Original string: {numberString}");
Console.WriteLine($"Parsed to int: {parsedInt}");
Console.WriteLine($"Converted to int: {convertedInt}");
Console.WriteLine($"Double to string: {doubleString}");
Output:
Original string: 123
Parsed to int: 123
Converted to int: 123
Double to string: 123.45
TryParse Methods
For safer conversions that handle potential errors:
string validNumber = "123";
string invalidNumber = "abc";
if (int.TryParse(validNumber, out int result1))
{
Console.WriteLine($"Successfully parsed: {result1}");
}
else
{
Console.WriteLine("Parsing failed");
}
if (int.TryParse(invalidNumber, out int result2))
{
Console.WriteLine($"Successfully parsed: {result2}");
}
else
{
Console.WriteLine("Parsing failed");
}
Output:
Successfully parsed: 123
Parsing failed
Nullable Types
C# allows value types to be nullable using the ?
modifier:
// Nullable types
int? nullableInt = null;
bool? nullableBool = null;
double? nullableDouble = 3.14;
// Using nullable types
Console.WriteLine($"Nullable int has value: {nullableInt.HasValue}");
Console.WriteLine($"Nullable double value: {nullableDouble.GetValueOrDefault()}");
// Null-coalescing operator
int definiteInt = nullableInt ?? -1;
Console.WriteLine($"Definite int value: {definiteInt}");
Output:
Nullable int has value: False
Nullable double value: 3.14
Definite int value: -1
Var Keyword - Implicit Typing
The var
keyword allows C# to infer the type during compilation:
var message = "Hello"; // Inferred as string
var number = 42; // Inferred as int
var date = DateTime.Now; // Inferred as DateTime
Console.WriteLine($"message is of type: {message.GetType()}");
Console.WriteLine($"number is of type: {number.GetType()}");
Console.WriteLine($"date is of type: {date.GetType()}");
Output:
message is of type: System.String
number is of type: System.Int32
date is of type: System.DateTime
The var
keyword doesn't make C# dynamically typed. The type is still inferred at compile-time and can't change during runtime.
Real-World Examples
Building a Simple Calculator
double num1 = 10.5;
double num2 = 5.25;
// Basic operations
double sum = num1 + num2;
double difference = num1 - num2;
double product = num1 * num2;
double quotient = num1 / num2;
Console.WriteLine("Simple Calculator");
Console.WriteLine($"{num1} + {num2} = {sum}");
Console.WriteLine($"{num1} - {num2} = {difference}");
Console.WriteLine($"{num1} * {num2} = {product}");
Console.WriteLine($"{num1} / {num2} = {quotient}");
Output:
Simple Calculator
10.5 + 5.25 = 15.75
10.5 - 5.25 = 5.25
10.5 * 5.25 = 55.125
10.5 / 5.25 = 2
Product Inventory System
class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
public Product(string name, decimal price, int quantity)
{
Name = name;
Price = price;
Quantity = quantity;
}
public decimal CalculateValue()
{
return Price * Quantity;
}
}
// Creating a product inventory
Product[] inventory = new Product[]
{
new Product("Laptop", 1299.99m, 5),
new Product("Mouse", 25.50m, 20),
new Product("Keyboard", 45.99m, 10)
};
// Calculating total inventory value
decimal totalValue = 0;
foreach (Product product in inventory)
{
decimal productValue = product.CalculateValue();
totalValue += productValue;
Console.WriteLine($"{product.Name}: {product.Quantity} units at ${product.Price} = ${productValue}");
}
Console.WriteLine($"\nTotal inventory value: ${totalValue}");
Output:
Laptop: 5 units at $1299.99 = $6499.95
Mouse: 20 units at $25.50 = $510.00
Keyboard: 10 units at $45.99 = $459.90
Total inventory value: $7469.85
Temperature Converter
// Function to convert Celsius to Fahrenheit
double CelsiusToFahrenheit(double celsius)
{
return celsius * 9 / 5 + 32;
}
// Function to convert Fahrenheit to Celsius
double FahrenheitToCelsius(double fahrenheit)
{
return (fahrenheit - 32) * 5 / 9;
}
// Test the converter
double[] celsiusTemperatures = { 0, 25, 100 };
Console.WriteLine("Temperature Converter");
Console.WriteLine("---------------------");
foreach (double c in celsiusTemperatures)
{
double f = CelsiusToFahrenheit(c);
Console.WriteLine($"{c}°C = {f}°F");
}
Console.WriteLine("\nReverse conversion:");
double[] fahrenheitTemperatures = { 32, 77, 212 };
foreach (double f in fahrenheitTemperatures)
{
double c = FahrenheitToCelsius(f);
Console.WriteLine($"{f}°F = {c}°C");
}
Output:
Temperature Converter
---------------------
0°C = 32°F
25°C = 77°F
100°C = 212°F
Reverse conversion:
32°F = 0°C
77°F = 25°C
212°F = 100°C
Summary
In this tutorial, we've covered the essential data types in C#:
- Value Types: Including numeric types (integer and floating-point), boolean, character, struct, and enum types.
- Reference Types: Including strings, objects, arrays, and custom classes.
- Type Conversion: Both implicit and explicit conversions, as well as parsing methods.
- Nullable Types: How to declare and work with nullable value types.
- Var Keyword: Using implicit typing to make code more concise.
Understanding data types is fundamental to writing effective C# programs. Choosing the right data type for your variables ensures optimal memory usage, prevents potential errors, and makes your code more maintainable.
Additional Resources
Exercises
- Create a program that converts user input (as string) to different numeric data types and handles potential conversion errors.
- Implement a simple banking system that uses appropriate data types for account balances, customer names, and transaction dates.
- Create a temperature class that stores temperature in Celsius but provides properties to get and set the value in Fahrenheit and Kelvin.
- Design a program that demonstrates the differences between value types and reference types by showing how they behave when passed to methods.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)