这是this question的后续行动。如果你对背景故事感兴趣,那就麻烦阅读。
简而言之,我使用反射从程序集中加载一组泛型类型。程序集在运行时加载。
我有一个第二个程序集,由我当前的项目和我正在加载的包含接口的程序集引用:
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
在技术上是一个对象,因为我需要使用非泛型重载来执行解析(即Type参数不是'(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和读取信息的类的各种属性。
答案 0 :(得分:2)
无法执行此强制转换(使用或不使用反射),因为要求通用类型参数完全匹配。因此,您可以将AnyImplementationOfIJobWrapper(Of IJob)
投射到IJobWrapper(Of IJob)
,但无法将AnyImplementationOfIJobWrapper(Of AnyImplementationOfIJob)
投射到IJobWrapper(Of IJob)
。
但是,如果您控制IJobWrapper
的代码,则可以将其声明为Interface IJobWrapper(Of Out IJob)
,将其变为covariant interface。这将允许您执行演员表。这是允许你进行相同的机制。将IEnumerable(Of Subclass)
分配给IEnumerable(Of Superclass)
。
(请随意纠正我可能犯的任何语法错误;自从我使用VB以来已经有一段时间了。)
一个自然的问题是“为什么不首先允许这种演员?”原因是它会导致危险的情况:假设你有一个IList(Of Vehicle) vehicles
。似乎很自然的是,应该可以将List(Of Car)
分配给vehicles
,因为汽车列表可以被视为车辆列表。但是,如果我们允许vehicles
引用真正属于List(Of Car)
的内容,我们可以尝试将Boat
添加到vehicles
,自vehicles
以来应该允许Vehicle
似乎是Boat
的列表,而Vehicle
是Car
。但是,实际列表仅限于包含vehicles
,因此我们必须抛出异常(Boat
的用户非常意外)或将Car
插入IEnumerable
列表中{1}} s(造成等待发生的灾难)。解决方案:禁止演员。 IList
(但不是IEnumerable(Of Car)
)可以变为协变,因为它只包含允许您从集合中读取的方法。因此,可以将IEnumerable(Of Vehicle)
分配给IEnumerable
,因为{{1}}只允许您阅读集合的内容,而不是将新对象插入其中。