为什么我不能将类型约束的泛型参数转换为约束类型?

时间:2012-10-01 14:50:24

标签: vb.net generics inheritance

我习惯使用接口,泛型并在真实环境中使用继承来开发它们,同时尝试使用并将其实现为我们即将开展的项目之一的新架构,并且我对我对此感到困惑的泛型有疑问。

这对我自己来说更像是一个教育问题,因为我无法理解为什么.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”

2 个答案:

答案 0 :(得分:3)

目前还不清楚你要做什么,但我注意到的一个问题是你似乎假设List<TypeThatImplementsIA>在某种程度上可以与List<IA>互换。事实并非如此。想象一下A是一类飞鸟,而IA是由可以飞行的生物实现的,有人创造了GenericClass<Airplane, BlueJay)。即使AirplaneBlueJay都是可以飞行的东西,也可能无法将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)