Byon April 24, 2009 7:28 AM
Ran into an interesting problem with .NET and it’s Generics earlier today that really, really surprised me. The problem that I was trying to solve was fairly straightforward. I’m working on porting a large collection of Classic ASP Web Applications to a collection of ASP.NET MVC Web Applications, in the process upgrading the applications to more advanced web technology (and I’m not just talking about .NET).
One particular function that we deal with regularly is needing to authorize users for subsets of data, however, often the permissions of a user may differ between various applications. As such, I wanted to create a simple data type to help me display these authorizations. Now, I’m simply going to call the app App1. It has a database table called App1Authorizations which manages the special authorizations for this app.
This led to some an interesting compile-time type-casting error where .NET refused to upcast the App1Authorization class to the IAuthorization interface (a perfectly legal move). I fought this for about twenty minutes, trying to use LINQ to force the cast, before I finally found documentation describing why I was encountering the error.aspx#csharpgenericstopic5).
The C# compiler only lets you implicitly cast generic type parameters to Object, or to constraint-specified types, as shown in Code block 5. Such implicit casting is type safe because any incompatibility is discovered at compile-time.
Essentially, this means that you can only cast to Objects (the most basic class), or a class which you’ve explicitly told the compiler is a legitimate cast ahead of time, because (the article claims) that’s the only way to provide type safe compile-time checking.
I don’t buy this line. The compiler should already know the relationships between the Interface I’ve declared and it’s child, and it should be able to handle that at compile time. Now, I’m not trying to claim to be smarter than Anders Hejlsberg. It’s highly likely I’m missing something, but it seems to me that this should be a solvable problem.
Okay, so I don’t like that what appears like a perfectly reasonable use of generics doesn’t work. But, what does it take to make it work? Well, we have to go back to Generics, and add generics where I really didn’t want to.
The only other alternative would be to implement a custom List class which would allow the IAuthorization cast. That’s basically unreasonable. This works, but in my opinion it’s harder to read and slightly confusing. In short, I think this smells. It was non-obvious what was wrong, and then what the solution was.
It’s unclear to me whether this is an issue with the .NET Virtual Machine (like my problem with Java’s Generics are), or with the C# compiler, but my hopes are that this is a correctable problem (maybe I should crack open the Mono C# Compiler source).
Now, because I’m sure it will come up, there is a single reason supporting this that I can think of. As I said above, I am writing an MVC Application. The LINQ query is in my Model, which is called by my Controller, which is then passed via the ViewData to a view for display. By forcing me to do this extra bit of casting, I can ensure (with compile-time assurance) that the right kind of IAuthorization object can make it into the View.
As I said, there may be a perfectly good reason for this design decision that I’m not aware of. If there is, I’d love to hear it. Now that I’ve seen this, I know to look out for it. Do I wish that it could be different? Absolutely. But hopefully this write-up can help someone else with this problem.