3

我已经习惯于使用接口、泛型并在真实环境中使用继承来开发它们,同时尝试将其用于我们即将进行的项目之一的新架构中,我有一个关于泛型的问题,我对此感到困惑。

这对我自己来说更像是一个教育问题,因为我不明白为什么 .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为什么在add方法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”

4

2 回答 2

3

目前尚不清楚您要做什么,但我注意到的一个问题是您似乎假设 aList<TypeThatImplementsIA>可以与 a 互换List<IA>。事实并非如此。想象一下,这A是一类会飞的鸟,IA由会飞的生物实现,有人创造了一个GenericClass<Airplane, BlueJay). 尽管AirplaneBlueJay都是可以飞行的东西,但无法将 a 添加BlueJay到 aList<Airplane>中。框架中可以将 aGenericType<DerivedType>用作 a的一种常见情况GenericType<BaseType>是与IEnumerable<T>. 原因是不能将T's 存储到IEnumerable<T>--one 只能读出它们。如果一个人期待一个IEnumerable<Animal>并且一个人得到一个IEnumerable<MaineCoonCat>,那么每次一个人期待阅读一个Animal,人们将读取 的一个实例MainCoonCat,该实例继承自Animal它,因此可以替代它。的这个特征IEnumerable<T>称为协方差

但是,这种行为有一个限制,这是因为将接口用作一种存储位置(变量、参数等)与将其用作约束之间存在差异。对于每个不可为空的值类型,在运行时中实际上有两种相关的类型。其中之一是真正的值类型,它没有继承的概念(但可以实现接口)。ValueType另一种是派生自(又派生自)的堆对象类型Object。大多数 .net 语言会隐式地将前者类型转换为后者,并允许代码将后者显式转换为前者。接口类型的存储位置只能保存对堆对象的引用。这很重要,因为这意味着虽然实现接口的结构是可转换为该接口类型,这并不意味着该结构实例是该接口类型的实例。协方差的工作前提是,例如 an 返回的每个对象IEnumerable<DerivedType>都可以直接用作 的实例而BaseType 无需转换。这种直接可替换性适用于继承的类类型,以及由类类型实现的接口。它不适用于由结构类型实现的接口,或没有泛型的泛型class约束。将类约束添加到泛型类类型参数将允许该类型参数参与协变,但可能会阻止将结构用作泛型类型参数。请注意,除非有特定的理由期望接口将由结构实现(例如IComparable<T>,在许多情况下,接口不太可能由结构实现,因此class约束是无害的)。

于 2012-10-01T16:12:04.553 回答
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)

所以在这种情况下,Tnow 是B,并且A不能转换为B

在这种情况下,代替您怀疑的部分,一个对我有意义的代码:

Dim foo4 As IA = New T()

编辑由于评论

为了能够实例化T,有必要New在类型定义中声明约束。所以泛型类声明将是:

Public Class GenericClass(Of T As {New, IA}, T2 As A)
于 2012-10-01T15:47:42.063 回答