List<T>
class defines a generic array-like list that’s quite comparable to the older, nongeneric ArrayList
— but better! When you pull List<T>
off the shelf to instantiate your own list of, say, int
s, you replace T
with int
:List<int> myList = new List<int>(); // A list limited to ints
The versatile part is that you can instantiate List<T>
for any single data type (string
, Student
, BankAccount
, CorduroyPants
— whatever), and it’s still type-safe like an array, without nongeneric costs. It’s the superarray.
Generics come in two flavors in C#: the built-in generics, such as List<T>
, and a variety of roll-your-own items.
What’s so hot about generics? They excel for two reasons: safety and performance.
Generics are type-safe
When you declare an array, you must specify the exact type of data it can hold. If you specifyint
, the array can’t hold anything other than int
s or other numeric types that C# can convert implicitly to int
. You see compiler errors at build-time if you try to put the wrong kind of data into an array. Thus the compiler enforces type-safety, enabling you to fix a problem before it ever gets out the door.A compiler error beats the heck out of a runtime error. Compiler errors are useful because they help you spot problems now.
The old-fashioned nongeneric collections aren’t type-safe. In C#, everything IS_A Object
because Object
is the base type for all other types, both value-types and reference-types. But when you store value-types (numbers, chars
, bools
, and structs
) in a collection, they must be boxed going in and unboxed coming back out. It’s as though you’re putting items in an egg carton and having to stuff them inside the eggs so that they fit and then breaking the eggshells to get the items back out. (Reference-types such as string
, Student
, and BankAccount
don’t undergo boxing.)
ArrayList
because it’s hidden inside an Object
:ArrayList aList = new ArrayList();
// Add five or six items, then ...
string myString = (string)aList[4]; // Cast to string.
Fine, but the second consequence is this: You can put eggs in the carton, sure. But you can also add marbles, rocks, diamonds, fudge — you name it. An ArrayList
can hold many different types of objects at the same time. So it’s legal to write this:
ArrayList aList = new ArrayList();
aList.Add(“a string”); // string -- OK
aList.Add(3); // int -- OK
aList.Add(aStudent); // Student -- OK
However, if you put a mixture of incompatible types into an ArrayList
(or another nongeneric collection), how do you know what type is in, say, aList[3]?
If it’s a Student
and you try to cast it to string
, you get a runtime error. It’s just like Harry Potter reaching into a box of Bertie Botts’s Every Flavor Beans: He doesn’t know whether he’ll grab raspberry beans or earwax.
To be safe, you have to resort to using the is
operator or the alternative, the as
operator:
// See if the object is the right type, then cast it ...
if(aList[i] is Student) // Is the object there a Student?
{
Student aStudent = (Student)aList[i]; // Yes, so it’s safe to cast.
}
// Or do the conversion and see if it went well...
Student aStudent = aList[i] as Student; // Extract a Student, if present;
if(aStudent != null) // if not, “as” returns null.
{
// OK to use aStudent; “as” operator worked.
}
You can avoid all this extra work by using generics. Generic collections work like arrays: You specify the one and only type they can hold when you declare them.
Generics are efficient
Polymorphism allows the typeObject
to hold any other type, as with the egg carton analogy. But you can incur a penalty by putting in value-type objects — numeric, char
, and bool
types and struct
s — and taking them out. That’s because value-type objects that you add have to be boxed.Boxing isn’t worrisome unless your collection is big (although the amount of boxing going on can startle you and be more costly than you imagined). If you’re stuffing a thousand, or a million, int
s into a nongeneric collection, it takes about 20 times as long, plus extra space on the memory heap, where reference-type objects are stored. Boxing can also lead to subtle errors that will have you tearing your hair out. Generic collections eliminate boxing and unboxing.