Generics vs non-generics in C#

There are two main types of collections in C#: generic collections and non-generic collections. The primary difference lies in type safety, performance, and how they handle different data types.

Generic Collections

Generic collections are type-safe and hold elements of the same data type. They provide better performance as they avoid boxing and unboxing operations for value types.

Key Features

  • Type Safety: Compile-time type checking prevents runtime errors

  • Better Performance: No boxing/unboxing for value types

  • IntelliSense Support: Better code completion and error detection

Common Generic Collections

  • List<T> − Dynamic array of type T

  • Dictionary<TKey, TValue> − Key-value pairs with specific types

  • HashSet<T> − Unique elements of type T

Example

using System;
using System.Collections.Generic;

class GenericExample {
    public static void Main() {
        // List of integers only
        List<int> numbers = new List<int>();
        numbers.Add(10);
        numbers.Add(20);
        // numbers.Add("string"); // Compile-time error
        
        Console.WriteLine("Generic List:");
        foreach(int num in numbers) {
            Console.WriteLine(num);
        }
        
        // Dictionary with string keys and int values
        Dictionary<string, int> ages = new Dictionary<string, int>();
        ages["Alice"] = 25;
        ages["Bob"] = 30;
        
        Console.WriteLine("\nGeneric Dictionary:");
        foreach(var pair in ages) {
            Console.WriteLine(pair.Key + ": " + pair.Value);
        }
    }
}

The output of the above code is −

Generic List:
10
20

Generic Dictionary:
Alice: 25
Bob: 30

Non-Generic Collections

Non-generic collections can hold elements of different data types but lack type safety. They store elements as object type, requiring casting when retrieving values.

Key Features

  • Mixed Data Types: Can store different types in the same collection

  • Boxing/Unboxing: Value types are boxed, causing performance overhead

  • Runtime Errors: Type mismatches discovered only at runtime

Common Non-Generic Collections

  • ArrayList − Dynamic array that can hold any object type

  • Hashtable − Key-value pairs without type restrictions

  • BitArray − Array of boolean values represented as bits

Example

using System;
using System.Collections;

class NonGenericExample {
    public static void Main() {
        // ArrayList can hold different types
        ArrayList mixedList = new ArrayList();
        mixedList.Add(10);          // int
        mixedList.Add("Hello");     // string
        mixedList.Add(3.14);        // double
        mixedList.Add(true);        // bool
        
        Console.WriteLine("Non-Generic ArrayList:");
        foreach(object item in mixedList) {
            Console.WriteLine(item + " (Type: " + item.GetType().Name + ")");
        }
        
        // Hashtable with mixed key-value types
        Hashtable table = new Hashtable();
        table[1] = "One";
        table["Two"] = 2;
        table[3.0] = "Three";
        
        Console.WriteLine("\nNon-Generic Hashtable:");
        foreach(DictionaryEntry entry in table) {
            Console.WriteLine(entry.Key + ": " + entry.Value);
        }
    }
}

The output of the above code is −

Non-Generic ArrayList:
10 (Type: Int32)
Hello (Type: String)
3.14 (Type: Double)
True (Type: Boolean)

Non-Generic Hashtable:
3: Three
1: One
Two: 2

Comparison

Aspect Generic Collections Non-Generic Collections
Type Safety Compile-time type checking Runtime type checking only
Performance Better (no boxing/unboxing) Slower (boxing/unboxing required)
Mixed Types Not allowed Allowed
IntelliSense Full support with type information Limited support
Memory Usage More efficient Higher overhead

Type Safety Demonstration

Example

using System;
using System.Collections;
using System.Collections.Generic;

class TypeSafetyExample {
    public static void Main() {
        // Generic - Type safe
        List<string> genericList = new List<string>();
        genericList.Add("Hello");
        genericList.Add("World");
        
        // Direct access without casting
        string firstItem = genericList[0];
        Console.WriteLine("Generic: " + firstItem);
        
        // Non-Generic - Requires casting
        ArrayList nonGenericList = new ArrayList();
        nonGenericList.Add("Hello");
        nonGenericList.Add("World");
        
        // Casting required and potential runtime error
        string firstNonGeneric = (string)nonGenericList[0];
        Console.WriteLine("Non-Generic: " + firstNonGeneric);
        
        // This would cause runtime error if uncommented
        // nonGenericList.Add(123);
        // string wrongCast = (string)nonGenericList[2]; // Runtime exception
    }
}

The output of the above code is −

Generic: Hello
Non-Generic: Hello

Conclusion

Generic collections provide type safety, better performance, and enhanced development experience through compile-time checking. Non-generic collections offer flexibility for mixed data types but sacrifice type safety and performance. Modern C# development favors generic collections for their reliability and efficiency.

Updated on: 2026-03-17T07:04:35+05:30

6K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements