如何在VB.net中模仿Java的通配符类型?

时间:2015-08-10 11:42:14

标签: vb.net generics

我有一个我这样定义的界面:

Public Interface ISomething(Of T)
' methods
End Interface

我现在做了一个实现:

Public Class ConcreteThing
Implements ISomething(of SomeClass)
' Implementation
End Class

我有多个这样的具体实现,并希望有一个函数根据其参数返回它们中的任何一个。在Java中,我会做这样的事情:

public ISomething<?> getSomething(ParamType p) {
    if(p.hasFoo()) return new ConcreteThing();
    if(p.hasBar()) return new OtherConcreteThing();
    throw new IllegalStateException("p neither has Foo nor Bar");
}

我已经搜索过这个问题并发现VB.net没有通配符类型,所以我尝试了:

Public Function GetSomething(p as ParamType) as ISomething(Of Object)
    If p.HasFoo Then Return New ConcreteThing()
    If p.HasBar Then Return New OtherConcreteThing()
    Throw New InvalidOperationException("p neither has Foo nor Bar")
End Function

编译,但我收到警告:Runtime errors might occurr when converting 'Foo.ConcreteThing' to 'Foo.ISomething(Of Object)'

当我尝试以下内容时,如同类似问题所示:

Public Function GetSomething(Of T)(p as ParamType) as ISomething(Of T)
    If p.HasFoo Then Return New ConcreteThing()
    If p.HasBar Then Return New OtherConcreteThing()
    Throw New InvalidOperationException("p neither has Foo nor Bar")
End Function

警告只会更改为Runtime errors might occurr when converting 'Foo.ConcreteThing' to 'Foo.ISomething(Of T)'

那么,我该如何做到这一点?或者,如果这确实是正确的,我如何让Visual Studio忽略此警告?

2 个答案:

答案 0 :(得分:1)

我对这个问题进行了一些调查,与同事讨论过,我认为我找到了警告的解决方案/原因。

警告信息有点难以理解和不确定。他们试图说的是,尽管听起来很傻,但即使使用Out关键字,协方差也不能像原始类型那样有效!

考虑摘自this example on MSDN

    ' Covariance.  
    Dim strings As IEnumerable(Of String) = New List(Of String)()
    ' An object that is instantiated with a more derived type argument  
    ' is assigned to an object instantiated with a less derived type argument.  
    ' Assignment compatibility is preserved.  
    Dim objects As IEnumerable(Of Object) = strings

这很有效。现在,将第一个IEnumerable更改为IList

    Dim strings As IList(Of String) = New List(Of String)()
    Dim objects As IEnumerable(Of Object) = strings

也适用。好的,我们很幸运,让我们改变第二个:

    Dim strings As IList(Of String) = New List(Of String)()
    Dim objects As IList(Of Object) = strings

热潮,InvalidCastException。查看签名,这是因为IEnumerable中的通用参数定义为Of Out T,而IList仅定义为As T

现在,让我们定义自己的。

Interface ISomething(Of Out T)
    ReadOnly Property Value As T
End Interface

Class IntThing
    Implements ISomething(Of Integer)

    Public ReadOnly Property Value As Integer Implements ISomething(Of Integer).Value
        Get
            Return 42
        End Get
    End Property
End Class

现在,这样做:

Dim s1 As ISomething(Of Integer) = new IntThing()

作品。现在添加:

Dim s2 As ISomething(Of Object) = s1

热潮,InvalidCastException。现在,最有趣的部分。添加ISomething的第二个实现:

Class StringThing
    Implements ISomething(Of String)

    Public ReadOnly Property Value As String Implements ISomething(Of String).Value
        Get
            Return "foo"
        End Get
    End Property
End Class

并做:

Dim s1 As ISomething(Of String) = New StringThing()
Dim s2 As ISomething(Of Object) = s1

另一方面,这是有效的!那么,让我们回到List示例。

Dim ints As IEnumerable(Of Integer) = New List(Of Integer)()
Dim objects As IEnumerable(Of Object) = ints

这也会给你一个InvalidCastException

所以,我的结论是协方差不仅需要Out关键字,而且还只适用于非基本类型。 .net似乎以与JVM不同的方式处理包装类。

所以,当它弹出时,永远不要忽略这个警告。当它发生时,事情将以绝对不合逻辑的方式变得难以置信!这意味着,对于我想要实现的目标,使用简单的Object而不是试图找到ISomething<?>的等价物是可行的方法。 我只是在内部使用它来读取二进制文件到一个更方便的结构来提取我最后通过API传递的数据,所以使用Object并不会使事情变得更糟。

答案 1 :(得分:0)

很奇怪,我没有像你那样得到警告。但是如果我尝试运行代码,我会得到InvalidCastException

要摆脱错误(并希望您的警告也是如此),您可以在T协变上制作通用类型ISomething

Public Interface ISomething(Of Out T) ' Add the "Out" keyword here to make it covariant
' methods
End Interface

然后您应该可以像尝试一样使用GetSomething功能:

Public Function GetSomething(p as ParamType) as ISomething(Of Object)
    If p.HasFoo Then Return New ConcreteThing()
    If p.HasBar Then Return New OtherConcreteThing()
    Throw New InvalidOperationException("p neither has Foo nor Bar")
End Function

相关文档:Covariance and Contravariance in Generics

  

<强>协方差

     

允许您使用比最初指定的更具体的类型。

     

您可以将IEnumerable<Derived>(Visual Basic中的IEnumerable(Of Derived))的实例分配给IEnumerable<Base>类型的变量。

定义变体通用接口和代理部分中更低:

  

协变类型参数使用out关键字在Visual Basic中标记Out关键字,+标记为MSIL汇编程序。