RSS 2.0 Feed
2004-08 Entries
摘要:我们在编写程序的时候,常常要使用Try Catch来进行异常的捕获。有时,将暂时无法处理的异常继续向外抛出也是十分常见的用法。完成这项操作可以通过两种方式,第一种是判断后继续抛出异常;第二种则是通过filter进行条件过滤,只捕获条件符合的异常。我们分别用两个子程序演示他们: '检查后重新抛出异常Private Sub Test1()    Dim a As Integer = 0     Try        a = 2 \ a    Catch ex As Exception        If a <= 0 Then Throw    End TryEnd Sub '用过滤器根据条件捕获异常Private Sub Test2()    Dim a As Integer = 0     Try        a = 2 \ a    Catch ex As Exception When a > 0     End TryEnd Sub 注意到区别了吗,一个是通过Throw语句完成,而另一个则是通过When语句。我用的抛出异常的语句是a = 2 \ a,它产生的被零除异常速度非常快,要比直接抛出Exception的实例还要快的多,只有这样我们才能将比较的焦点放在捕获上。 这是测试的代码: Dim w As New Stopwatch()For i As Integer = 1 To 50    w.Start()    Try        Test1() 'Or Test2()    Catch ex As Exception     End Try    w.Stop()Next TextBox1.AppendText(w.ElapsedMilliseconds & vbCrLf) 测试进行在Release,不开优化(以免编译器改变机理)的情况下,预运行10次的条件下,这是两者的对比(毫秒): Throw版:341Filter(When)版:169 性能差距极大!Throw是一个非常慢的操作。相比之下Filter则十分快速。此处只循环了50次,就能有数百毫秒的差距,可见此性能差距是日常程序里肉眼就能看出的严重差距。建议大家需要按条件捕获异常时,尽量用When语句,以获得Filter的性能好处。...[阅读全文]

posted @ | Feedback (11) | Filed Under [ 灵感记录 ]

摘要:虽然Visual Studio 2005 BETA版整体性能还不如Visual Studio.NET 2003,但他从单击“启动”到程序开始运行的速度比Visual Studio.NET 2003更快了,这主要就是Visual Studio 2005的新功能——宿主进程的功劳。 想看看宿主进程是什么吗,请看VSHOST -- the Hosting Process 。 此外我最近对DirectX越发的感兴趣起来(也许是买了新显卡的缘故)。我要再下一次决心,学习一下DirectX。...[阅读全文]

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

摘要:在.NET开发中,保持线程安全的默认手段是采用System.Threading.Monitor加锁以确保对共享数据访问的同步性。我们已经很习惯于用SyncLock语句来实现这个功能。但是锁住什么变量也是有讲究的。如果一个线程过程所用的同步变量被别的过程锁住而又不放开的话,这个线程就会陷入永久的沉睡。普遍方案是: 1、锁住被访问的数据本身。对于只存在一种线程过程的情况可以使用,但若有很多种不同的线程过程都要访问这数据,就很危险了。 2、锁住容器的实例。常见是锁住thMe。也有锁住Me.GetType()的,都是相似的原理。这种方法的最大缺陷就是Me尤其是Me.GetType()都是可以被对象外部访问到的对象,如果从外部锁住他们而且不放开,对象内的线程就被锁死了。这是相当危险的。 3、锁住特殊的同步根。常常是类型的字段,没有任何功能,只是用来维持同步的。这种变量从外部是无法锁住的,但类内部的其他过程仍可锁住他们,导致死锁。 所以我想出一种方案:锁住局部静态变量。如下所示: Static syncRoot As New Object()LyncLock syncRoot    'CodeEnd SyncLock 局部静态是无法被其他过程访问的,因此这个同步根就成了本线程过程专用的。不用担心任何其他人可以锁住它。而局部静态变量的生命周期与类的字段一样,完全可以胜任这个工作。...[阅读全文]

posted @ | Feedback (10) | Filed Under [ 灵感记录 ]

摘要:这个系列总算走到了Visual Basic最后一个大的语言改进——泛型。事实上,泛型是.NET Framework 2.0所支持的一项特别的功能,Visual Basic 2005只不过从语言层面上支持他,就像C#和C++/CLI一样。首先,我们从泛型本身介绍起。 需求 我们常常会有一种需求,就是我们编写的代码能够针对多种类型执行。比如排序,检索,集合的操作等等。这些操作的代码应该能够只编写一次,就能够广泛地用于所有类型。.NET Framework 1.1或更早版本提供的方案是,用继承树的根——Object,承载任何类型。这样针对Object的算法就等于兼容于所有的类型。但是以Object作为所有类型的承载容器有两个重要的问题:首先值类型在转换成Object时要进行“装箱”操作,该操作的速度非常缓慢,因此性能就成最大的问题。其次是对Object进行操作必须通过运行时类型转换才能进行,这样就不能在编译期间发现类型不兼容的操作。比如我们有一个自定义的结构——Customer,我们希望用ArrayList来保存一个Customer的列表: Dim customerList As New ArrayList()customerList.Add(New Customer("Harry Potter", 13, "Hogwarts School"))'当我们要使用这条记录时MsgBox(CType(customerList(0), Customer).Name) 初看起来这没有任何问题,但是如果我们添加的语句写成这样: customerList.Add("Hello") 会怎么样呢?String根本不能转换成Customer类型,但是没有任何提示阻止你这样做。这个错误直到运行时才会体现出来。我们需要强类型的方法,编译时的类型检查和更高的性能,所以我们需要泛型。 感受泛型 现在我们就来看看针对上述需求的泛型解决方案。.NET Framework 2.0支持一种新的泛型列表——List(Of T)。这里我们引入了Of语句,他就是Visual Basic为实现泛型而增加的。其中T称为“类型参数”,它接受任意一个.NET类型作为元素的类型。于是我们可以将上述Customer的代码写成: Dim customerList As New List(Of Customer)customerList.Add(New Customer("Harry Potter", 13, "Hogwarts School"))MsgBox(customerList(0).Name) 我们来看看这段代码中改变的地方。首先List(Of Customer)对T进行了指定,所以现在customerList对象就是一个只能装Customer类型元素的列表。这件事是在编译期间决定的,因此编译期始终知道customerList元素的类型,所以在取出其中对象时无需再进行任何类型装换。更重要的是,现在再向customerList中放入不是Customer类型的变量就会出现编译错误,而不是原来的运行时错误了。泛型能让编译期明确正在操作的类型,它就不会对值类型进行装箱操作,于是性能也大大提高了。使用泛型的另外一个好处是,Visual Basic的IDE能够为你提供智能感知,当你使用customerList.Add时,含有正确信息的提示出现了: 开始编写泛型的代码 泛型提供给你的不仅仅是使用.NET Framework中已经设计好的泛型类型,你完全可以自己书写泛型的代码。仍然是使用Of语句: Class MyGeneric(Of T) 这时,Of语句的作用不再是为类型参数提供所需的类型,而是定义新的类型参数。现在MyGeneric就接受一个名为T的类型参数。在MyGeneric泛型类的内部T被看作一个类型。你应当将T想象成使用该泛型类时能在Of语句之后提供的任何类型。现在只要对T进行编码就行了,比如: Class MyGeneric(Of T)    Private myVar As T    Public Sub SetVar(ByVal newValue As T)        myVar = newValue    End Sub    Public Function GetVar() As T        Return myVar    End FunctionEnd Class 相当简单吧。当你要使用这个泛型类的时候,如同Framework提供的泛型类一样,要指定类型参数T的真实类型。如这一语句: Dim obj As New MyGeneric(Of Integer) 这时obj的类型实际上就是一个将上述定义中所有T都换成Integer的类型。而 Dim obj As New MyGeneric(Of String) 这条语句中,obj则是一个T为String型的实例。你可以任意指定类型参数,以便创建出更多适于不同类型的对象,这就是泛型的精髓——“书写一次,使用于广泛的类型”。 泛型不仅仅能用于类型的定义,你还能够定义泛型的方法。其语法和泛型类型基本一样: Public Function IIf(Of T)(Expression As Boolean, TruePart As T, _    FalsePart As T) As T 这就是一个泛型方法,其参数和返回类型都可以使用类型参数T所表示的类型。泛型方法可以像泛型类型那样,使用Of语句来确定类型参数T: max = IIf(Of Integer)(a > b, a, b) 通过指定类型参数为Integer,我们的IIf(Of T)函数就成了专门针对Integer的IIf函数。其实,Visual Basic还支持类型参数的隐式指定,就是说当参数或返回值能够有足够的信息确定类型参数时,Of语句就不必写了。比如 Dim max, a, b......[阅读全文]

posted @ | Feedback (8) | Filed Under [ 技术随笔 Visual Basic 2005 新功能点评 ]

摘要:.NET Framework 2.0中新增了ArraySegment(Of T)结构,我看名字还以为他能够帮助我保存或者用来读写数组的片断。但是当我试验过以后(特别是Reflecor以后)我发现它只不过帮我保存了数组的引用和offset、count两个变量而已,没有为我做任何事。 Dim testArray() As Integer = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}Dim arrSeg As New ArraySegment(Of Integer)(testArray, 5, 3)arrSeg.Array(1) = 700For i As Integer = 0 To 9    TextBox1.AppendText(testArray(i) & vbCrLf)Next 结果和直接给testArray赋值没有任何区别。我不知道这个东西到底是干什么用的?...[阅读全文]

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

摘要:在Visual Studio中进行开发时,我们已经很习惯代码窗口右边放置解决方案资源管理器和属性窗口的布局。但是写代码时那个属性窗口能做什么用呢?Visual Basic 2005的开发人员决定利用这个空间,于是就创造出了Attribute编辑器。说到Attribute我们都不陌生,他是给代码添加自定义元数据的一种方式,有些Attribute能够被.NET Framework或编译器识别利用。比如常用的有Serializable、COMVisible等。传统方法添加Attribute都是通过书写代码,这样Attribute及其属性越写越多,修改就会很麻烦。而采用了Attribute编辑器,就能用鼠标作这件事了。首先我们切换到代码窗口,可以看出右下的属性窗口已经变成Attribute编辑器了。如图: 如果要添加SerializableAttribute,只要双击那个位置即可。除了这些默认的常用Attribute,这个编辑器还可以可视化地设计其他一些Attribute。在设计控件时我们经常要编写一大堆ComponentModel里面的Attribute,正好可以用Attribute编辑器来简化这个操作。 不过目前Attribute编辑器能够编辑的Attribute还很少,也不能用于自定义的Attribute。我相信在正式版本中这个功能会有更好的表现。 本期就介绍这个小小的功能,下期介绍可视化调试器...[阅读全文]

posted @ | Feedback (4) | Filed Under [ 技术随笔 Visual Basic 2005 新功能点评 ]

摘要:我已经介绍过如何在C#中使用Visual Basic的My.Application模型,但是我没有突出用了他之后有什么比原本C#使用的方式更好的地方。所以我将追加几篇帖子,让C#程序员进一步了解采用这种新Application模型的优点。 显示Splash Screen是现代软件常用的手段,显示一个多彩缤纷的Splash Screen可以让应用程序启动的感觉速度加快。但是如何显示Splash Screen呢,首先这个窗口必须先于主窗体出现,而且必须在主窗体初始化完毕以后消失。这个对老手不成问题,但是对于新手,往往无法控制窗体前后显示的问题,导致产生出很多不“地道”的解决方式。现在,如果你用了ApplicationBase模型,就可以很容易地实现一个Splash Screen的最佳模式。(对于Visual Basic程序员,此功能只需要在IDE中选择即可,无需编码) 首先准备你的主窗体和Splash Screen窗体,假设主窗体是Form1而Splash Screen是Form2。你需要继承System.Windows.Forms.WindowsFormsApplicationBase类: class MyApplication : System.Windows.Forms.WindowsFormsApplicationBase{    public MyApplication() : base(AuthenticationMode.Windows)    {        EnableVisualStyles = true;        ShutdownStyle = ShutdownMode.AfterMainFormCloses;    }    protected override void OnCreateMainForm()    {        MainForm = new Form1();    }    protected override void OnCreateSplashScreen()    {        SplashScreen = new Form2();    }} 非常简单,只需要重写两个方法——OnCreateMainForm和OnCreateSplashScreen方法,分别指定主窗体和Splash Screen窗体即可。接下来我们创建MyApplication类的默认实例,按照惯例使用My作类名: static class My{    private static MyApplication _app;    private static object _appSyncRoot = new object();    public static MyApplication Application    {        get {            if (_app == null)            {                lock (_appSyncRoot)                {                    if (_app == null)                    {                        _app = new MyApplication();                    }                }            }            return _app;         }    }} 最后修改Main方法,将其中原来所有的Application类方法调用的语句全部去掉,改为My.Application.Run(),如下: [STAThread]static void Main(){    // Application.EnableVisualStyles();    // Application.EnableRTLMirroring();    // Application.Run(new Form1());    My.Application.Run();} 现在只要直接运行你的程序,就可以看到Splash......[阅读全文]

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

摘要:一个小小的问题(可能是因为VS2005 在IDE上的一些变更)导致Visual Basic Power Pack的TaskPane控件在Visual Basic 2005中使用时出现大量设计时异常。虽然不影响运行,但是这将导致窗体设计器无法使用。要改变这个问题,需要修改位于TaskPaneDesigner.vb中的OnSelectionChanged方法。找到这行代码: tf = CType(c, TaskFrame) 修改为 #If VBC_VER >= 8.0 Then     tf = TryCast(c, TaskFrame) #Else     If TypeOf c Is TaskFrame Then tf = c Else tf = Nothing #End If 这样修改就没错了。但是行为仍然不太正常。我还得再研究研究。...[阅读全文]

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

摘要:我已经将My命名空间中所有默认的对象都介绍完了,相信大家已经开始体验到My的方便之处。但是对于一些高级用户来说,这些功能还显得有所欠缺。老手们有时也编写了类似My功能的类或函数,要是能把他们放到My命名空间中多好。My就像一个可随时访问的工具箱,除了里面已经有的工具以外,当然允许我们将自己的东西放进去。下面我们就来看看怎么扩展My命名空间。 添加自定义的类或模块 如果我们想要放进My中的函数都是静态的,那么直接把类或模块放入My命名空间是个方便的方法,做法非常简单,只要将类或模块定义在My命名空间中即可 Namespace My    Public Module Tools        Public Sub DoSomething()            'Some code here        End Sub    End ModuleEnd Namespace 现在就可以直接用My.Tools来访问自定义的模块,很方便。 添加自定义类的实例 如果我们观察Visual Basic所提供的My命名空间成员,将发现他们都不是类本身,而是对象。因为所有的方法都做成静态毕竟不是最佳方法,我们有时候需要将一些自定义类的实例放到My命名空间中,这样不用的时候就比较节省内存,而且易于控制资源释放的问题。首先我们要定义自定义类,可以放在任何地方,而不必放到My命名空间中,这样就可以避免类名直接显示在My关键字后。然后,在My命名空间下,定义一个带有HideModuleNameAttribute的模块,名称可以随便起;最后在模块中设定访问自定义类实例的属性。假设我们的自定义类叫ToolsProxy,而自定义属性叫Tools,那么可以这样写: 'Namespace My<HideModuleName()> _Friend Module MyCustomModule    Private syncRoot As New Object    Private _tools As ToolsProxy    ''' <summary>    ''' Contains my custom tools.    ''' </summary>    Public ReadOnly Property Tools() As ToolsProxy        Get            'Double check lock to ensure thread safty.            If _tools Is Nothing Then                SyncLock syncRoot                    If _tools Is Nothing Then                        _tools = New ToolsProxy                    End If                End SyncLock            End If            Return _tools        End Get    End PropertyEnd Module 有了HideModuleName这个Attribute,它本身就不会出现在My关键字后面,而它的属性则会显示。完成以后,你会发现My.Tools这次与Visual Basic内置在My中的对象没有任何区别了。此方法为扩展My最佳的方法,推荐使用。 扩展My.Application或My.Computer 有时候,我们不仅要将自定义的类添加到My中,还希望更进一步直接补充My.Application或My.Computer本身的功能。而这完全可以做到。Visual Basic提供了Partial关键字,他可以扩写当前项目中的类或结构,无论原先定义的时候是否加上了Partial关键字。于是我们就可以利用这一特征扩写定义看不见的My.Application或My.Computer。My.Application对应的是MyApplication类,而My.Computer对应的是MyComputer类。比如我们要给My.Computer增加一个新的功能——CdRomDriver属性,用以控制光驱的弹出、缩进与自动运行,就可以直接在My命名空间下扩写MyComputer类,如下: 'Namespace My Partial Class MyComputer    Public ReadOnly Property CdRomDriver() As CdRomDriver        Get            'Codes here        End Get    End PropertyEnd......[阅读全文]

posted @ | Feedback (6) | Filed Under [ 技术随笔 Visual Basic 2005 新功能点评 ]

摘要:如果说My.Application、My.Computer和My.User是VB2005提供的汇集常用功能的类库,My.Resources是一个对项目资源的强类型封装,那么My.Forms和My.WebServices就是一个窗体和Web服务使用模式的绝佳范例。从VB6升级至Visual Basic .NET的程序员往往对VB.NET新的窗体编程模式不适应。因为VB.NET的窗体是类,必须要创建实例才能使用,而VB6的窗体则既是类又是对象,无须创建实例就能使用。在VB.NET中,往往要用这样的语法来使用窗体: Dim frmForm2 As New Form1()frmForm2.Show 然而用这样的语法显示窗体,各个窗体之间的通信或数据传递就十分困难。比如新生成的frmForm2要想访问另一个窗体Form1就难以做到,因为frmForm2无法获得Form1实例的引用。许多初学者在使用窗体的时候弄不清类、实力和引用之间的关系因此常常遇到苦难。即使熟悉了这些概念,有时仍不能用正确的方法解决窗体互相访问的问题。许多解决方案,如通过构造函数传递数据,通过全局变量或者静态变量,甚至在模块中生成项目中所有窗体的实例等等都不是十分理想,他们会增加窗体之间的耦合性,或者浪费内存。为了彻底解决窗体创建和互相访问的问题,Visual Basic 2005引入了My.Forms。My.Forms虽然在My命名空间中,但是使用它不需要输入My.Forms。假设你有两个窗体——Form1和Form2,Form1是启动窗体,现在你要用代码显示Form2,新的语法是这样的: Form2.Show 这种用法和VB6几乎一样。Form2是窗体的类,怎么可以直接使用了呢?因为My.Forms为项目中每一个窗体维护了一个默认实例,其实现方法很像Singleton模式——每个窗体都有一个默认实例,而且有一个全局访问点,就通过窗体的类名即可访问到。这种方式彻底解决了窗体互相访问的问题,因为每个窗体都可以随时访问到任何其它的窗体的默认实例。比如要在Form2中修改Form1中一个TextBox的文字,只需要这样: Form1.textBox1.Text = "Hello" 不在需要任何传递参数的构造函数或者静态/全局变量。一个项目中多数窗体都是只需要一个实例的,所以这种模式适合任何项目使用。无论是新手或老手,我都建议尽情使用My.Forms的功能,他是解决窗体互访的最佳模式,同时不会浪费内存,因为它只有在第一次访问所需窗体的时候才建立它。 My.WebServices的原理与My.Forms如出一辙,因为原来WebService的代理类都需要手工创建对象才能使用。而WebService对于项目全局应该有一个一致的访问点,所以VB2005将代替你创建代理类的实例,并维护于My.WebServices中,你可以随时访问他。比如你的项目添加了一个Web引用到Service1服务,它提供了一个方法叫Method1,以前的Visual Basic你必须写成: Dim myService1 As New Service1()Dim myResult As Integer = myService1.Method1() 而现在,无论在何地,你都可以直接写: myResult = My.WebServices.Service1.Method1() 而无须手工创建代理类的对象了。 到今天为止,我已经介绍了My命名空间中的六个主要的功能区域,还剩下一个My.Settings,由于它在目前的BETA版本中还有缺陷,所以我将在以后找机会介绍它。下一次,我将介绍如何在C#和其他语言中使用My命名空间,以及如何通过编程扩展My命名空间的功能。...[阅读全文]

posted @ | Feedback (9) | Filed Under [ 技术随笔 Visual Basic 2005 新功能点评 ]

摘要:(注:这里说的=指的是判断等值的运算符,而不是赋值语句) 我们都知道Nothing代表空引用,代表C#中的null,在C#中我们判断是否空引用经常使用a == null这样写法,那么VB中呢,是否也可以写成 a = Nothing呢?答案是不行!判断是否空引用必须使用Is Nothing而不是= Nothing。VB与C#不一样,=符号只用于判断值的等价,不管是否值类型都是这样。在Option Strict打开的时候,未重载等值判断运算符的引用类型对象间都不允许使用=号。而Is运算符则总是用于引用比较,不能用于值类型。即时Option Strict关闭的时候,=运算符也会默认执行值的判断而不是引用的判断。那么是否空引用的问题显然是应该用Is Nothing,但为什么还可以写= Nothing呢,他代表什么? 原来VB的Nothing不仅仅表示空引用,而可以表示值类型类型的初始值。下面的代码在C#中是无法成立的。 Dim i As Integer = Nothing i并不是被设置为空引用,而是0,Integer的初始值。所以,如果将=用于Nothing,将进行与该类型初始值的比较,只有Is Nothing才能进行空引用的测试。而如果要测试是否等于初始值,我们直接用那种值类型的初始值的字面量更好,无须使用Nothing。比如If a = 0显然比If a = Nothing来的清楚。所以无论在任何语境下,都不要将=运算符用于 Nothing,特别是String类型的对象。初学者更要牢记这一点。...[阅读全文]

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