In general, when we want to create a collection of items, we make use of arrays due to their strongly typed nature. The only drawback which we experience is that they cannot grow in size. Their size depend upon their initialization.
Due to this reason, in dotnet v1.0, collections were introduced like ArrayList, Stack, Queue, Hashtable etc which are part of System.Collections namespace. Unlike arrays, they can grow in size but there is lack of type safety because they operate on an object data type. Since every type in dotnet directly or indirectly inherit from object class, you can add any data type in that collection like integer, string, float which makes program more error prone at runtime. Even if you add a same data type to it, then from a performance point of view it will be extremely slow because of unboxing.
Object is of reference type. Integer, bytes, float etc are value types. So, when you convert an object type to value type like integer, then unboxing happens.
Now, there was requirement of a collection which should have a type safety feature as well as can grow in size. So, in dotnet v2.0, generics were introduced like List <T>, Stack <T>, Queue <T> etc. where T stands for Type by naming convention . Generics are part of System.Collections.Generic namespace. In generics, if you want to create a list of integers, just create an instance of List<T> by replacing T with integer data type. But if you try to add any value which belongs to different data type like string or float, you will get a compile time error which ensures type safety.
Example of generic collection is given where we are using List<T> to store items.