1

私は以下で遊んでいます:

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

しかし、取得した値に対して何も取得しません。その理由は、派生クラスのフィールドは、基本クラスのコンストラクターが呼び出されるまで初期化されないためと思われます...さらに複雑なことに、次のようにします。

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

コンパイラは、派生クラスのコンストラクタの最初の行で基本クラスのコンストラクタを呼び出す必要があると文句を言います。

派生クラスのフィールドが初期化されるまで、基本クラスのコンストラクターの実行を遅らせる方法はありますか?

4

2 に答える 2

1

これを行う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()メソッドに移動して、探している効果を得ることができます。

于 2012-05-12T21:16:42.417 に答える
1

これは、一般に管理対象言語でよく知られている動作です。驚いたことに、VB.NET言語仕様で明示的に言及されているのを見つけることができないので、自分で説明して説明する必要があります。

CLIはフィールド初期化子を直接サポートしていますが、フィールドをサポートするのに十分な強度はありません。単純なデータのみを格納でき、値型を考えます。TypedLeafクラスのように参照型を初期化するには、コードを実行する必要があります。また、コードはフィールド初期化子に格納できず、メソッド内にのみ表示できます。

したがって、VB.NETコンパイラは、フィールド初期化式を次の論理的な場所であるクラスコンストラクタに移動することにより、この制限を回避します。これは完全に自動化されており、コンストラクターを自分で提供する場合は、必要に応じて新しいオペレーター呼び出しを挿入して、コンストラクターを実際に書き換えます。

ここで選択肢があり、基本クラスのコンストラクター呼び出しのまたはにそれらの呼び出しを移動できます。あなたはすでに行われた選択を知っています、それはに起こります。フィールド初期化子は、まだ初期化されていない基本クラスのメンバーを監視できないようにする必要があるという正当な理由があります。回避策の試みは、実際にはかなり英雄的なコンパイラの記述スキルであり、実際には、ベースコンストラクタが最初に呼び出されることを確認します。

残念ながら、基本コンストラクターが呼び出される前に発生した場合は、実際に満足している場合があります。それは正当なことですが、残念ながら許可されていません。言語設計者は足を踏み入れ、「これを行うには1つの方法しかサポートしていません」と宣言しました。公正な呼びかけ、そのような基本は予測可能である必要があります。

回避策は簡単です。基本クラスにProtectedメソッドを配置し、「Initialize」と言って、コンストラクターにあるコードをそのメソッドに移動するだけです。派生クラスのコンストラクターでは、そのメソッドを呼び出すだけです。コンストラクターの書き換えにより、基本コンストラクター呼び出しが最初で、フィールド初期化子コードが2番目になり、メソッド呼び出しが3番目になります。その呼び出しを行うことを忘れないようにするためのマイナス33.3ポイントなので、Nothingが表示されたときにInvalidOperationExceptionをスローするコードを追加します。

于 2012-05-12T21:57:31.440 に答える