Using ConvertAll to Imitate Native Covariance/Contravariance in C# Generics

A recent project at work required that we port a relatively large solution from the 1.1 to the 2.0 .NET framework. Overall the conversion went fairly smoothly, with most conflicts being namespace issues with classes that we had in our codebase which conflicted with new classes in the System API.

Once the conversion was completed, our methodology was to start implementing 2.0 specific language features like generics, anonymous methods, etc on an as-need basis or when we were implementing new code. It was during one of these attempts to utilize generics at a base level of our data access layer where I found a slight problem.

The problem was that in our 1.1 version of the code we heavily utilized a cached mechanism using reflection to determine parameterization values on all of our stored procedures. Basically at a core base level the DAL code could determine which return values in a SqlDataReader matched up with properties of our inner data classes (using a combination of custom attributes and some reflection), thus eliminating the need to ever write explicit code for input or output parameters on SQL calls. That is highly simplifying how this works, but in a nutshell that’s the context of this example.

Now, here is a simplified call from a DAL class which is specific to a particular class called “DerivedClass”. This business entity object contains an instance of another class called DerivedClassData whose sole purpose is as a transport for the actual data of that class.


        public ArrayList GetDerivedClasses(params object[] a_params)
        {
            return base.ReadMultipleRecords(
                "STORED_PROC", //SP
                typeof(DerivedClassData), //Type to return
                a_params);
        }

        public ArrayList ReadMultipleRecords(string spName,Type type, params object[] parameters)...

Basically the call passes in the SP name, the type of inner data class expected back from the query and a list of search parameters to give to the SP to limit the results. Inside the ReadMultipleRecords are a series of shared base methods which hammer out the details of how to call the SP, with the parameters given, create instances of the type passed in for returning the results and make sure the columns of the results match with the expected properties of the return type.

The downside to this method is the use of an ArrayList to return data. It was selected because it can be ordered, and it doesn’t care what “type” you fill it with. Inside the BuildRead method the instances are created at runtime based on the type passed in, so the code can literally return any number of hundreds if not thousands of types of data class objects in the ArrayList. As most are aware, this is possible by boxing and unboxing the reference types to the variant object type, which incurs a performance penalty. So let’s look at how we can use generics to speed this up a bit.


        public List<DerivedClass> GetDerivedClasses(params object[] a_params)
        {
            return base.ReadMultipleRecords(
            "STORED_PROC", //SP
            typeof(DerivedClassData), //Type to return
            a_params);
        }

While nice looking, unfortunately it isn’t quite as easy as changing the return type to a strongly typed generic implementation of ArrayList in the List<T> class. The problem lies in that we have to have a return type on the ReadMultipleRecords of type List<BaseData>. This is a strongly typed class, however the inner type T is parent class of our return class DerivedClass. Generics don’t allow covariance and contravariance, thus eliminating our ability to have an implicit conversion between the two in this case.

So, in order to solve this issue there is a dandy method which is implemented as part of the List<T> implementation called ConvertAll which will help us out. All we have to do is create a new instance of the Converter generic class, giving it the base and derived types to convert from and to respectively….as well as the address of a method which will actually perform the explicit cast.


        public List<DerivedClass> GetDerivedClasses(params object[] a_params)
        {
             return base.ReadMultipleRecords(
                  “STORED_PROC”, //SP
                  typeof(DerivedClassData), //Type to return
                  a_params).ConvertAll(new Converter<BaseData, DerivedClassData>(FromBase));
        }

        private static DerivedClassData FromBase(BaseData data)
        {
             return data as DerivedClassData;
        }

        public List<BaseData> ReadMultipleRecords(string spName, Type type, params object[] parameters)...

I’d much rather have generics support contra/co-variance inherently, however this work around isn’t all that bad and basically accomplishes the same thing. In this example any number of method calls to differing stored procedures which return the same type can reuse the same static method for casting, however one converter class will be needed per derived type.

Advertisements

5 Comments

Filed under ASP.NET, C#, General, Programming

5 responses to “Using ConvertAll to Imitate Native Covariance/Contravariance in C# Generics

  1. This is a shorter code sequence that has the same result:

    public List GetRooms()
    {
    return GetObjects(typeof(STRoom), “levelId”).ConvertAll(delegate(DALClass a) { return a as STRoom; });
    }

    But what about performance ?

  2. Ram Dash

    I wrote the following for List and Array coversion. It’ll work when

    1. TOUT derives from TIN but the list objects of type TOUT are boxed in type TIN (Unboxing) OR
    2. TIN derives from TOUT (Boxing)


    public class ListConverter
    where TIN : class
    where TOUT : class
    {
    public static List Convert(List inList)
    {
    return inList.ConvertAll(new Converter(Variant));
    }

    public static TOUT[] Convert(TIN[] inList)
    {
    return Array.ConvertAll(inList, new Converter(Variant));
    }

    private static TOUT Variant(TIN inObj)
    {
    return inObj as TOUT;
    }
    }

  3. Ram Dash

    Replace

    public class ListConverter

    with

    public class ListConverter<TIN, TOUT>

    It ate out that part in the first try.

  4. In C#3 this becomes simpler:

    //Where ClassB is derived from ClassA
    //Get list of A
    List listOfA = GetMyList();

    //Convert all to B
    List listOfB = listOfA.ConvertAll(
    (input) => input as ClassB );

    This achieves the same result, but with the same cost of essentially having two lists and having to run through them.

    I’d want to be able to do:

    List listOfB = listOfA as List;

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s