.Net 使用反射加载泛型然后进行强制转换
这是此问题的后续问题。仅当您对背景故事感兴趣时才需要阅读它。
简而言之,我使用反射从程序集中加载一组泛型类型。该程序集在运行时加载。
我有第二个程序集,由我当前的项目和我正在加载的程序集引用,其中包含接口:
IJob
IJobWrapper(Of IJob)
我正在加载的程序集 (称之为 Jobs.dll
)包含一个 JobWrapper(Of IJob)
- 它实现了 IJobWrapper
接口。
Jobs.dll
还包含多个实现 IJob
的作业。
现在,我将适当的类型加载到容器 (Unity) 中,并在需要时将它们拉出。这是有效的(也就是说,容器适当地解析引用并实例化一个对象)
注意:下面的 JobType
和 JobWrapperType
是通过反射检索的。
具体来说:
Dim TypeArgs As Type() = {JobType}
Dim WrappedJob = JobWrapperType.MakeGenericType(TypeArgs)
Dim ContainerJob = Container.Resolve(WrappedJob)
Dim JobInstance = DirectCast(ContainerJob, IJobWrapper(Of IJob))
这里抛出错误的是最后一行的强制转换。
第 3 行的 ContainerJob 技术上来说是一个对象,因为我需要使用非泛型重载来进行解析(即类型参数而不是“(Of XXX)”)。
但根据调试器,它实际上是一个 MyProject.Jobs.JobWrapper(Of MyProject.Jobs.DailyStatusReport)
MyProject.Jobs.JobWrapper
实现 IJobWrapper(Of IJob)
MyProject.Jobs.DailyStatusReport
实现 IJob
DirectCast 抛出此异常:
Unable to cast object of type 'MyProject.Jobs.JobWrapper`1[MyProject.Jobs.DailyStatusReport]' to type 'MyProject.JobService.Common.IJobWrapper`1[MyProject.JobService.Common.IJob]'.
有人可以解释一下为什么它不能进行转换/如何解决它吗?我想知道将引用的程序集中定义的接口与 Jobs.dll
中引用的接口进行匹配是否遇到问题 - 但如果是这样,我不确定如何协调两者。
非常感谢。
编辑:
接口中的示例方法:
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
例如 - Job Wrapper 不会返回 IJob 类型的任何内容。它确实使用类型信息来查找已附加到实现 IJob 的类的各种属性并读取信息。
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-generics 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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
无法执行此转换(无论有或没有反射),因为泛型类型参数需要完全匹配。因此,您可以将
AnyImplementationOfIJobWrapper(Of IJob)
转换为IJobWrapper(Of IJob)
,但不能将AnyImplementationOfIJobWrapper(Of AnyImplementationOfIJob)
转换为IJobWrapper(IJob)
。但是,如果您控制
IJobWrapper
的代码,则可以将其转换为 协变接口,将其声明为Interface IJobWrapper(Of Out IJob)
。这将允许您执行演员表。它是相同的机制,允许您将IEnumerable(Of Subclass)
分配给IEnumerable(Of Superclass)
。(请随意纠正我可能犯的任何语法错误;自从我使用 VB 以来已经有一段时间了。)
一个自然的问题是“为什么不允许这种强制转换?”原因是它会导致危险情况:假设您有一个
IList(Of Vehicle) 车辆
。似乎很自然,应该可以将List(Of Car)
分配给vehicles
,因为汽车列表可以被视为车辆列表。但是,如果我们允许vehicles
引用真正的List(Of Car)
,我们可以尝试将Boat
添加到Vehicles
,应该允许,因为vehicles
似乎是Vehicle
的列表,而Boat
是Vehicle
。但是,实际列表仅限于包含Car
,因此我们必须抛出异常(对于vehicles
的用户来说非常意外)或插入Boat
进入Car
列表(造成一场即将发生的灾难)。解决方案:禁止强制转换。IEnumerable
(但不是IList
)可以设为协变,因为它只包含允许您从集合中读取的方法。因此,IEnumerable(Of Car)
可以分配给IEnumerable(Of Vehicle)
,因为IEnumerable
只允许您读取集合的内容,不向其中插入新对象。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)
toIJobWrapper(Of IJob)
, but you can not cast anAnyImplementationOfIJobWrapper(Of AnyImplementationOfIJob)
toIJobWrapper(Of IJob)
.However, if you control the code for
IJobWrapper
, you can turn it into a covariant interface by declaring it asInterface IJobWrapper(Of Out IJob)
. This will allow you to perform the cast. It is the same mechanism that allows you to e.g. assign anIEnumerable(Of Subclass)
toIEnumerable(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 aList(Of Car)
tovehicles
, since a list of cars could be considered a list of vehicles. But if we allowvehicles
to refer to what is truly aList(Of Car)
, we could try to add aBoat
tovehicles
, which should be allowed sincevehicles
appears to be a list ofVehicle
s and aBoat
is aVehicle
. However, the actual list is restricted to only containCar
s, so we must either throw an exception (very unexpected for the user ofvehicles
) or insert theBoat
into a list ofCar
s (creating a disaster waiting to happen). Solution: disallow the cast.IEnumerable
(but notIList
) can be made covariant because it only contains methods that lets you read from the collection. Therefore, anIEnumerable(Of Car)
can be assigned to anIEnumerable(Of Vehicle)
becauseIEnumerable
only lets you read contents of the collection, not insert new objects into it.