泛型、接口和强制转换问题
Posted
技术标签:
【中文标题】泛型、接口和强制转换问题【英文标题】:Issue with generics, interfaces, and casting 【发布时间】:2013-06-06 12:51:24 【问题描述】:我最近为我实现的一些自定义用户控件添加了一个界面。界面非常基本。它有一种支持链接的方法:
Public Interface IMyInterface(Of T As WebControl)
Function DoSomething() As T
End Interface
实现也很基本:
Public Class MyCustomControl
Inherits CompositeControl
Implements IMyInterface(Of MyCustomControl)
Public Function DoSomething() As MyCustomControl _
Implements IMyInterface(Of MyCustomControl).DoSomething
' do stuff
Return Me
End Class
到目前为止一切正常。当我尝试遍历所有实现 IMyInterface
接口的控件集合时,就会出现问题,如下所示:
Dim myList = New List(Of IMyInterface(Of WebControl))
myList.Add(someCustomControl)
myList.ForEach(Sub(i) i.DoSomething())
someCustomControl
是一个MyCustomControl
,它实现了IMyInterface(Of MyCustomControl)
而不是IMyInterface(Of WebControl)
。
我在第二行(我尝试添加someCustomControl
)收到此错误:
Option Strict On 不允许从“MyCustomControl”到“IMyInterface(Of WebControl)”的隐式转换。
有什么办法可以绕过这个错误吗?我接近让它工作了,但我对泛型的了解还不够,无法超越这一点。
【问题讨论】:
当您尝试像Dim myList = New List(Of IMyInterface(Of WebControl))
一样声明 myList 时,请尝试使用 Dim myList = New List(Of Object)
或类似的东西。
@Vishal 我认为如果没有IMyInterface
类型,您将无法在ForEach
循环中的对象上调用DoSomething()
。
对不起,我不知道。
【参考方案1】:
协方差是 VS 2010 中引入的一种语言功能,可以解决您的问题。您需要定义您的泛型,使 T
类型前面有 Out
关键字:
公共接口 IMyInterface(Of Out T As WebControl) 函数 DoSomething() 作为 T 端接口
当您使用Out
关键字时,您使用的是协方差。它允许使用更多派生类型的泛型来代替具有基类型的泛型。因此,在您的情况下,它将允许在代码通常期望 IMyInterface(Of WebControl))
的地方使用 IMyInterface(Of MyCustomControl))
对象,例如您的 for
循环。
请注意,协方差有一个限制。协变类型T
只能用作函数返回值,不能用作函数(或子函数)的参数。例如,如果 IMyInterface
中的 DoSomething
签名看起来像这样,编译器会抱怨:
' Here the type T is used as an input param - compiler error
Sub DoSomething(ByVal sampleArg As T)
鉴于您的链接场景,我认为上述限制不是问题。
MSDN 上的更多信息:
Covariance and Contravariance Creating Variant Generic Interfaces【讨论】:
这正是我想要的。谢谢。【参考方案2】:我不知道你的函数 DoSomething
是做什么的,但我尝试在其中分配实例的 CssClass 以进行测试。
如下声明接口:
Public Interface IMyInterface(Of Out T As WebControl)
Function DoSomething() As T
End Interface
注意 Out T
参数。
创建2个实现接口的控件:
Public Class MyCustomControl1
Inherits CompositeControl
Implements IMyInterface(Of MyCustomControl1)
Public Function DoSomething() As MyCustomControl1 Implements IMyInterface(Of MyCustomControl1).DoSomething
' do stuff
Me.CssClass = "XXX"
Return Me
End Function
End Class
Public Class MyCustomControl2
Inherits CompositeControl
Implements IMyInterface(Of MyCustomControl2)
Public Function DoSomething() As MyCustomControl2 Implements IMyInterface(Of MyCustomControl2).DoSomething
' do stuff
Me.CssClass = "YYY"
Return Me
End Function
End Class
在测试页的 PageLoad 事件上:
Dim someCustomControl As New MyCustomControl1
Dim someCustomControl2 As New MyCustomControl2
Dim myList = New List(Of IMyInterface(Of WebControl))
myList.Add(someCustomControl)
myList.Add(someCustomControl2)
myList.ForEach(Sub(i) Literal1.Text &= i.DoSomething.CssClass & "<br />")
结果是,someCustomControl 和 someCustomControl2 的 CssClass
属性都设置为各自的值。
这说明接口函数DoSomething
调用成功,实例发生变化。
【讨论】:
Out
是我所缺少的。谢谢。【参考方案3】:
您需要在添加对象之前对其进行投射:
myList.Add(CType(someCustomControl, IMyInterface(Of WebControl)))
您可能还想考虑使接口不是通用的,并且您的“DoWork”方法返回类型作为接口本身。
Public Interface IMyInterface
Function DoSomething() As IMyInterface
End Interface
当您必须在接口定义中指定类型时,它会失去接口的力量(不必了解实现)。
【讨论】:
我希望能够在不破坏链接功能的情况下使其正常工作 如果返回的接口本身不满足您的链接功能,那么应该重新设计接口,或者不要使用接口。或者,您可以编写一个WebControl
的基类,其作用相同。以上是关于泛型、接口和强制转换问题的主要内容,如果未能解决你的问题,请参考以下文章
当两个类型参数在C#中实现公共接口时,如何将泛型强制转换为它实现的接口