Casting Rules

by Aeden Jameson on 11/2/2007 8:57:00 AM Suppose I have the follwing


Public Interface IEntity

End Interface


Public Class TestEntity
    Implements IEntity

End Class


Public Interface IRepository(Of T As {IEntity})

End Interface


Public Interface IConcreteRepository
    Inherits IRepository(Of TestEntity)
End Interface

Public Class ConcreteRepository
    Implements IConcreteRepository
End Class


Public Class Factory
    Public Shared Function Create(Of TRepository As {Class,
IRepository(Of TEntity)}, TEntity As {IEntity})()
        Return New ConcreteRepository()
    End Function
End Class

I get casting exception on the line

        Dim Repository As IRepository(Of IEntity) = Factory.Create(Of
IConcreteRepository, TestEntity)()


What am I missing?


The exception states

    Unable to cast object of type 'ConcreteRepository' to type
'IRepository`1[IEntity]'.


Cheers,
Aeden

 

Re: Casting Rules

by Peter Duniho on 11/2/2007 9:40:00 AM On 2007-11-02 15:57:23 -0700, Aeden Jameson <aeden.jameson@gmail.com> said:

> [...]
> I get casting exception on the line
>
> Dim Repository As IRepository(Of IEntity) = Factory.Create(Of
> IConcreteRepository, TestEntity)()
>
> What am I missing?
>
> The exception states
>
> Unable to cast object of type 'ConcreteRepository' to type
> 'IRepository`1[IEntity]'.

I believe this is because generics don't support covariant generics.
Your ConcreteRepository class implements IConcreteRepository, which
inherits IRepository<TestEntity>, and not IRepository<IEntity>. You
would need the latter in order to successfully perform the cast.

The reasoning behind this has to do with what a generic class _could_
do with the concrete type for the class. In particular, if you could
cast down to a base class like that, then the possibility would arise
that something in the generic class would replaces some instance of
TestEntity with some instance of a different IEntity-derived class,
which would cause a problem later in the code that had the
TestEntity-specific concrete class.

By requiring the generic type parameter to always be the actual type,
this sort of problem is avoided. It does lead to less-flexible code
such as in the example you're running into, but along with that comes
compile-time type safety.

If you don't want compile-time type safety, you should either not use
generics, or make the IRepository<> generic use IEntity instead of
TestEntity.

Pete