随笔 - 89, 评论 - 163, 引用 - 33

导航

关于

标签

每月存档

最新留言

广告

 
[原文作者]:Lucian
 
    这是我们如何在VB将来某一个版本中实现Co/Contra-Variance的一系列探索的第一项。 这并不是对下一代VB的承诺,而是作为一个提议放在这里,从而能够从我们潜在的用户那儿得到一些反馈。
 
Sub EatFruit(ByVal x As IEnumerable(Of Fruit))
...
 
Dim x As New List(Of Apple)
x.Add(New GrannySmith)
x.Add(New GoldenDelicious)
EatFruit(x)
' ERROR: cannot convert List(Of Apple) to IEnumerable(Of Fruit)
    观察一下以上的代码,或许你觉的它是没问题的。这是个很常见的情景:一个库函数处理一些data类型,但是你的自定义类型继承了这个data类型。如何能够将一个自定义类型的集合传给这个库函数呢?
    我们正在考虑为VB语言增加一个特征来支持这种转变,我们称之为“Co/Contra-Variance”,简称为“Variance”。实际上大概在2005年的时候,CLR已经支持“Variance”了,但并没有一种发布的语言用到它。但是一些其它的语言用到它,这里有一些链接,都是 关于这个主题的:
    我将谈一下VB中如何使用Variance:如何用它让你的代码更简单或清晰,如果我们实现了它能解决什么问题。Variance博大精深,apples转化为fruit只不过是它的寻常一功能而已,以上的文章更是让人觉得它复杂。但是我相信我们提议的语法和例子能够揭去这层神秘的面纱。
    昨天我用Variance解决了一个问题:
Function Call(instance As Expression, method As MethodInfo, arguments As IEnumerable(Of Expression)) As MethodCallExpression
...
 
' Create a new callsite that takes two arguments:
Dim args As New List(Of ConstantExpression)
args.Add(Expression.Constant("x"))
args.Add(Expression.Constant("y"))
'
Dim call1 = Expression.Call(instance, method, args)
' args inherits from IEnumerable(Of ConstantExpression), which
' variance-converts to IEnumerable(Of Expression)
 
    对应于第一段,我们转化为:
' some example classes to get us started
Class Food : End Class
Class Fruit : Inherits Food : End Class
Class Apple : Inherits Fruit : End Class
Class GrannySmith : Inherits Apple : End Class
Class GoldenDelicious : Inherits Apple : End Class
 
' GoldenDelicious < Apple < Fruit < Food
' using < in the mathematical sense of "is smaller than",
' and in the VB sense of "can be converted to"
 
Class AppleBasket
    Implements IReadOnly(Of Apple)
    Implements IWriteOnly(Of Apple)
End Class
 
out”参数
    我们想用关键字“out”和“in”来介绍Variance:
Interface IReadOnly(Of Out T)
    Function Read() As T
End Interface
' "Out" declares that T will only ever be used
' as return type of functions *
 
Dim x As IReadOnly(Of Apple) = New AppleBasket
Dim y As IReadOnly(Of Fruit) = x
 
Dim f As Fruit = y.Read()
' This is guaranteed not to throw InvalidCastException
 
    当接口的参数类型是“out”时,它表明这个类型只能用来返回(其他地方表明传出数据),如果试图调用“Sub(ByVal x As T)”,就会产生一个编译错误。(CLR如何使用Variance限制了很多设计,我们希望能和其他的.NET语言兼容。)
    正是这个“out”保证了CLR能够转化接口:
 
' GoldenDelicions < Apple < Fruit < Food < Object
 
Dim apples As IReadOnly(Of Apple) = New AppleBasket
 
' It is allowed to change to an IReadOnly of something bigger:
Dim fruits As IReadOnly(Of Fruit) = apples
Dim foods As IReadOnly(Of Food) = apples
Dim things As IReadOnly(Of Object) = fruits
 
' It is an ERROR to change to an IReadOnly that is smaller:
Dim golds As IReadOnly(Of GoldenDelicious) = apples
 
' Also an ERROR to change to something unrelated
Dim cars As IReadOnly(Of Car) = apples
 
    通常来说,如果你有一个泛型接口IreadOnly(Of Out T),然后你可以把“Of T”转换为它可以转化的其它类型。很显然,这是类型安全的:
    Variance转换是类型安全的和有效的,它只用一句中间语言指令来实现,不需要运行时Runtime检查。(这区别于数组:每次往数组里放东西,都得进行Runtime检查。)
    参数类型是“out”的接口被成为covariant。
In”参数
Interface IWriteOnly(Of In T)
    Sub Write(ByVal x As T)
End Interface
' "In" declares that T will only ever be used
' as ByVal arguments to functions.
 
Dim x As IWriteOnly(Of Apple) = New AppleBasket
Dim z As IWriteOnly(Of GoldenDelicious) = x
 
z.Write(New GoldenDelicious)
 
    “In”参数正好相反。当接口的参数类型是“In”时,它表明这个类型只能用于ByVal引用(其他地方表明传入数据),如果试图调用“Function f() as T”,就会产生一个编译错误。
    “In”参数保证了反向的类型转换:
' GoldenDelcious < Apple < Fruit < Food < Object
 
Dim apples As IWriteOnly(Of Apple) = New AppleBasket
 
' It is allowed to convert to an IWriteOnly of something smaller:
Dim golds As IWriteOnly(Of GoldenDelicious) = apples
 
' It is an ERROR to convert to something bigger, or unrelated:
Dim foods As IWriteOnly(Of Food) = apples
Dim cars As IWriteOnly(Of Car) = apples
 
 
 
    参数类型是“out”的接口被成为contravariant。
同时有“In”和“Out”
    直到20世纪90年代,人们仍然在为“In”或者“Out”是否是正确的而争论。现在我们知道了他们都是正确的!第一个在这方面有说服力的论据是1995年Giuseppe Castagna的研究论文"Conflict Without A Cause" [PDF]。
    这里有两个例子,说明他们为什么是正确的,以及将他们放在一起:
Class AppleBasket
 Implements IReadOnly(Of Apple)
 Implements IWriteOnly(Of Apple)
 
 Private m_value As Apple
 
 Public Function Read() As Apple Implements IReadOnly(Of Apple).Read
    Return m_value
 End Function
 
 Public Sub Write(ByVal x As Apple) Implements IWriteOnly(Of Apple).Write
    m_value = x
 End Sub
End Class
 
Pipes: 为内部和外部契约(contracts)用“In”和“Out”
' Here we implement a Pipe. Each element in the pipe is an ICollection.
'    IList < ICollection < IEnumerable
'
' When we give out reader ("Out") access to the public, we force it so
' readers can only ever assume that elements are IEnumerable.
' And when we give out writer ("In") access, we force it so
' that writers must always put in IList
'
' This future-proofs our code in TWO directions: it forces the
' implementation to provide IList in case in the future we want
' to expose more to the clients; but it does so without making
' a public commitment to the clients that future implementations
' would have to uphold.
 
Class MyPipe(Of T)
 Implements IWriteOnly(Of T)
 Implements IReadOnly(Of T)
 
 Private contents As New Stack(Of T)
 
 Public Sub Write(ByVal x As T) Implements IWriteOnly(Of T).Write
    contents.Push(x)
 End Sub
 
 Public Function Read() As T Implements IReadOnly(Of T).Read
    Return contents.Pop()
 End Function
End Class
 
 
    我们很希望能得到用户的反馈,从而帮助我们决定是否将这个功能加入VB语言,并且思考让他如何工作。请踊跃评论。
    在以后的几周里,我会新更多关于Variance的东西。
    另外:关于这边文正的标题,这是我们的设想:
Dim x As New List(Of Apple)
Dim y As List(Of Fruit) = x
'
' ERROR: List(Of Fruit) cannot be converted to List(Of Apple)
' Consider using IEnumerable(Of Fruit) instead.

打印 | 张贴于 2008-12-17 17:07:39 | Tag:VB Team Blog  Visual Basic

留言反馈

暂时没有留言纪录
博客主人设置本博客不允许匿名用户发表言论,请登录后再试

Powered by: Joycode.MVC引擎 0.5.2.0