Collections are essential in any programming language for organizing, managing, and manipulating groups of objects. In C#, collections are divided into two types: generic collections and non-generic collections. Both types serve to hold multiple items, but they differ in functionality, performance, and type safety. Understanding these differences helps in making the right choice for optimal performance and maintainable code.
Collections in C# store and manage groups of related items, making data handling more convenient. Collections can be resized automatically, offer different ways to access data, and enable actions such as sorting, filtering, and searching.
Collections are primarily split into two main categories in C#:
Non-generic collections are part of the System.Collections
namespace in C#. They are flexible but lack type safety, which can lead to runtime errors. These collections work with objects, meaning they can store any data type. However, every item is treated as an object, and you need to cast types when accessing elements.
ArrayList is a resizable array. However, since it is non-generic, any type of object can be added.
using System;
using System.Collections;
ArrayList myList = new ArrayList();
myList.Add(10); // int
myList.Add("Hello"); // string
myList.Add(DateTime.Now); // DateTime
foreach (var item in myList)
{
Console.WriteLine(item);
}
A hashtable stores key-value pairs, where keys are unique. It’s similar to a dictionary, but it's non-generic.
Hashtable myTable = new Hashtable();
myTable.Add("ID", 1);
myTable.Add("Name", "Alice");
myTable.Add("Country", "USA");
Console.WriteLine(myTable["Name"]); // Output: Alice
Generic collections, found in the System.Collections.Generic
namespace, allow for type safety at compile time. They prevent runtime errors related to type mismatches, enhance performance, and are easier to maintain.
List<T>
is a strongly-typed, dynamic array that only stores elements of the specified type T
.
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
numbers.Add(6);
foreach (int number in numbers)
{
Console.WriteLine(number);
}
A dictionary stores key-value pairs, where each key is unique and strongly typed.
Dictionary<int, string> employeeNames = new Dictionary<int, string>();
employeeNames.Add(1, "Alice");
employeeNames.Add(2, "Bob");
Console.WriteLine(employeeNames[1]); // Output: Alice
Queues are useful for processing items in a first-in, first-out (FIFO) order. They’re commonly used in scenarios like task scheduling.
Queue<string> tasks = new Queue<string>();
tasks.Enqueue("Task1");
tasks.Enqueue("Task2");
tasks.Enqueue("Task3");
Console.WriteLine(tasks.Dequeue()); // Output: Task1
List<T>
: Dynamic arrays where you need type safety.Dictionary<TKey, TValue>
: Fast lookups with key-value pairs.Queue<T>
and Stack<T>
: Order-sensitive operations.Consider an e-commerce application where users add items to their shopping carts. Using generic collections such as List<T>
for a cart and Dictionary<TKey, TValue>
for inventory is ideal.
using System;
using System.Collections.Generic;
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public double Price { get; set; }
}
public class ShoppingCart
{
private List<Product> cartItems = new List<Product>();
public void AddProduct(Product product)
{
cartItems.Add(product);
Console.WriteLine($"{product.Name} added to the cart.");
}
public void RemoveProduct(int productId)
{
var product = cartItems.Find(p => p.ProductId == productId);
if (product != null)
{
cartItems.Remove(product);
Console.WriteLine($"{product.Name} removed from the cart.");
}
}
public void DisplayCart()
{
Console.WriteLine("Shopping Cart:");
foreach (var item in cartItems)
{
Console.WriteLine($"{item.Name} - ${item.Price}");
}
}
}
public class Inventory
{
private Dictionary<int, Product> productInventory = new Dictionary<int, Product>();
public void AddToInventory(Product product)
{
productInventory[product.ProductId] = product;
}
public Product GetProductById(int id)
{
return productInventory.ContainsKey(id) ? productInventory[id] : null;
}
}
public class Program
{
public static void Main()
{
Inventory inventory = new Inventory();
inventory.AddToInventory(new Product { ProductId = 1, Name = "Laptop", Price = 1500.00 });
inventory.AddToInventory(new Product { ProductId = 2, Name = "Smartphone", Price = 800.00 });
ShoppingCart cart = new ShoppingCart();
Product laptop = inventory.GetProductById(1);
Product phone = inventory.GetProductById(2);
if (laptop != null) cart.AddProduct(laptop);
if (phone != null) cart.AddProduct(phone);
cart.DisplayCart();
}
}
Inventory
uses a Dictionary<int, Product>
to store and retrieve products by their ID
.ShoppingCart
uses a List<Product>
to keep track of products added by a user.Inventory
dictionary enables quick product lookup by ProductId
, while the ShoppingCart
list allows adding and removing items easily.List<T>
, Dictionary<TKey, TValue>
, Queue<T>
, and Stack<T>
are widely used for type safety and performance.In C#, generic and non-generic collections provide diverse options for managing groups of objects. Non-generic collections offer flexibility, but their lack of type safety can lead to errors. Generic collections, on the other hand, enforce type safety, improve performance, and simplify code maintenance. By understanding the differences and appropriate use cases for each, you can write more efficient and reliable code. Whether you’re implementing a shopping cart, handling inventory, or processing tasks in order, C# collections are essential tools that contribute to clean, structured, and high-performing applications.