.NET Makes Me Mad (Generics and Collections edition)

February 1, 2007 - 4 minute read -
software-development csharp

Ok, so I've decided I need to rant a little bit about .NET. This ends up in part being, "What I like about Java that I don't like about C#". I think this is fair though. It's not like C# and .NET were developed in a vacuum. It's not like C# is the first Object Oriented, VM run language. As such I think it's fair to point out where they should have learned from others.

Generics Don't Fully Support Covariant Types

Generic collections in .NET can only handle a single type of object well. You can add a sub-type to a Collection, but if you have two Collections with covariant types, you can not mix them without jumping through hoops.

Example

The simple case of adding a single Covariant type works, but when dealing with a Generic Collection of covariant types, it does not.
abstract class Vehicle {
}</p>
<p>class Car : Vehicle {
}</p>
<p>class MotorCycle : Vehicle {
}</p>
<p>List<Vehicle> vehicles = new List<Vehicle>();
vehicles.Add(new Car());    // This is OK</p>
<p>List<MotorCycle> motorCycles = LoadMotorCycles();
vehicles.AddRange(motorCycles);    // This does not work!

To make it work with AddRange, you have to perform a manual conversion

public IList<Vehicle> AllVehicles
{
    get
    {
         List<Vehicle> vehicleList = new List<Vehicle>();
         vehicleList
             .AddRange(AllCars.ConvertAll(
                 new Converter<Car, LocationContainer>(ToVehicle)));
         vehicleList
             .AddRange(AllCycles.ConvertAll(
                new Converter<MotorCycle, LocationContainer>(ToVehicle)));
         return vehicleList ;
    }
}</p>
<p>private static Vehicle ToVehicle<T>(T vehic) where T : Vehicle
{
    return vehic;
}

What's Good About .NET Generics

.NET Generic type information is available at runtime. In Java Generics are implemented as an erasure. Basically this means all of the type checking is done at compile time. The compiler then inserts explicit casts into the code for you. At runtime the code looks the same as if generics were never used. .NET chose not to use erasures but to make the type information available at runtime. This is generally more efficient and less prone to errors or problems with reflections. So good work there.

Note: (The Java folks did this so as not to break backwards compatibility. I think that major revisions should be allowed to break backwards compatibility when there are compelling reasons to do so.)

.NET Collections

Are collections classes such a mysterious art?

.NET does not have a Set or a Bag. These are generally useful and very common collections. A Set guarantees the uniqueness of elements in List like interface. A Bag can contain any objects. The unique thing about it is that it keeps a count of the same objects.

Example of a Bag

Bag fruitBag = new Bag();
Banana b = new Banana();
Apple a = new Apple();
fruitBag.Add(b);
fruitBag.Add(b);
fruitBag.Add(a);</p>
<p>int bananas = fruitBag.GetCount(b);

The SortedList and the SortedDictionary both have a Dictionary interface. Why wouldn't the SortedList have an, uhh maybe, a List interface?

The IList interface is so anemic as to be basically worthless. IList does not even have an AddRange method (or an AddAll) to merge the values in one collection into another. It's so limited that it makes it very hard to return interfaces from classes which is a good idea to encapsulate the implementation details of methods.

What Do You Think

Do you have things about .NET that annoy you? If so, leave a comment and let me know what it is.