在这种情况下,使用仅在运行时已知的类型的泛型有什么更好的选择?

What is a better alternative to using generics with types only known at runtime in this case?

现在我只能将 Jumper class 用于“Something”类型的对象。我尝试将其重写为通用 class 但 运行 在实际从我的 SomeForm class 调用它时遇到困难,因为“T”可能是我只在运行时知道的几种类型之一。我读到这意味着我基本上是在与整个泛型语言设计作斗争。

示例代码是 VB,但我也可以使用 C# 答案。

问题:这可以用泛型重新设计吗?或者在我的情况下什么是泛型的更好替代品?

Public Class Jumper
    Private _enumeratorForwards As IEnumerator(Of Something)

    Public Sub JumpNext(functions As FunctionCombi)
        Forwards(functions.SelectorFunc)
    End Sub

    Private Iterator Function Forwards(selectorFunc As Func(Of Something, Boolean)) As IEnumerable(Of Something)

End Class

Public Class FunctionCombi
    Public Property SelectorFunc As Func(Of Something, Boolean)

    Sub New(_selectorFunc As Func(Of Something, Boolean))
        SelectorFunc = _selectorFunc
    End Sub

End Class

Public Class SomeForm

    'x and y would be different types
    Private _functionCombis As New Dictionary(Of SomeEnum, FunctionCombi) From {
                                                   {SomeEnum.A, New FunctionCombi(Function(x) RunSomeFunction(x)},
                                                   {SomeEnum.B, New FunctionCombi(Function(y) RunSomeOtherFunction(y))}

    Private Sub SomeHandler(sender as object, e as EventArgs)
        For i = 1 To [Enum].GetValues(GetType(SomeEnum)).Length

            'The type I would need here differs and I only know it at runtime

            Dim functionInfo As FunctionCombi = Nothing
            If Not _functionCombis.TryGetValue(i, functionInfo) Then Continue For
            Dim jumper As Jumper = sender.Tag(2)
        Next
    End Sub
End Class

泛型很可能不是您所需要的,因为它们已在 编译时 中解决。 对于要在 运行时 解析的不同类型,请使用接口。

如果一个人可以完全控制所涉及的类型,那就意味着:定义一个接口并让两种类型都实现该接口。

Public Interface IOfficeObject
    ' any members that Shape and TextRange have in common
End Interface

Public Interface Shape
    Inherits IOfficeObject

    ' any members specific for Shape
End Interface

Public Interface TextRange
    Inherits IOfficeObject

    ' any members specific for TextRange
End Interface

然后 Jumper 将只与该接口对话,而不用关心底层实现。 Jumper 不应该 关心那些潜在的 classes,否则它会违背将两个 classes 放在同一个 IEnumerator.

Public Class Jumper
    Private _enumeratorForwards As IEnumerator(Of IOfficeObject)

    ...
End Class

您没有解释 ShapeTextRange(以及您可能感兴趣的任何其他 MS 界面)的共同点, 你也没有解释你的代码将对这些对象采取什么样的行动。 真可惜,因为这意味着我必须用一些假设的例子来说明我的观点。

让我们假设 ShapeTextRange 有以下共同点:

  • a 属性 Application 类型 Application,类似于 this
  • 一个方法Copy,类似于this

这使得我的通用界面:

Public Interface IOfficeObject
    ReadOnly Property Application As Application
    Sub Copy()
End Interface

很遗憾,您无法控制 ShapeTextRange;它们是微软定义的接口。 显然,微软并没有费心去定义一个共同的接口,让 ShapeTextRange 都继承。 我不知道为什么;我不熟悉这些界面或它们的历史。 这可能是一个疏忽,变成了遗产。

您可以使用 adapter pattern 来解决该问题。

Public Class ShapeAdapter
    Implements IOfficeObject

    Private ReadOnly _shape As Shape

    Public Sub New(shape As Shape)
        _shape = shape
    End Sub

    Public ReadOnly Property Application As Application
        Get
            Return _shape.Application
        End Get
    End Property

    Public Sub Copy()
        _shape.Copy()
    End Sub

    ' Any other members, forwarding to _shape
End Class

Public Class TextRangeAdapter
    Implements IOfficeObject

    Private ReadOnly _textRange As TextRange

    Public Sub New(textRange As TextRange)
        _textRange = textRange
    End Sub

    Public ReadOnly Property Application As Application
        Get
            Return _textRange.Application
        End Get
    End Property

    Public Sub Copy()
        _textRange.Copy()
    End Sub

    ' Any other members, forwarding to _textRange
End Class

生成可枚举的对象涉及将每个对象包装在其适配器中 class。示例:

Dim listOfOfficeObjects As New List(Of IOfficeObject)
listOfOfficeObjects.Add(New ShapeAdapter(shape1))
listOfOfficeObjects.Add(New TextRangeAdapter(textRange1))
listOfOfficeObjects.Add(New ShapeAdapter(shape2))
listOfOfficeObjects.Add(New TextRangeAdapter(textRange2))

(如果您更喜欢工厂而不是 ShapeAdapterTextRangeAdapter 的显式实例化,那当然没问题。)

Class Jumper 将检索这些适配器。 消费者将始终通过接口 IOfficeObject 与这些适配器对话。 在您自己的代码示例中,我认出了两个消费者,但我相信您正在处理的解决方案中会有更多消费者。

Public Function RunSomeFunction(obj As IOfficeObject) As Boolean
    ' pulling whatever is necessary from IOfficeObject to come to a return value
End Function

Public Function RunSomeOtherFunction(obj As IOfficeObject) As Boolean
    ' likewise
End Function

如果消费者需要来自 ShapeTextRange 的任何东西,但接口 and/or 未公开,适配器未实现, 那么这就是需要在接口和适配器中修复的东西。 如果它不能被适配器实现,那么你的架构就有缺陷。