开发者

.Net Loading Generics using Reflection then Casting

This is a follow-up to this question. Only bother reading it if you're interested in the back-story.

In short, I am using reflection to load a set of generic types from an assembly. The assembly is loaded at run time.

I have a second assembly referenced both by my current project and the assembly I'm loading which contains the interfaces:

  • IJob
  • IJobWrapper(Of IJob)

The Assembly I'm loading (Call it Jobs.dll) contains a JobWrapper(Of IJob) - which implements the IJobWrapper interface.

Jobs.dll also contains multiple jobs which implement IJob

Now, I'm loading the appropriate Types into a container (Unity) and pulling them out when required. This works (that is to say, the container resolves the references as appropriate and instantiates an object)

NB: JobType and JobWrapperType below are retrieved via reflection.

Specifically:

        Dim TypeArgs As Type() = {JobType}
        Dim WrappedJob = JobWrapperType.MakeGenericType(TypeArgs)
        Dim ContainerJob = Container.Resolve(WrappedJob)
        Dim JobInstance = DirectCast(ContainerJob, IJobWrapper(Of IJob))

What's throwing an error here is the last line with the cast.

ContainerJob on line 3 is technically an object as I need to use the non-gener开发者_如何学运维ics overload to do the resolution (ie a Type parameter not an '(Of XXX)').

According to the debugger, however it's actually a MyProject.Jobs.JobWrapper(Of MyProject.Jobs.DailyStatusReport)

MyProject.Jobs.JobWrapper Implements IJobWrapper(Of IJob) MyProject.Jobs.DailyStatusReport Implements IJob

The DirectCast throws this exception:

Unable to cast object of type 'MyProject.Jobs.JobWrapper`1[MyProject.Jobs.DailyStatusReport]' to type 'MyProject.JobService.Common.IJobWrapper`1[MyProject.JobService.Common.IJob]'.

Can someone please explain why it can't do the cast / how to get around it? I'm wondering if it is having trouble matching the Interface defined in the referenced assembly with the one referenced from within Jobs.dll - But if that is the case, I'm unsure how to reconcile the two.

Many thanks.

EDIT:

Sample methods from the interfaces:

IJob:

Function ShouldExectute() As Boolean
Sub Execute()

IJobWrapper:

Function ShouldExectute() As Boolean
Sub Execute()
ReadOnly Property DatabaseId as Long
ReadOnly Property Name as String
ReadOnly Property IsDisabled As Boolean

Eg - The Job Wrapper doesn't ever return anythign with the type of the IJob. It does use the type information to look up various Attributes which have been attached to the class which implements IJob and read information.


It is not possible to perform this cast (with or without reflection) because generic type parameters are required to match exactly. So you could cast an AnyImplementationOfIJobWrapper(Of IJob) to IJobWrapper(Of IJob), but you can not cast an AnyImplementationOfIJobWrapper(Of AnyImplementationOfIJob) to IJobWrapper(Of IJob).

However, if you control the code for IJobWrapper, you can turn it into a covariant interface by declaring it as Interface IJobWrapper(Of Out IJob). This will allow you to perform the cast. It is the same mechanism that allows you to e.g. assign an IEnumerable(Of Subclass) to IEnumerable(Of Superclass).

(Feel free to correct any syntax mistakes I might have made; it's been a while since I used VB.)

A natural question would be "Why is not this cast allowed in the first place?" The reason is that it would lead to dangerous situations: Assume that you have an IList(Of Vehicle) vehicles. It seems natural that it should be possible to assign a List(Of Car) to vehicles, since a list of cars could be considered a list of vehicles. But if we allow vehicles to refer to what is truly a List(Of Car), we could try to add a Boat to vehicles, which should be allowed since vehicles appears to be a list of Vehicles and a Boat is a Vehicle. However, the actual list is restricted to only contain Cars, so we must either throw an exception (very unexpected for the user of vehicles) or insert the Boat into a list of Cars (creating a disaster waiting to happen). Solution: disallow the cast. IEnumerable (but not IList) can be made covariant because it only contains methods that lets you read from the collection. Therefore, an IEnumerable(Of Car) can be assigned to an IEnumerable(Of Vehicle) because IEnumerable only lets you read contents of the collection, not insert new objects into it.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜