.Net 使用反射加载泛型然后进行强制转换

发布于 2024-10-18 21:48:56 字数 2048 浏览 2 评论 0原文

这是此问题的后续问题。仅当您对背景故事感兴趣时才需要阅读它。

简而言之,我使用反射从程序集中加载一组泛型类型。该程序集在运行时加载。

我有第二个程序集,由我当前的项目和我正在加载的程序集引用,其中包含接口:

  • IJob
  • IJobWrapper(Of IJob)

我正在加载的程序集 (称之为 Jobs.dll)包含一个 JobWrapper(Of IJob) - 它实现了 IJobWrapper 接口。

Jobs.dll 还包含多个实现 IJob 的作业。

现在,我将适当的类型加载到容器 (Unity) 中,并在需要时将它们拉出。这是有效的(也就是说,容器适当地解析引用并实例化一个对象)

注意:下面的 JobTypeJobWrapperType 是通过反射检索的。

具体来说:

        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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

温折酒 2024-10-25 21:48:56

无法执行此转换(无论有或没有反射),因为泛型类型参数需要完全匹配。因此,您可以将 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 的列表,而 BoatVehicle。但是,实际列表仅限于包含 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) 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文