2011-01-05 9 views
27

インスタンス変数が初期化される順序を含むVB.NETで特に問題をデバッグした後、私はC#から予想された動作とVB.NETの実際の動作との間に大きな相違があることを発見しました。基本型コンストラクタを呼び出す前にVB.NETでインスタンス変数の初期化を強制できますか?

メモ:この質問は、VB.NETとC#の動作に若干の不一致があります。 以外の回答を提供できない言語の不名誉な人なら、C#、noob "を使うべきです。ここには何もありません。親切に一緒に移動します。

具体的には、私はC# Language Specification(強調追加)によって概説挙動を期待:

インスタンスコンストラクタは、コンストラクタの初期化子を有していない、またはそれがフォームbase(...)のコンストラクタ初期化子を有する場合、そのコンストラクタは暗黙的に実行しますそのクラスで宣言されたインスタンスフィールドの変数初期化子によって指定された初期化。 これは、コンストラクタへのエントリ時および直接ベースクラスコンストラクタの暗黙的な呼び出しの前に直ちに実行される一連の割り当てに対応します。変数初期化子は、クラス宣言に現れるテキスト順に実行されます。

言うVB.NET言語仕様の一部とInstance Constructorsに関することコントラスト、(強調追加):

コンストラクタの最初の文の形式MyBase.New(...)である場合、コンストラクタは暗黙的に実行しますその型で宣言されたインスタンス変数の変数初期化子によって指定された初期化。 これは、直接ベース型コンストラクタを呼び出した直後に実行される一連の代入に対応します。このような順序付けにより、すべての基本インスタンス変数は、そのインスタンスにアクセスできるステートメントが実行される前に、変数初期化子によって初期化されます。

ここでの矛盾はすぐに明らかです。 C#は、の前にクラスコンストラクターを呼び出すを初期化します。 VB.NETはまったく逆の動作をしますが、のインスタンスフィールドの値を設定する前に、ベースコンストラクタを呼び出す方が好きです。

コードを表示したい場合は、this related questionが発散動作のより具体的な例を示します。残念ながら、VB.NETをC#によって確立されたモデルに従うように強制する方法についてのヒントはありません。

私は、2つの言語の設計者が、問題の可能性のある回避策のような多岐にわたるアプローチを選んだ理由に興味がありません。最終的に私の質問は次のようになります:VB.NETでコードを記述したり構造化したりしてインスタンス変数を初期化する方法がありますかの前に、ベースタイプのコンストラクタはと同じです。 ?

+1

これを変更することは可能ではないと私は考えています。そして、VBコンパイラはこれを行う試みに対して闘うでしょう。このような動作に頼っているのであれば、通常、コード内のどこかに問題があることを示しています。 (他の質問のサンプルコードのように)長い間コンストラクタ内の仮想メンバを呼び出すことについてアドバイスがありました。 –

+0

@Damien:コンストラクタ内で仮想メンバを呼び出すのは悪い習慣です。残念ながら、その決定は私のものではありませんでした。私はフレームワークによって提供される型をサブクラス化し、その仮想メソッドの1つをオーバーライドします。私は問題が私のコードの外にあると確信しています、そして、私がこれを修正するために考えている唯一の事は、私に "醜いハック"を叫びます。 –

+0

も参照してくださいhttp://stackoverflow.com/q/12585555/185593 – serhio

答えて

8

建設中に呼び出される予定の仮想メンバーがある場合(最善のアドバイスを受けていますが、すでに同意しています)、初期化を別の方法に移す必要があります。呼び出し(つまり、initがすでに発生している場合は、すぐに戻ります)。そのメソッドは、仮想メンバとコンストラクタによって呼び出され、初期化が発生したことに依存します。

これはちょっと面倒なことですが、マイナーなパフォーマンス上のペナルティがあるかもしれませんが、VBでできることはほとんどありません。

2

よく書き込まれたクラスは、部分的に構築されたインスタンスで呼び出される可能性のあるすべての仮想メンバーが確実に動作するようにする必要があります。 C#では、フィールドイニシャライザが実行されたクラスインスタンスが、仮想メソッドを使用できるように十分に感知できる状態になるという考えを示します。 VB.netは、フィールドイニシャライザが部分的に構築されたオブジェクトを使用できるようにするという考え方を採用しています(小さな作業では、コンストラクタに渡されるパラメータはすべて、フィールドイニシャライザが仮想メソッドが呼び出されます。

IMHOでは、言語設計の観点からは、指定されたフィールド初期化子を「早い」または「遅い」で実行する必要があることを示す便利な手段を提供していました。私はコンストラクタのパラメータをフィールドイニシャライザで利用できるようにしているので、 "遅い"スタイルを好む。たとえば:C#ので

 
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 

は、フィールド宣言または初期化子がコンストラクタに渡される引数を利用することが持っている何の良い方法はありません。 vbでは、上記のように合理的にきれいに行うことができます。 vbでは、フィールド初期化子を実行する前に、参照渡しのコンストラクタパラメータを使用して構築中のオブジェクトのコピーを密輸することもできます。クラスのDisposeルーチンが、部分的に構築されたオブジェクトを処理するために適切に記述されている場合、そのコンストラクタにスローされるオブジェクトを適切にクリーンアップすることができます。

+1

私はあなたがここで言うことを続けているかどうかはわかりません。私はいずれかの言語を再設計する能力を持っていないし、特に選択肢の相対的なメリットを議論しているわけではない。さらに、 'RIAA()'はコードサンプルのどこから来ますか? VB.NETでこれを行うことができると言っています。私は構文に慣れていない。 'RIAA()'はあなたが書いたカスタムラッパークラスですか?そして、それは 'using'ステートメントとどのように違うのでしょうか?それ以外に、「古い」VB.NETと「新しい」VB.NETの区別は何ですか? 1つはタイプセーフですが、もう1つはありませんか?破壊とは何が関係していますか? –

+0

@Cody Grey:RIAAは、確定的な自動クリーンアップ(C++/RIAAスタイル)のIDisposableを登録し、それを返すクラスメソッドです。 vb.netでは、フィールドイニシャライザはdisposablesのリストが作成された後に実行されるので、RIAAメソッドはそれらをそのリストに追加できます。 VB.Netでは、コンストラクタからコンストラクタを密輸して、コンストラクタがスローされた場合に登録されたIDisposableオブジェクトをクリーンアップするファクトリメソッドにラップすることもできます。 – supercat

+0

このRIAA方法についての議論ができますか?私はオンラインでドキュメントを見つけることができません。また、基本クラスのコンストラクタが呼び出される前に、インスタンスを初期化することでアイテムを破棄することが何であるかもわかりません。私の質問はオブジェクトに関するものではなく、 'IDisposable'を実装するものはずっと少ないです。テストケースは、実際にはInteger型またはBoolean型であり、どちらも値型であり、処分する必要はありません。 RIAAは役に立たないので、ここでは役に立たない。 –

関連する問題