我有一个我这样定义的界面:
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忽略此警告?
答案 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汇编程序。