RSS 2.0 Feed
2006-02 Entries
摘要:昨天忙了一个晚上,终于发布了新的BETA 1.5。叫BETA 1.5而不是BETA2的原因是我觉得这次发布比较仓卒,品质恐怕还到不了叫BETA2的程度。但是这次更新带来的新特性还是比较多的。下载页面: http://sourceforge.net/project/showfiles.php?group_id=148613 首先给不熟悉VBF的人简单介绍一下VBF:VBF是一个.NET下的函数式编程类库,提供了创建函数式算法逻辑的基本框架,还提供了一个使用组合子和高阶函数来创建新函数的体系。使用VBF可以实现对象查询、词法解析以及延迟执行逻辑的许多程序。这个版本提供的新功能主要包括: 1、Nullable的支持和IsNull判定 这次提供了对.NET Framework 2.0中Nullable Type的支持,即支持返回Nullalbe<T>类型的函数子(Functor)使用定义在T上的四则运算或比较运算符。对C#而言,这是一个非常方便的特性。比如我们有个一System.Collections.Generic.List<int?>类型的列表list1,下面的代码演示了如何从中查询到所有小于0或者为空的项: //C# //定义一个Identity对象用于查询 Identity<int?> IdNInt = new Identity<int?>(); List<int?> result = list1.FindAll(IdNInt.IsNull() || IdNInt < 0); 如你所见,现在可以使用捷径运算符||和&&了,这也是本版本一个新增功能。另一个新增功能:IsNull()返回一个函数用于判断当前项是否为空。 2、元组(Tuple)和多元函数支持 现在VBF通过对Tuple的支持,提供了处理二元函数及三元函数的能力。Tuple是一种只读结构体,其每个字段的类型均由类型参数指定。因此使用Tuple,你可以快速创建出满足你要求的小型结构。 'VB '演示生成一个Tuple Dim t1 As Tuple(Of Integer, String) = Values(12, "Hello World") Dim t2 As Tuple(Of Long, Single, Date) = _     Values(100L, -58.8, Now()) Values函数自动生成一个相应类型的Tuple,当前Tuple最多支持5个类型参数。Tuple用于模拟多元函数的参数表,还可以作为查询的临时返回量。 3、属性访问语法 这是本版本极其重要的一个功能,有了他,就可以在Id型查询中访问被查询对象的属性。我们先看一个例子。假设Employee类型有两个属性——整数型的Age和字符串型的Name。如果我们有一个System.Collections.Generic.List<Employee>类型的列表emplist,现在要找到所有Age大于25的Employee对象,该如何写呢?VBF提供了独特的!语法(仅支持VB)和Satisfies满足条件方法: 'VB 'Employee是一个有Age和Name属性的类型 'emplist是一个装有Employee实例的List(Of Employee) '定义Identity对象用于查询 Dim IdEmp As New Identity(Of Employee) result = emplist.FindAll(IdEmp!Age.Satisfies(IdInt > 25)) 注意!Age语法,它提供了对Age的访问,但无法得知Age的类型。因此需要在Satisfies函数后面用强类型的IdInt指明要查询的Age是Integer类型。这条查询语句可以读作“寻找Employee,它的Age属性满足整数大于25”。这已经是我找到的最具可读性的写法。除了Satisfies满足条件方法,还支持直接取得属性值的功能。比如我们要的到另一个List(Of String),是刚才那个Employee集合中所有项目Name属性组成的集合,可以这样写: 'VB '接上段代码 Dim names As List(Of String) = _     emplist.ConvertAll(Of String)(IdEmp!Name.AsType(Of String)) AsType方法表明了你要访问的属性的真实类型,这里不支持任何协变。比如上述语法如果你是用Of Object就会运行错误。C#不支持!运算符,我在C#中采用了indexer的语法,即使用方括号来访问想要的属性: //C# Identity<Employee> IdEmp = new Identity<Employee>(); result = emplist.FindAll(IdEmp["Age"].Satisfies(Identities.IdInt > 25)); 4、延迟执行 现在VBF的每一个函数子对象(Functor)都支持一个DelayInoke的函数,它能返回一个IDelayInvoke(Of TReturn)的接口。使用DelayInvoke可以延迟函数的真实求值过程,直到真正使用函数返回值的时候才开始计算。延迟执行可以用来实现Command模式以及Undo/Redo功能。 VBF 1.5还提供了许多其它的新功能,比如用于String型函数的IndexOf方法支持、一元正负运算符支持、类型转换函数支持等等。有兴趣的可以直接下载源代码观看。很抱歉的是这次时间仍然较紧张,我还是没有提供文档,如果发现很难看懂我建议从Functor(Of TArg, TReturn)这个类的虚函数开始看起。希望大家多多提出建议,以便在VBF中提供更多新酷功能。...[阅读全文]

posted @ | Feedback (5) | Filed Under [ 技术随笔 ]

摘要:注意:本系列未经许可,不得转载。 在泛型编程当中,我们对类型的关注大大提高了。有时需要这样的功能:“当类型是A的时候执行这段代码;当类型是B的时候执行另一段代码”。就是说,需要针对类型进行分支选择。当前,我们有三种机制可以实现这种分支选择:1、根据编译期声明类型选择的函数重载机制。2、根据运行时类型的虚函数-重写机制。3、If语句+类型判断。我们使用面向对象和泛型中的某些机制都是为了消除If语句带来的影响,所以这个时候使用If语句可能令人不太兴奋。但是有时候If语句是唯一的实现方式,我们可以将If封装到基础逻辑中以消除它的不利影响。 本来在泛型世界中,我们应该有特化或偏特化手段,用以实现类型方面的匹配式选择。但是.NET泛型没有提供这一机制。那么我们就只能利用运行期类型比较加以模拟。但问题就出来了:运行期类型与声明类型可以是不同的,比如一个对象可以声明成Object类型,但运行时可以是String类型。这种情况下,使用TypeOf运算符(C#的is运算符)的行为将和编译期选择的行为不同。如何让运行期也能识别编译期声明类型呢?上次讲到抽象工厂的实现时,我们提到了一个工具——TypeToken,我们还可以利用它!不过这次我要改个名字:TypeMate,因为我发现.NET Framework中也有一个东东叫TypeToken,起名字真难啊。。这就是TypeMate的定义: Public NotInheritable Class TypeMate(Of T)     Private Sub New()     End Sub     Public Shared ReadOnly Value As TypeMate(Of T) = New TypeMate(Of T) End Class 这个和上次的TypeToken几乎一样,唯一不同就是把属性换成了静态只读字段。因为TypeToken的运行时作用很小,所以我们不能让他在运行时带来太多负担。TypeMate(Of T)与T一一对应,但不管T之间有什么继承关系,各个TypeMate(Of T)之间都没有任何关系,更进一步的是,TypeMate(Of T)的实例将会把T的编译期类型带到运行期,这个正是我们需要的。 接下来我们定义TypeSwitch,作为应用TypeMate的第一个例子,他实现了我们开始的想法,能够根据声明类型进行分派选择。 Public MustInherit Class TypeSwitch(Of T1, T2)     Public MustOverride Sub Fire(ByVal m As TypeMate(Of T1))     Public MustOverride Sub Fire(ByVal m As TypeMate(Of T2))     Public Sub Fire(Of T)(ByVal m As TypeMate(Of T))         Dim typeless = m         If TypeOf typeless Is TypeMate(Of T1) Then             Fire(TypeMate(Of T1).Value)         ElseIf TypeOf typeless Is TypeMate(Of T2) Then             Fire(TypeMate(Of T2).Value)         Else             Throw New ArgumentException("No matched type")         End If     End Sub End Class 注意,为什么除了两个基本的Fire虚方法,还需要带有第三个类型参数的Fire方法呢?这全怪.NET泛型的局限性,编译器在编译的时候无法推测类型参数实际表示的类型,只能把这件事推迟到运行时来做。 现在我们用一个例子看看这种方法与普通的函数重载有什么不同。先定义一个字符串和整数的选择器: Class Test     Inherits TypeSwitch(Of Integer, String)     Public Overloads Overrides Sub Fire(ByVal m As TypeMate(Of Integer))         Console.WriteLine("Interger!")     End Sub     Public Overloads Overrides Sub Fire(ByVal m As TypeMate(Of String))         Console.WriteLine("String!")     End Sub End Class 下面我们编写一个利用该选择器进行类型选择的代码例子: Module Module1     Sub Main()         Dim o As Object = "abc"         Dim s As String = "abc"         SwitchByDeclarationType(o)         SwitchByDeclarationType(s)     End Sub     Sub SwitchByDeclarationType(Of T)(ByVal arg As T)         Dim switch As TypeSwitch(Of Integer, String) = New Test         switch.Fire(TypeMate(Of T).Value)     End Sub End Module 注意泛型算法SwitchByDeclarationType,他利用TypeSwitch功能实现了针对类型参数T(而不是一个具体类型)的判断与分派功能。虽然判断的过程在运行时,效果却是根据参数的声明类型,而不是运行时类型来选择的。例子中两个变量运行时都是字符串,但是object那一次调用的时候会失败。 ...[阅读全文]

posted @ | Feedback (8) | Filed Under [ 技术随笔 ]