RSS 2.0 Feed
2004-04 Entries
摘要:最近VB FAQ提到了一个问题——VB.NET支持非零下标数组吗?答案是不支持。即使VB2005有这样的语法Dim a(0 To 10) As String,那个“0”也不能是别的数字。CLR在某种程度上支持非零下标数组,但C#这样有C传统的语言一开始就设计成只支持零下标,如果VB支持非零下标数组,各种语言的交互将会变得困难。关于这个问题,详细讨论请见这里。在FAQ中,另一篇帖子讨论了解决方法,就是用一个类模拟数组的行为,用默认属性(或索引器,C#)来模拟数组的下标。但是原文提到,没有泛型的情况下,我们需要为每一种类型创建一个非零下标的数组类。现在我们不但有了泛型,还有运算符重载,可以做一个更加清晰、漂亮的非零下标数组类。在动手以前,我发现C#还支持迭代器,可以令我的类更加方便,于是就用了C#,后面给出VB调用它的例子。 /// <summary>/// 表示一个下标可以不是零的数组/// </summary>/// <typeparam name="T">数组元素的类型</typeparam>public class VBArray<T> : IEnumerable<T>{    private T[] arr;    private int _LBound;    public int LBound    {        get        {            return _LBound;        }    }    public int UBound    {        get        {            return _LBound + arr.Length - 1;        }    }    public VBArray(int LBound, int UBound)    {        if (UBound < LBound)        {            throw new ArgumentException("上界不能小于下界");        }        _LBound = LBound;        arr = new T[UBound - LBound + 1];    }    public T this[int index]    {        get        {            return arr[index - _LBound];        }        set        {            arr[index - _LBound] = value;        }    }    public void ReDim(int UBound)    {        if......[阅读全文]

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

摘要:今天在恢复访问的Hi-PDA网站看到了几篇新闻,实在是十分兴奋。首先是Windows CE 5.0的消息(名字里没有.NET了)。还没有仔细看,但是据说支持Direct3D Mobile!还有今年9月要推出的Windows Mobile 2003 SE,其实在Visual Studio 2005的技术御览里就见过了它的模拟器。详细的报道在这里。它可能会支持最新的.NET Compact Framework 2.0,可以用VS 2005的Visual Basic或C#开发应用程序。最后是Intel推出的新XScale CPU,型号为PXA 270,主频可达624MHz,而且支持无线MMX指令,看来功能又强劲了不少!配合Windows CE 5.0,应该能开发出效果惊人的图形效果。详细介绍见这里。还看到了一些关于下一代PPC和Smart Phone的消息。 看来未来一年是移动年。...[阅读全文]

posted @ | Feedback (23) | Filed Under [ 闲话集锦 ]

摘要:从Paul Vick的Blog上看到的一个PPT,专门讲述.NET中常见的数据结构,如数组、字符串、集合和正则表达式等。如果你是VB程序员,希望快速应用这些FCL中的数据结构,可以看看这篇文章。 http://www.only4gurus.com/techlib/miscellaneous/n6.pdf...[阅读全文]

posted @ | Feedback (1) | Filed Under [ 闲话集锦 ]

摘要:最近老写《VB.NET是怎样做到》这样的文章,估计大家看我分析没劲的IL都已经失去兴趣了,所以今天写一片针对初学者的文章,让所有VB程序员都能写出优良的代码。 大家都知道,在Visual Basic里,事件是经过特殊封装的委托变量,事件与普通委托变量的不同在于事件在类的内部只能被引发,不能与处理程序绑定,而在类的外部只能与处理程序绑定,不能引发,也不能获取真实的委托变量。虽然事件的语法很简单,但如何在代码中正确使用事件是许多VB初学者所不知道的。 一、如何定义和引发事件 定义事件有两种语法,一种是显式指定事件处理方法的委托类型: [modifier] Event EventName As HandlerType 另一种是直接在定义语句中写出事件处理方法的参数,这是一种隐式指定委托类型的方法: [modifier] Event EventName(args) 在这种定义下VB会根据参数表生成一个嵌套定义的委托类型。我建议大多数情况使用第一种定义方法,因为第一种方法可以将事件处理方法的委托类型独立出来,防止无意中创建大量浪费的类型。比如下面两个事件: Public Event Test(ByVal sender As Object, ByVal e As EventArgs)Public Event Test1(ByVal sender As Object, ByVal e As EventArgs) 这两个事件具有同样的参数结构,因此他们的委托类型应当是一样的。但是VB还没有智能到那个地步,这两句语句将产生两个不同的委托类型。更深一步,这种参数的处理过程已经被系统定义为System.EventHandler,所以没必要创建任何新的委托类型,只需要这样写: Public Event Test As EventHandlerPublic Event Test1 As EventHandler 只有一种情况可以随意使用参数表形式定义事件,那就是实现定义在接口中的事件时。比如接口中有这个事件: Public Interface ITest    Event Test As EventHandlerEnd Interface 则实现这个接口的事件定义可以写成 Public Event Test(ByVal sender As Object, ByVal e As EventArgs) Implements ITest.Test 这里不会产生新的委托类型,因为VB能够判断事件的委托类型来自于接口中的定义。用显示指定委托类型的方式定义事件的另一个好处是让C#中使用该事件的代码变得简洁些。 在引发事件方面,很多VB的初学者到处随意使用RaiseEvent来引发事件,这是非常不正确的。应该总是在一个叫OnXXX(XXX是事件的名称)的受保护的虚方法中引发事件。比如引发上面例子中Test事件,应该写一个这样的方法: Protected Overridable Sub OnTest(e As EventArgs)    RaiseEvent Test(Me, e)End Sub 在真正需要引发事件的地方调用OnTest方法来引发事件。为什么要这么做?因为只有这样,才能允许该类型的子类控制该事件的行为。如果不是这样写,子类就无法获知父类何时引发事件,更无法在事件引发的时候执行自定义的代码。至于为什么必须起名为OnXXX,这可能是.NET类库一致的约定。请一定要习惯这种写法,在进一步的开发中,我们将逐渐发现这种写法的思想所在。 二、事件参数的设计 我们看到.NET类库中对象的事件总是提供一个Object类型的参数sender和一个XXXEventArgs类型的参数e。这是整个.NET Framework统一的设计。XXXEventArgs是一个继承自System.EventArgs的类型,他表示通过事件传递的数据,而sender则表示事件的引发者。我们设计事件处理方法的时候,如果没有特殊情况,应当总是提供两个参数:一个Sender参数和一个继承自EventArgs的类型的参数。这种设计可以最大限度的让所有事件处理过程有相似的签名。你将会发现,当你的设计需求发生改变时,这种设计可以最大限度地减小整个系统的修改。比如若事件设计成这样: Event MyEvent(sender As Object, index As Integer, product As String) 当需求改变,需要再多传递一个Decimal类型的price参数时,系统中所有响应这个事件的过程都需要增加一个参数,那将是一场灾难。如果将所有要传递的数据封装在一个继承自EventArgs的类型——MyEventArgs中,代码就能写成这样: Event MyEvent(sender As Object, e As MyEventArgs) 那么当需求改变的时候,只需要修改一下MyEventArgs的成员即可,响应事件的方法定义不需要做任何变化。多数情况下,事件是不需要传递参数的。因此用系统定义的System.EventHandler是非常好的选择,没必要为每个事件编写一个新的EventHandler或者EventArgs类型。 如果需要通过事件进行参数回传(就是说,事件的处理方法要将某些数据传递给事件的引发者),那么同样可以用处理方法的参数来完成,只需要将自定义EventArgs类型中需要回传的属性设置为可写(提供Set访问器)即可。一般不需要用某些教材中建议的那样,设计ByRef的参数来实现回传。因为EventArgs能够满足大部分设计的需要。 至于Sender参数,应该始终让调用者传递Me变量以便事件的处理方法能够正确识别事件的引发者。 结论 事件设计是面向对象的重要内容,设计一个良好的事件通常包括:显式指定事件处理方法的委托类型,一般情况采用系统设定的System.EventHandler,用名为OnXXX的受保护的虚方法引发事件以及用自定义的继承自EventArgs的类型包装事件传递的数据。 ...[阅读全文]

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

摘要:上次说到后期绑定的桥梁方法InternalLateCall将通过以下条件进行判断: 1、不是COM方法2、方法没有重载3、传递的形参表和实参表完全匹配4、如果不匹配,那么没有需要回传的ByRef参数和参数数组 符合上述条件,就采用LateBinding.FastCall来调用方法,否则采用一个VBBinder对象来处理。从这里我们可以看出VBBinder对象在后期绑定调用中能完成的任务,就是: 1、在多种重载中,查找正确的版本。2、处理形参和实参结合的问题,包括类型转换、可选参数、参数数组等情况。3、处理调用COM方法的事宜。 VBBinder是继承自Binder的一个类,而Binder是完成上述任务的抽象类。要实现一个Binder,必须重写BindToMethod、BindToField、SelectMethod、SelectProperty 和 ChangeType这四个抽象方法。其中,BindToMethod方法是后期绑定功能的重点。VB在实现的这个方法中,有非常严密而复杂的挑选和判断语句。我在这里就不贴出它的实现了,几乎都是长长的If语句和Select语句。总之,它的功能就是从多种候选的方法中,选出符合和传入参数的正确版本。 另一个重要的实现ChangeType方法,VB是借用内部针对Object类型的转换函数——CTypeHelper来实现的(看来VB为CType定义了很多新的功能)。而通过参数类型表来选择适当方法的SelectMethod,VB则没有实现它。VBBinder还有很多私有的工具方法,如GetDefaultMemberName就是获取表示默认属性的那个成员等等。 接下来我们看看实现方法调用的VBBinder.InvokeMember方法。它主要分成两个部分: 1、COM对象方法的调用。在COM对象方面,InvokeMember是通过调用包含正确信息IReflect对象(这个对象由InternalLateCall传入)的InvokeMember方法来实现调用的。有意思的是这里对COM对象的可选参数提供了支持,也就是说即使是后期绑定调用COM对象,VB便利的可选参数功能仍然得到支持。而在处理ByRef和需要回送参数值的情况时,VB首先通过RemotingServices.IsTransparentProxy来判断对象是否是一个跨越边界访问所用的透明代理。因为跨越边界访问(如Web Service)的方法调用是无法进行参数值回传的,所以对这种情况将自动取消接受参数值回传。此外由于对COM的访问可能会访问到非托管代码,所以,VB在正式调用前用SecurityPermission.Demand方法对调用堆栈中各级方法的权限进行了检查。如果试图通过后期绑定进行不安全的操作,就会抛出异常。 2、托管对象方法的调用。由于托管对象的方法可以重载,可以用缺省参数,还可以用参数数组等,所以这一部分的判断比较复杂。VB首先区分对象的默认属性/方法调用或一般方法调用,这里面判断的工具就是上面提到的工具方法。对于默认方法,VB将获取这个方法的信息,然后按照一般方法调用的步骤继续调用;对于一般方法调用,VB将寻找所有与调用方法同名的成员,然后通过上问提到的BindeToMethod方法找到正确的版本的MethodBase对象。最后,VB通过从该对象中获取的MethodInfo正式用反射调用对象。这中间还包括对静态方法和安全性的检查等复杂操作。 到此为止,一次完整的后期绑定调用操作就结束了,其中经历的复杂过程确实难以想象。我们下次讨论FastCall的实现和关于后期绑定属性操作的内容。...[阅读全文]

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

摘要:Visual Basic一大吸引人的地方是它支持直接的后期绑定。就是说可以直接在代码里访问那些要到运行时才能确定的对象成员。比如这样一段代码: Dim f As Object = New Form2()f.Show() Object是没有Show方法的,但是这段代码可以正确运行。这个功能让VB有了很强的灵活性,它能够直接操作没有类型库的COM对象,而C#则很麻烦。在COM时代,VB的后期绑定是通过IDispatch实现的,而.NET时代,它是由Reflection实现的。为了揭开VB后期绑定的秘密,后面几篇就来讨论一下后期绑定的实现原理。 首先,对方法的调用是后期绑定最重要的一个环节,因为实现了后期绑定的方法调用,就能实现对属性的访问和对事件的操作,这基本上就是对象操作的全部内容。VB的编译器在发现后期绑定的调用后,会用Microsoft.VisualBasic.CompilerServices中的相关操作类实现后期绑定的方法调用。其中,LateBinding.InternalLateCall是这个操作的桥梁。我们来看看这个方法的实现。 这段代码是我从IL手工翻译来的,因为流行的反编译器都不能正确反编译VB的When语句。这段代码很可能有错,凑合着看吧: <DebuggerStepThrough(), _ DebuggerHidden()> _ Friend Function InternalLateCall( _     ByVal o As Object, _     ByVal objType As System.Type, _     ByVal name As String, _     ByVal args() As Object, _     ByVal paramnames() As String, _     ByVal CopyBack() As Boolean, _     ByVal IgnoreReturn As Boolean _ ) As Object       '以下变量的名字已经被我安我的理解改变过       Dim binder As Microsoft.VisualBasic.CompilerServices.VBBinder     Dim flags As System.Reflection.BindingFlags     Dim result As Object     Dim correctIReflect As System.Reflection.IReflect     Dim members() As System.Reflection.MemberInfo     Dim argNumber As Integer     Dim firstMember As System.Reflection.MemberInfo     Dim firstMethodBase As System.Reflection.MethodBase     Dim params() As System.Reflection.ParameterInfo     Dim paramsNumber As Integer     Dim paramsInfo As System.Reflection.ParameterInfo     Dim......[阅读全文]

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

摘要:在.NET泛型中,若要对泛型类型的变量进行操作,必须为泛型类型提供相应的约束。比如,最常见的例子是约束一个IComparable接口,以便对泛型类型的对象进行比较操作。然而,约束除了性能问题以外,最大的问题是降低了灵活性。在.NET众多的类型中,实现IComparable接口的类型很少,所以若约束了这个接口,将大大限制能够为类型参数赋值的范围。如果我们同时需要进行多种操作,就需要约束更多接口,那样能用的类型就寥寥无几了。为了突破约束的限制,我进行了两种尝试。以下两个例子都是为了完成不要约束的等值比较操作。 方案一:动态类型判断 如果无法改变.NET泛型在编译阶段不提供动态约束的事实,那只有将这件事放到运行时来做。实践证明,TryCast能够对泛型类型的变量进行正确的转换,也就是说下面的代码能够有效地工作: Public Class DynamicConstraint(Of T)    Public Sub Test(ByVal a As T, ByVal b As T)        Dim c As IComparable = TryCast(a, IComparable)        If c IsNot Nothing Then            MsgBox(0 = c.CompareTo(b))        Else            MsgBox(Object.Equals(a, b))        End If    End SubEnd Class 这段代码很像动态约束,经实验,对值类型和引用类型都能用。如果T是实现了IComparable的类型,则转换会成功,执行CompareTo方法;如果T没有实现IComparable,则用通用的Object.Equals方法。这种方法的优点是可以根据需要进行判断,而且提供的操作完全由类型自己提供,调用者无需关心内部的实现细节。而类型比较和转换(对值类型要装箱)是比较费时间的,每次调用都这样做,泛型的意义就不太大了。 方案二:外部提供特定的比较方法 这种方法的出发点是,外部调用泛型类型时,必须提供相应的操作方法。比如这个例子中的比较操作,当外部提供类型参数时,还必须提供一个追加的比较方法的实例。其实现方法如下: Public Delegate Function CompareEqualAction(Of CompareType) _    (ByVal A As CompareType, ByVal B As CompareType) As BooleanPublic Class ExternelAction(Of T)    Private _CompareEqual As CompareEqualAction(Of T)    Public Sub New()        '提供默认的比较方法        _CompareEqual = AddressOf CompareEqualDefault    End Sub    ''' <summary>    ''' 获取或设置外部处理过程的委托    ''' </summary>    Public Property CompareEqual() As CompareEqualAction(Of T)        Get            Return _CompareEqual        End Get        Set(ByVal value As CompareEqualAction(Of T))            _CompareEqual = value        End Set    End Property    ''' <summary>   ......[阅读全文]

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

摘要:前一段看知秋一叶关于如何在WebMethod中返回XML的贴子,就想实验一下,结果发现不知道Visual Basic怎样给返回值加Attribue,如下面C#代码所示: [return: XmlAnyElement]public XmlElement Test() 我在VB里无法用类似的<Return: XmlAnyElement>给方法的返回值加Attribute,而且文档里也介绍得不清楚。研究了一下才发现,原来VB是这样给返回值加Attribute的: Public Function Test()As <XmlAnyElement>XmlElement 就是在返回类型前添加这个Attribute。希望对所有和我一样还没有发现这个语法的人有帮助。...[阅读全文]

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

摘要:Duncan Mackenzie 正在筹划一个Visual Basic的FAQ,内容涉及Visual Basic语言、IDE以及VB2005的所有问题。现在就可以到 http://blogs.msdn.com/vbfaq/ 提问,以便建立这个FAQ。 Paul Vick也对这个建立这个FAQ很感兴趣。...[阅读全文]

posted @ | Feedback (3) | Filed Under [ 闲话集锦 ]

摘要:简单工厂(Simple Factory)常用来搭配其他设计模式使用,比如Factory Method或Template Method模式等。简单工厂常用静态方法来做。在.NET中,运算符过程总是静态的,因此可以用运算符过程来做简单工厂。我发现用重载CType运算符来做简单工厂能让代码非常直观。重载CType运算符的方法是: Shared {Widening|Narrowing} Operator CType(param As Type1)As Type2 其中Widening表示类型转换总能成功,而Narrowing表示有可能转换失败。在Visual Basic中,如果类型转换是Widening的,通常可以省略CType不写,我正是要利用这一点。 Public MustInherit Class CustomClass    Public Shared Widening Operator CType( _        ByVal init As CreateOptions) As CustomClass        '用CType运算符做一个简单工厂。        '根据CreateOptions枚举的值确定生成的对象。        '实际应用时,可以从多种类型的参数创建对象。        '如果需要从多个参数创建对象,则可以把init设定成自定义的选项类。        Select Case init            Case CreateOptions.English                Return New CustomClass1            Case CreateOptions.Chinese                Return New CustomClass2            Case CreateOptions.French                Return New CustomClass3        End Select    End Operator    Public MustOverride Sub SayHello()End ClassPublic Class CustomClass1    Inherits CustomClass    Public Overrides Sub SayHello()        MsgBox("Hello")    End SubEnd ClassPublic Class CustomClass2    Inherits CustomClass    Public Overrides Sub SayHello()        MsgBox("你好")    End SubEnd Class Public Class CustomClass3    Inherits CustomClass    Public Overrides Sub SayHello()        MsgBox("Bonjour")    End SubEnd Class  '用作选项的枚举,简单工厂根据枚举的值创建对象Public Enum CreateOptions    English    Chinese    FrenchEnd Enum 现在就可以用Widening CType运算符带来的便利来使用这个简单工厂: Dim......[阅读全文]

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

摘要:最近看到有人提到了VB特有的With语句,于是我就来研究一下。With语句是VB从Pascal中借鉴的语法,其作用就是多次使用同一个变量的成员。如下面对按钮的操作:         Button1.Text = "Button1"        Button1.Left = 100        Button1.ForeColor = Color.Blue 就可以简化为         With Button1            .Text = "Button1"            .Left = 100            .ForeColor = Color.Blue        End With 在早期的VB中,With语句就说比逐一使用其成员性能更好。那么在.NET中是不是也是这样?通过研究发现,With语句总是增加一个临时变量。比如上面那个例子中的With语句,就会产生如下效果:         Dim TempButton As Button        TempButton = Button1        TempButton.Text = "Button1"        TempButton.Left = 100        TempButton.ForeColor = Color.Blue 这说明,With语句用于大部分变量的时候不但没有增加性能,反而多增加了一个变量引用。因此,对于局部变量和类的普通字段,使用With是没有必要的。我记得网上有人提到过这个问题,还以次证明VB产生的代码效率不高。但是,这么说太绝对了,如果With语句应用在用窗体设计器添加的控件上,就会有效率的提升。为什么这么说呢?我在以前的《VB.NET是怎样做到的》讲解WithEvents原理的时候说过,由WithEvents定义的变量,其实质是一个属性,里面包含了对控件事件映射的代码。因此,若Button1那个变量是由窗体设计器生成的,包含WithEvents的变量,那么实际上需要通过窗体的属性来访问,就是说Button1.Text = “A”将被翻译成Me.get_Button1().set_Text(“A”)若对Button1的大量属性做这种操作,开销就比较大了。这时,如果用With语句,VB就会自动生成一个局部变量来接受Me.get_Button1()的结果,然后对其属性进行修改的语句全部都在这个局部变量上进行,需要修改的属性越多,这种修改的好处就越明显。 因此,我们得出的结论是,With语句通常不能增加效率,但是,在需要修改由窗体设计器生成的,带有WithEvents定义的控件的大量属性时,采用With可以获得性能收益。...[阅读全文]

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

摘要:这是我花费一个星期的时间,查阅了不少资料写出来的。可以说是一篇图文并茂的Visual Basic简史。喜欢Visual Basic的或者用过Visual Basic以前版本的朋友可以来看看,说不定能勾起你的回忆。 Visual Basic 历史版本大回顾...[阅读全文]

posted @ | Feedback (2) | Filed Under [ 闲话集锦 ]

摘要:这是我在博客堂的第一篇帖子。前几天被开心就好热情邀请,终于入住成功,但是开心老大却离开了博客堂,真是一大憾事。 我的水平和MVP以及博客堂的其他大侠们是有很大差距的,所以为了不给博客堂抹黑,我只有以各位大侠为榜样努力学习。 首先我要把.NET Blog上我的系列《VB.NET是怎样做到的》已经完成的部分搬运到这里来。因为我多半要在这里将这个系列进行下去。《VB.NET是怎样做到的》是专门揭示VB.NET独有特征的系列,我在.NET Blog已经写了九篇,搬运过来的版本《VB.NET是怎样做到的(搬家版)》是我经过简单整理的版本,已经去掉了心情部分,还添加了一些在原Blog上与人讨论的内容。由于水平有限,如果错误望大家指出。...[阅读全文]

posted @ | Feedback (9) | Filed Under [ 闲话集锦 ]