コントロール配列について への返答
投稿で使用できる特殊コードの説明。(別タブで開きます。)
以下の返答は逆順(新しい順)に並んでいます。
投稿者 YuO  (社会人)
投稿日時
2010/8/18 13:23:06
ひゃあ,ごめんなさい。初期化順序が間違っていました。
まさかC#とVBで初期化順序が違うとは思わなかった……。
# すいません,基本C#屋なんです,と言い訳しておきます。
という,同じ動作をするはずのコードに対して,Class2を作成してみたところ,VBでは
と,基本クラスのフィールド→基本クラスのコンストラクタ→派生クラスのフィールド→派生クラスのコンストラクタの順序で実行しました。
それに対して,C#では,
と,派生クラスのフィールド→基本クラスのフィールド→基本クラスのコンストラクタ→派生クラスのコンストラクタの順序で実行しました。
根本的にこんなのCLIで決まっているだろうと思っていたのですが,甘かったです。
まさかC#とVBで初期化順序が違うとは思わなかった……。
# すいません,基本C#屋なんです,と言い訳しておきます。
Imports System
Namespace VbLibrary
Public Class Class1
Public F1 As New Class3(GetType(Class1).FullName)
Public Sub New()
Console.WriteLine("{0}.ctor()", GetType(Class1).FullName)
End Sub
End Class
Public Class Class2
Inherits Class1
Public F2 As New Class3(GetType(Class2).FullName)
Public Sub New()
MyBase.New()
Console.WriteLine("{0}.ctor()", GetType(Class2).FullName)
End Sub
End Class
Public Class Class3
Public Sub New(ByVal s As String)
Console.WriteLine("{0}.ctor({1})", Me.GetType().FullName, s)
End Sub
End Class
End Namespace
using System;
namespace CsLibrary
{
public class Class1
{
public Class3 F1 = new Class3(typeof(Class1).FullName);
public Class1 ()
{
Console.WriteLine("{0}.ctor()", typeof(Class1).FullName);
}
}
public class Class2 : Class1
{
public Class3 F2 = new Class3(typeof(Class2).FullName);
public Class2 ()
: base()
{
Console.WriteLine("{0}.ctor()", typeof(Class2).FullName);
}
}
public class Class3
{
public Class3 (string s)
{
Console.WriteLine("{0}.ctor({1})", this.GetType().FullName, s);
}
}
}
という,同じ動作をするはずのコードに対して,Class2を作成してみたところ,VBでは
VbLibrary.Class3.ctor(VbLibrary.Class1)
VbLibrary.Class1.ctor()
VbLibrary.Class3.ctor(VbLibrary.Class2)
VbLibrary.Class2.ctor()
VbLibrary.Class1.ctor()
VbLibrary.Class3.ctor(VbLibrary.Class2)
VbLibrary.Class2.ctor()
と,基本クラスのフィールド→基本クラスのコンストラクタ→派生クラスのフィールド→派生クラスのコンストラクタの順序で実行しました。
それに対して,C#では,
CsLibrary.Class3.ctor(CsLibrary.Class2)
CsLibrary.Class3.ctor(CsLibrary.Class1)
CsLibrary.Class1.ctor()
CsLibrary.Class2.ctor()
CsLibrary.Class3.ctor(CsLibrary.Class1)
CsLibrary.Class1.ctor()
CsLibrary.Class2.ctor()
と,派生クラスのフィールド→基本クラスのフィールド→基本クラスのコンストラクタ→派生クラスのコンストラクタの順序で実行しました。
根本的にこんなのCLIで決まっているだろうと思っていたのですが,甘かったです。
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2010/8/18 13:04:43
> Me.Controls("TextBox" & i).Text = "ここに入力"
この方法の場合、フォーム上に配置した TextBox は操作できますが、
Panel や GroupBox に配置した TextBox は操作できません。
Panel 上のコントロールを拾う場合には、
Panel1.Controls(名前).Text = "ここに入力"
のように記述するか、もしくは Find メソッドを使って
Me.Controls.Find(名前, True)(0).Text = "ここに入力"
下位要素まで検索するようにすれば OK です。
この方法の場合、フォーム上に配置した TextBox は操作できますが、
Panel や GroupBox に配置した TextBox は操作できません。
Panel 上のコントロールを拾う場合には、
Panel1.Controls(名前).Text = "ここに入力"
のように記述するか、もしくは Find メソッドを使って
Me.Controls.Find(名前, True)(0).Text = "ここに入力"
下位要素まで検索するようにすれば OK です。
投稿者 るきお  (社会人)
投稿日時
2010/8/18 12:45:02
>・フィールドの初期化を派生クラス→基本クラスの順序で行う
なるほどです。
これが実行されるときはまだ、TextBox1~5がまだNothingなんですね。
回避策として名前で指定する方法も紹介します。
なるほどです。
Dim TextBoxs() As TextBox = {TextBox1, TextBox2, TextBox3, TextBox4, TextBox5} 'コントロール配列
これが実行されるときはまだ、TextBox1~5がまだNothingなんですね。
回避策として名前で指定する方法も紹介します。
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
For i As Integer = 1 To 5
Me.Controls("TextBox" & i).Text = "ここに入力"
Next
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
For i As Integer = 1 To 5
Me.Controls("TextBox" & i).Text = String.Empty
Next
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim sum As Integer
For i As Integer = 1 To 5
sum += CInt(Me.Controls("TextBox" & i).Text)
Next i
TextBox6.Text = sum
End Sub
投稿者 YuO  (社会人)
投稿日時
2010/8/18 12:28:59
仕様書上の記述を探しきれなかったのですが,初期化の順序は,
・フィールドの初期化を派生クラス→基本クラスの順序で行う
・コンストラクタを基本クラス→派生クラスの順序で呼び出す
ことによって行われます。
TextBox1などのコントロールは,コンストラクタ中で作成されます。
そのため,フィールドで初期化しても作成される前のオブジェクトが取り出されることになります。
TextBoxsの初期化を,コンストラクタに移せば,やりたいことができると思います。
・フィールドの初期化を派生クラス→基本クラスの順序で行う
・コンストラクタを基本クラス→派生クラスの順序で呼び出す
ことによって行われます。
TextBox1などのコントロールは,コンストラクタ中で作成されます。
そのため,フィールドで初期化しても作成される前のオブジェクトが取り出されることになります。
TextBoxsの初期化を,コンストラクタに移せば,やりたいことができると思います。
Public Partial Class Form1
Private TextBoxes As TextBox()
Public Sub New ()
InitializeComponent()
TextBoxes = New TextBox() { TextBox1, TextBox2, TextBox3, TextBox4, TextBox5 }
End Sub
End Class
投稿者 heaven  (社会人)
投稿日時
2010/8/18 11:22:24
追記ですが、環境開発はVB2005です。
投稿者 heaven  (社会人)
投稿日時
2010/8/18 11:10:39
初めまして、初投稿させていただきます。
現在複数のコントロールを配列にまとめて処理を行っているのですが、クラスレベルでコントロール配列を宣言して実際に実行してみると、「オブジェクト参照がオブジェクト インスタンスに設定されていません。」と出てエラーが出てしまいます。
ただし、各プロシージャー内でコントロールを配列を宣言すればエラーは起こらないのですが、大量のコントロール配列をプロシージャー毎に宣言するとコードが莫大になってしまうのでなんとか避けたいです。
クラスレベルでコントロール配列を宣言して使い回す事はできないでしょうか?
現在複数のコントロールを配列にまとめて処理を行っているのですが、クラスレベルでコントロール配列を宣言して実際に実行してみると、「オブジェクト参照がオブジェクト インスタンスに設定されていません。」と出てエラーが出てしまいます。
ただし、各プロシージャー内でコントロールを配列を宣言すればエラーは起こらないのですが、大量のコントロール配列をプロシージャー毎に宣言するとコードが莫大になってしまうのでなんとか避けたいです。
クラスレベルでコントロール配列を宣言して使い回す事はできないでしょうか?
' 例:
Dim TextBoxs() As TextBox = {TextBox1, TextBox2, TextBox3, TextBox4, TextBox5} 'コントロール配列
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
For i As Integer = 0 To 4
TextBoxs(i).Text = "ここに入力"
Next
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
For i As Integer = 0 To 4
TextBoxs(i).Text = String.Empty
Next
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim sum As Integer
For i As Integer = 0 To 4
sum += CInt(TextBoxs(i).Text)
Next i
TextBox6.Text = sum
End Sub
YuOさまの方法で無事に思い通りに作ることが出来ました。
非常に助かりました。
また、コントロールが10個以上ある場合は、管理人さまの方法も合理的だと思いましたので参考にさせていただきます。
今後ともよろしくお願いいたします。