我习惯使用接口,泛型并在真实环境中使用继承来开发它们,同时尝试使用并将其实现为我们即将开展的项目之一的新架构,并且我对我对此感到困惑的泛型有疑问。
这对我自己来说更像是一个教育问题,因为我无法理解为什么.NET不允许这样做。
如果我有一个(Of T As IA, T2 As A)
的泛型类,那么我有以下接口和实现基接口的类
Public Interface IA
Property A As String
End Interface
Public Interface IB
inherits IA
Property B As String
End Interface
Public Class GenericClass(Of T As IA, T2 As A)
'Should be list of IA?
Public list As New List(Of T)
Public Sub Add()
End Sub
End Class
因为我已经T as IA
为什么在添加方法时Dim foo4 As T = New A()
不合法
Dim foo1 As IA = New A()
Dim foo2 As T
Dim foo3 = Activator.CreateInstance(Of T2)()
Dim x As IA = foo2
Dim y As IA = foo3
list.Add(x)
list.Add(y)
以上都是?对于我来说,这对于我来说已经成为一个学习曲线了,但我只是很困惑,为什么我逻辑上不能这样做呢?
编辑:抱歉忘了Class A
,错误消息请参阅下面
Public Class A
Implements IA
Public Property A As String Implements IA.A
End Class
编辑2:错误输入错误
“类型a的值无法转换为T”
答案 0 :(得分:3)
目前还不清楚你要做什么,但我注意到的一个问题是你似乎假设List<TypeThatImplementsIA>
在某种程度上可以与List<IA>
互换。事实并非如此。想象一下A
是一类飞鸟,而IA
是由可以飞行的生物实现的,有人创造了GenericClass<Airplane, BlueJay)
。即使Airplane
和BlueJay
都是可以飞行的东西,也可能无法将BlueJay
添加到List<Airplane>
。框架中可以使用GenericType<DerivedType>
作为GenericType<BaseType>
的一种常见情况是IEnumerable<T>
。原因是人们无法将T
存储到IEnumerable<T>
中 - 只能将其读出来。如果一个人期待IEnumerable<Animal>
而一个人有IEnumerable<MaineCoonCat>
,那么每当有人想要读Animal
时,就会读到MainCoonCat
的实例,该实例继承自Animal
IEnumerable<T>
因此可以替代它。 ValueType
的此功能称为协方差。
但是,这种行为存在限制,这是因为使用接口作为一种存储位置(变量,参数等)与使用它作为约束之间存在差异。对于每个非可空值类型,Runtime中实际上有两个相关类型。其中一个是真正的值类型,它没有继承的概念(但可以实现接口)。另一种是源对象类型,它派生自Object
(后者又来自IEnumerable<DerivedType>
)。大多数.net语言将隐式地将前一种类型转换为后者,并允许代码将后者明确地转换为前者。接口类型存储位置只能保存对堆对象的引用。这很重要,因为它意味着虽然实现接口的结构是 convertible 到该接口类型,但这并不意味着struct 的实例是该接口的实例类型。协方差的前提是每个对象都由例如BaseType
可以直接用作class
的实例而无需转换。这种直接替代性适用于继承的类类型,以及由类类型实现的接口。它不适用于结构类型实现的接口,也不适用于没有IComparable<T>
约束的泛型。将类约束添加到泛型类类型参数将允许该类型参数参与协方差,但可能排除使用结构作为泛型类型参数。请注意,除非有特殊理由期望接口将由结构实现(例如class
的情况,在许多情况下,接口不太可能由结构实现,因此{{1约束将是无害的。)
答案 1 :(得分:2)
那是因为T
不是接口IA
本身。这是它的一个实现。
假设您有另一个实现IA
的类:
Public Class B
Implements IA
Public Property B_A As String Implements IA.A
Public Property OtherProperty as Object
End Class
然后你像这样创建一个Generic Class
的新实例:
Dim genericObject as new GenericClass(Of B, A)
因此,在这种情况下,T
现在为B
,而A
无法投放到B
。
在这种情况下,替换你的疑问部分,一个对我有意义的代码:
Dim foo4 As IA = New T()
由于评论而编辑
为了能够实例化T
,有必要在类型定义中声明New
约束。因此泛型类声明将是:
Public Class GenericClass(Of T As {New, IA}, T2 As A)