在这种情况下,使用仅在运行时已知的类型的泛型有什么更好的选择?
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
您没有解释 Shape
和 TextRange
(以及您可能感兴趣的任何其他 MS 界面)的共同点,
你也没有解释你的代码将对这些对象采取什么样的行动。
真可惜,因为这意味着我必须用一些假设的例子来说明我的观点。
让我们假设 Shape
和 TextRange
有以下共同点:
这使得我的通用界面:
Public Interface IOfficeObject
ReadOnly Property Application As Application
Sub Copy()
End Interface
很遗憾,您无法控制 Shape
和 TextRange
;它们是微软定义的接口。
显然,微软并没有费心去定义一个共同的接口,让 Shape
和 TextRange
都继承。
我不知道为什么;我不熟悉这些界面或它们的历史。
这可能是一个疏忽,变成了遗产。
您可以使用 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))
(如果您更喜欢工厂而不是 ShapeAdapter
和 TextRangeAdapter
的显式实例化,那当然没问题。)
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
如果消费者需要来自 Shape
或 TextRange
的任何东西,但接口 and/or 未公开,适配器未实现,
那么这就是需要在接口和适配器中修复的东西。
如果它不能被适配器实现,那么你的架构就有缺陷。
现在我只能将 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
您没有解释 Shape
和 TextRange
(以及您可能感兴趣的任何其他 MS 界面)的共同点,
你也没有解释你的代码将对这些对象采取什么样的行动。
真可惜,因为这意味着我必须用一些假设的例子来说明我的观点。
让我们假设 Shape
和 TextRange
有以下共同点:
这使得我的通用界面:
Public Interface IOfficeObject
ReadOnly Property Application As Application
Sub Copy()
End Interface
很遗憾,您无法控制 Shape
和 TextRange
;它们是微软定义的接口。
显然,微软并没有费心去定义一个共同的接口,让 Shape
和 TextRange
都继承。
我不知道为什么;我不熟悉这些界面或它们的历史。
这可能是一个疏忽,变成了遗产。
您可以使用 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))
(如果您更喜欢工厂而不是 ShapeAdapter
和 TextRangeAdapter
的显式实例化,那当然没问题。)
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
如果消费者需要来自 Shape
或 TextRange
的任何东西,但接口 and/or 未公开,适配器未实现,
那么这就是需要在接口和适配器中修复的东西。
如果它不能被适配器实现,那么你的架构就有缺陷。