泛型、接口和强制转换问题

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 的基类,其作用相同。

以上是关于泛型、接口和强制转换问题的主要内容,如果未能解决你的问题,请参考以下文章

JAVA泛型

泛型和强制转换 - 不能将继承的类强制转换为基类

当两个类型参数在C#中实现公共接口时,如何将泛型强制转换为它实现的接口

泛型:强制转换和值类型,为啥这是非法的?

未经检查的强制转换:尝试在同一方法中将 Int 或 String 强制转换为 T(泛型类型)

c#中泛型集合怎样写强制类弄转换