C
C#

C to C#

10 lessons

Progress0%
1Variables & Types2Functions & Methods3Arrays & Lists4Structs → Classes5Memory Management6Strings7File I/O8Object-Oriented Programming9Generics and Collections10Async and Concurrency
All Mirror Courses
C
C#
Generics and Collections
MirrorLesson 9 of 10
Lesson 9

Generics and Collections

C# generics and type-safe collections vs C's void* and manual data structures

Introduction

In this lesson, you'll learn about generics and collections in C#. Coming from C, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.

Mirror Card
C
From C:

In C, you're familiar with c# generics and type-safe collections vs c's void* and manual data structures.

C#
In C#:

C# has its own approach to c# generics and type-safe collections vs c's void* and manual data structures, which we'll explore step by step.

The C# Way

Let's see how C# handles this concept. Here's a typical example:

C#
C# Example
using System.Collections.Generic;

// Generic method (type-safe swap)
void Swap<T>(ref T a, ref T b) { (a, b) = (b, a); }

int x = 1, y = 2;
Swap(ref x, ref y); // x=2, y=1

// Generic class
class Box<T>
{
    public T Value { get; }
    public Box(T v) => Value = v;
}

// Constraints
T Max<T>(T a, T b) where T : IComparable<T>
    => a.CompareTo(b) >= 0 ? a : b;

// Generic collections (type-safe dynamic arrays, maps, sets)
var nums = new List<int> { 1, 2, 3 };
nums.Add(4);
nums[2];         // 3
nums.Contains(2); // true

var dict = new Dictionary<string, int>();
dict["alice"] = 30;
dict.TryGetValue("bob", out int age); // safe get

var set = new HashSet<string>();
set.Add("a"); set.Add("a"); // dedup
set.Count; // 1

// LINQ on any collection
var evens = nums.Where(x => x % 2 == 0).ToList();
var sum   = nums.Sum();

Comparing to C

Here's how you might have written similar code in C:

C
C (What you know)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Generic via void* — no type safety
void swap(void *a, void *b, size_t size) {
    char tmp[size];
    memcpy(tmp, a, size);
    memcpy(a, b, size);
    memcpy(b, tmp, size);
}

// Dynamic array (no generics — must duplicate for each type)
typedef struct {
    int *data;
    int len, cap;
} IntArray;

void push(IntArray *arr, int val) {
    if (arr->len == arr->cap) {
        arr->cap *= 2;
        arr->data = realloc(arr->data, arr->cap * sizeof(int));
    }
    arr->data[arr->len++] = val;
}

// Hash map would need another manual implementation
// No type safety — wrong type = runtime crash
Mirror Card
C
From C:

You may be used to different syntax or behavior.

C#
In C#:

C# generics are compile-time type-safe; C's void* gives no type checking

Mirror Card
C
From C:

You may be used to different syntax or behavior.

C#
In C#:

List<T> = C's IntArray but works for any T — no code duplication

Mirror Card
C
From C:

You may be used to different syntax or behavior.

C#
In C#:

Dictionary<K,V> = hash table built-in; C needs manual implementation

Mirror Card
C
From C:

You may be used to different syntax or behavior.

C#
In C#:

Constraints (where T : IComparable<T>) enable operations on T

Mirror Card
C
From C:

You may be used to different syntax or behavior.

C#
In C#:

LINQ extends all IEnumerable<T> collections with filter/map/reduce

Step-by-Step Breakdown

1. Generic Methods

C# generic methods work for any type T while preserving type safety. No void* casting, no runtime crashes from wrong types.

C
C
void swap(void *a, void *b, size_t size) { ... }
C#
C#
void Swap<T>(ref T a, ref T b) {
    (a, b) = (b, a);
}
int x=1, y=2;
Swap(ref x, ref y); // type-safe: no void*

2. List<T>

List<T> is a generic dynamic array — like C's resizing array but works for any type without code duplication.

C
C
IntArray arr; // must rewrite for each type
FloatArray arr2; // duplicate code
C#
C#
var nums  = new List<int>();
var names = new List<string>();
// Same List<T>, different T — no duplication
nums.Add(1); names.Add("Alice");
nums[0];     // type-safe: returns int

3. Dictionary<K,V>

Dictionary<K,V> is a built-in hash map. TryGetValue safely handles missing keys — no error codes.

C
C
// C: manual hash table implementation ~100 lines
C#
C#
var d = new Dictionary<string, int>();
d["key"] = 42;
if (d.TryGetValue("key", out int v))
    Console.WriteLine(v);
d.ContainsKey("key"); // true
d.Remove("key");

4. LINQ

LINQ adds functional methods to all IEnumerable<T> collections — like Python's list comprehensions but for any collection type.

C
C
// C: manual loops
int sum = 0;
for(int i=0;i<n;i++) if(arr[i]%2==0) sum+=arr[i];
C#
C#
var evens = nums.Where(x => x % 2 == 0);
var sum   = evens.Sum();
var top3  = nums.OrderByDescending(x=>x).Take(3);
var names = people.Select(p => p.Name).ToList();

Common Mistakes

When coming from C, developers often make these mistakes:

  • C# generics are compile-time type-safe; C's void* gives no type checking
  • List<T> = C's IntArray but works for any T — no code duplication
  • Dictionary<K,V> = hash table built-in; C needs manual implementation
Common Pitfall
Don't assume C# works exactly like C. While the concepts may be similar, the syntax and behavior can differ significantly.

Key Takeaways

  • Generic methods <T> replace void* — type-safe at compile time
  • List<T> replaces manual dynamic array — no rewrite per type
  • Dictionary<K,V> replaces manual hash table; TryGetValue for safe access
  • LINQ: Where/Select/OrderBy/Sum on any IEnumerable<T>
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your C code in C# to practice these concepts.
PreviousNext