首页 文章

在调用基类型构造函数之前,是否可以强制VB.NET初始化实例变量?

提问于
浏览
27

在VB.NET中调试一个涉及初始化实例变量的顺序的特别棘手的问题之后,我发现我期望从C#开始的行为与VB.NET中的实际行为之间存在突破性差异 .

Nota bene:这个问题涉及VB.NET和C#的行为略有不同 . 如果你是一个无法提供答案的语言偏执,而不是“这就是为什么你应该使用C#,noob”,那么你在这里看不到任何东西;和睦相处 .

具体来说,我期待C# Language Specification概述的行为(强调增加):

当实例构造函数没有构造函数初始值设定项,或者它具有形式为base(...)的构造函数初始值设定项时,该构造函数隐式执行由其类中声明的实例字段的变量初始值设定项指定的初始化 . 这对应于在进入构造函数之后和直接调用直接基类构造函数之前立即执行的赋值序列 . 变量初始值设定项以它们出现在类声明中的文本顺序执行 .

与VB.NET语言规范中有关Instance Constructors的部分形成对比,后者表示(强调添加):

当构造函数的第一个语句的形式为MyBase.New(...)时,构造函数隐式执行由类型中声明的实例变量的变量初始值设定项指定的初始化 . 这对应于在调用直接基类型构造函数后立即执行的赋值序列 . 这样的排序可确保在执行有权访问实例的任何语句之前,所有基本实例变量都由其变量初始化程序初始化 .

The discrepancy here is immediately obvious. C#在调用基础构造函数之前初始化类级变量 . VB.NET完全相反,显然更喜欢在设置实例字段的值之前调用基础构造函数 .

如果你想看一些代码,this related question提供了一个更具体的分歧行为的例子 . 不幸的是,它没有提供关于如何强制VB.NET遵循C# Build 的模型的任何提示 .

我不太感兴趣为什么这两种语言的设计者选择这种不同的方法而不是我可能的问题解决方法 . 最后,我的问题如下: Is there any way that I can write or structure my code in VB.NET to force instance variables to be initialized before the base type's constructor is called ,C#中的标准行为是什么?

2 回答

  • 8

    如果您有构建期间要调用的虚拟成员(针对最佳建议,但我们已经同意),那么您需要将初始化移动到一个单独的方法中,以防止多次调用(即, init已经发生,立即返回) . 然后,虚拟成员和构造函数将在依赖发生初始化之前调用该方法 .

    它有点乱,并且可能代表轻微的性能损失,但在VB中你几乎无能为力 .

  • 2

    任何编写良好的类都必须确保任何可能在部分构造的实例上调用的虚拟成员都会表现得很明智 . C#采用的理念是,其字段初始值设定项运行的类实例将处于足够明智的状态,以允许使用虚拟方法 . VB.net采用的理念是允许字段初始化程序使用部分构造的对象(并且 - 只需要一点工作 - 传递给构造函数的任何参数)比保证字段初始化程序在任何虚拟程序之前运行更有用方法被称为 .

    恕我直言,从语言设计的角度来看,正确的方法是提供一种方便的方法来指示指定的字段初始化器应该“早”或“晚”运行,因为有时候每个都可能有用(尽管我更喜欢“迟“样式,因为它允许构造函数参数可用于字段初始化程序) . 例如:

    Class ParamPasserBase(Of T) ' Generic class for passing one constructor parameter
      Protected ConstructorParam1 As T
      Sub New(Param As T)
        ConstructorParam1 = Param
      End SUb
    End Class
    Class MyThing
      Inherits ParamPasserBase(Of Integer)
      Dim MyArray(ConstructorParam1-1) As String
      Sub New(ArraySize As Integer)
        MyBase.New(ArraySize)
      End Sub
      ...
    End Class
    

    在C#中,没有很好的方法让字段声明或初始化器使用传递给构造函数的参数 . 在vb中,如上所述,它可以合理地干净地完成 . 请注意,在vb中,还可以使用by-reference构造函数参数在运行任何字段初始值设定项之前走私构建的对象的副本;如果正确编写了类的Dispose例程来处理对于部分构造的对象,可以正确地清理在其构造函数中抛出的对象 .

相关问题