初始化顺序

时间:2012-05-12 21:04:38

标签: vb.net

我正在玩以下内容:

Public MustInherit Class TempTable
    Public Sub New()
        For Each f As FieldInfo In Me.GetType().GetFields
            Dim l As TypedLeaf = CType(f.GetValue(Me), TypedLeaf)
            Console.WriteLine(l.Name)
        Next
    End Sub
End Class

Public Class JMTempTable
    Inherits TempTable

    Public KeyIndex As New TypedLeaf(Me, "KeyIndex", OQL.Type.AUTONUMBER)
    Public Debit As New TypedLeaf(Me, "Debit", OQL.Type.DECIMAL(16, 2))

    Public Sub New()
        MyBase.New()
    End Sub
End Class

但是为获取的值获取Nothing。原因似乎是派生类'字段在基类'构造函数被调用之后才被初始化......如果我这样做会使问题进一步复杂化:

Public Class JMTempTable
    Inherits TempTable

    Public KeyIndex As TypedLeaf
    Public Debit As TypedLeaf

    Public Sub New()
        KeyIndex = New TypedLeaf(Me, "KeyIndex", OQL.Type.AUTONUMBER)
        Debit = New TypedLeaf(Me, "Debit", OQL.Type.DECIMAL(16, 2))

        MyBase.New()
    End Sub
End Class

编译器会抱怨必须在派生类的构造函数的第一行中调用基类构造函数...

有没有办法可以延迟基类构造函数的运行,直到派生类的字段初始化为止?

2 个答案:

答案 0 :(得分:1)

这是一种方式(也许 方式):

Public MustInherit Class TempTable
    Public Sub New()
        Initialize()
        For Each f As FieldInfo In Me.GetType().GetFields
            Dim l As TypedLeaf = CType(f.GetValue(Me), TypedLeaf)
            Console.WriteLine(l.Name)
        Next
    End Sub

    Protected MustOverride Sub Initialize()
End Class

Public Class JMTempTable
    Inherits TempTable

    Public KeyIndex As TypedLeaf()
    Public Debit As TypedLeaf()

    Public Sub New() ' Optional block. You don't have to explicitly define a default constructor.
        MyBase.New()
    End Sub

    Protected Overrides Sub Initialize()
        KeyIndex = New TypedLeaf(Me, "KeyIndex", OQL.Type.AUTONUMBER)
        Debit = New TypedLeaf(Me, "Debit", OQL.Type.DECIMAL(16, 2))
    End Sub
End Class

抽象Initialize()方法强制继承者拥有一个名为Initialize()的方法。当您调用MyBase.New()时,会隐式调用此方法。这意味着您现在可以将初始化逻辑移出构造函数并移动到Initialize()方法中以获得您正在寻找的效果。

答案 1 :(得分:1)

这在管理语言中通常是众所周知的行为。令人惊讶的是我无法在VB.NET语言规范中明确提到它,所以我必须自己解释它。

CLI直接支持字段初始值设定项,但它们不足以支持您的字段。他们只能存储简单的数据,思考价值类型。初始化引用类型,如TypedLeaf类需要执行代码。并且代码不能存储在字段初始值设定项中,它只能出现在方法内部。

因此,VB.NET编译器通过 字段初始化表达式移动到下一个逻辑位置(类构造函数)来解决该限制。这是完全自动的,它实际上重写您的构造函数,以防您自己提供,根据需要注入 new 运算符调用。

现在有一个选择,它可以在基类构造函数调用之后或之前移动那些调用。您已经知道所做出的选择,它发生在之后。有理由认为字段初始值设定项不应该能够观察到尚未初始化的基类成员。您尝试解决方法实际上是非常英雄的编译器编写技能,它实际上检查首先调用基础构造函数。

不幸的是,如果它发生在基础构造函数调用之前,你发现了一个实际上更幸福的情况。这是合理的,但不幸的是不允许,语言设计师放下脚步并声明“我们只支持一种方式来做到这一点”。公平的电话,这样的基础知识需要是可预测的。

解决方法很简单。只需在基类中放置一个Protected方法,说“Initialize”,然后将构建器中现有的代码移动到该方法。在派生类构造函数中,只需调用该方法即可。构造函数重写确保基本构造函数调用是第一个,字段初始化程序代码是第二个,使方法调用第三个。减去33.3分必须记住进行该调用,所以添加代码以在看到Nothing时抛出InvalidOperationException。