.NETプログラミング研究 第30号 †
.NET Tips †
型のメンバを動的に呼び出す †
ここでは、Typeオブジェクトで表された型のインスタンスの作成、メソッドの呼び出し、プロパティ、フィールドの設定と取得の方法について説明します。
まず、次のようなクラスが宣言されているものとします。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
| | Namespace MyNamespace
Public Class [MyClass]
Private PrivateField As Integer
Public StringArray() As String
Public Property PublicProperty() As Integer
Get
Return PrivateField
End Get
Set(ByVal Value As Integer)
PrivateField = Value
End Set
End Property
Default Public Property Item(ByVal index As Integer) As String
Get
Return index.ToString()
End Get
Set(ByVal Value As String)
End Set
End Property
Public Sub New(ByVal val As Integer)
PrivateField = val
StringArray = New String() {"1", "2", "3"}
End Sub
Public Sub New()
PrivateField = 0
StringArray = New String() {"1", "2", "3"}
End Sub
Public Overloads Function PublicMethod( _
ByVal num1 As Integer, ByVal num2 As Integer) As String
Return (num1 - num2).ToString()
End Function
Public Overloads Function PublicMethod( _
ByVal num1 As Integer) As String
Return num1.ToString()
End Function
Public Overloads Function PublicMethod() As String
Return PublicMethod(0)
End Function
Public Overloads Shared Function StaticMethod( _
ByVal num1 As Integer, ByVal num2 As Integer) As Integer
Return num1 + num2
End Function
Public Overloads Shared Function StaticMethod( _
ByVal num1 As Integer, ByVal num2 As Integer, _
ByRef num3 As Integer) As Integer
Dim sum As Integer = num1 + num2 + num3
num3 = num1 - num2 - num3
Return sum
End Function
End Class
End Namespace
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
| | namespace MyNamespace
{
public class MyClass
{
private int PrivateField;
public string[] StringArray;
public int PublicProperty
{
get
{
return PrivateField;
}
set
{
PrivateField = value;
}
}
public string this[int index]
{
get
{
return index.ToString();
}
set
{
}
}
public MyClass(int val)
{
PrivateField = val;
StringArray = new string[] {"1", "2", "3"};
}
public MyClass()
{
PrivateField = 0;
StringArray = new string[] {"1", "2", "3"};
}
public string PublicMethod(int num1, int num2)
{
return (num1 - num2).ToString();
}
public string PublicMethod(int num1)
{
return num1.ToString();
}
public string PublicMethod()
{
return PublicMethod(0);
}
public static int StaticMethod(int num1, int num2)
{
return num1 + num2;
}
public static int StaticMethod(
int num1, int num2, ref int num3)
{
int sum = num1 + num2 + num3;
num3 = num1 - num2 - num3;
return sum;
}
}
}
|
Typeオブジェクトのメンバを呼び出すには、InvokeMemberメソッドを使用します。InvokeMemberメソッドにより、インスタンスの作成、メソッドの呼び出し、プロパティ、フィールドの設定と取得がすべてできます。
次にInvokeMemberメソッドを用いて、MyClassクラスのメンバを呼び出す例を示します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
| |
Dim num As Integer
Dim str As String
Dim t As Type = GetType(MyNamespace.MyClass)
Dim target As Object = t.InvokeMember(Nothing, _
BindingFlags.CreateInstance, _
Nothing, _
Nothing, _
New Object() {100})
str = CStr(t.InvokeMember("PublicMethod", _
BindingFlags.InvokeMethod, _
Nothing, _
target, _
New Object() {12}))
num = CInt(t.InvokeMember("StaticMethod", _
BindingFlags.InvokeMethod, _
Nothing, _
Nothing, _
New Object() {1, 2}))
Dim objs() As Object = {1, 2, 0}
num = CInt(t.InvokeMember("StaticMethod", _
BindingFlags.InvokeMethod, _
Nothing, _
Nothing, _
objs))
Console.WriteLine(objs(2))
str = CStr(t.InvokeMember("PublicMethod", _
BindingFlags.InvokeMethod, _
Nothing, target, _
New Object() {5, 7}, _
Nothing, _
Nothing, _
New String() {"num2", "num1"}))
t.InvokeMember("PublicProperty", _
BindingFlags.SetProperty, _
Nothing, _
target, _
New Object() {100})
num = CInt(t.InvokeMember("PublicProperty", _
BindingFlags.GetProperty, _
Nothing, _
target, _
Nothing))
t.InvokeMember("Item", _
BindingFlags.SetProperty, _
Nothing, _
target, _
New Object() {1, "two"})
str = CStr(t.InvokeMember("Item", _
BindingFlags.GetProperty, _
Nothing, _
target, _
New Object() {1}))
t.InvokeMember("PrivateField", _
BindingFlags.Public Or BindingFlags.NonPublic Or _
BindingFlags.Instance Or BindingFlags.SetField, _
Nothing, _
target, _
New Object() {0})
num = CInt(t.InvokeMember("PrivateField", _
BindingFlags.Public Or BindingFlags.NonPublic Or _
BindingFlags.Instance Or BindingFlags.GetField, _
Nothing, _
target, _
Nothing))
t.InvokeMember("StringArray", _
BindingFlags.SetField, _
Nothing, _
target, _
New Object() {0, "ゼロ"})
str = CStr(t.InvokeMember("StringArray", _
BindingFlags.GetField, _
Nothing, _
target, _
New Object() {0}))
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
| |
int num;
string str;
Type t = typeof(MyNamespace.MyClass);
object target = t.InvokeMember(null,
BindingFlags.CreateInstance,
null,
null,
new object[] {100});
str = (string) t.InvokeMember("PublicMethod",
BindingFlags.InvokeMethod,
null,
target,
new object[] {12});
num = (int) t.InvokeMember("StaticMethod",
BindingFlags.InvokeMethod,
null,
null,
new object[] {1, 2});
object[] objs = new object[] {1, 2, 0};
num = (int) t.InvokeMember("StaticMethod",
BindingFlags.InvokeMethod,
null,
null,
objs);
Console.WriteLine(objs[2]);
str = (string) t.InvokeMember("PublicMethod",
BindingFlags.InvokeMethod, null, target,
new object[] {5, 7},
null,
null,
new string[] {"num2", "num1"});
t.InvokeMember("PublicProperty",
BindingFlags.SetProperty,
null,
target,
new object[] {100});
num = (int) t.InvokeMember("PublicProperty",
BindingFlags.GetProperty,
null,
target,
null);
t.InvokeMember("Item",
BindingFlags.SetProperty,
null,
target,
new object[] {1, "two"});
str = (string) t.InvokeMember("Item",
BindingFlags.GetProperty,
null,
target,
new object[] {1});
t.InvokeMember("PrivateField",
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.SetField,
null,
target,
new object[] {0});
num = (int) t.InvokeMember("PrivateField",
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.GetField,
null,
target,
null);
t.InvokeMember("StringArray",
BindingFlags.SetField,
null,
target,
new object[] {0, "ゼロ"});
str = (string) t.InvokeMember("StringArray",
BindingFlags.GetField,
null,
target,
new object[] {0});
|
上記のようにType.InvokeMemberメソッドを使う以外に、MethodInfo、ConstructorInfoクラスのInvokeメソッドを使ってメソッドやコンストラクタを呼び出したり、PropertyInfo、FieldInfoクラスのSetValue、GetValueメソッドを使ってプロパティやフィールドの値を取得、設定することもできます。
次にこれらの方法を使った簡単な例を示します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| |
Dim t As Type = GetType(MyNamespace.MyClass)
Dim ci As ConstructorInfo = _
t.GetConstructor(New Type() {GetType(Integer)})
Dim target As Object = ci.Invoke(New Object() {5})
Dim mi As MethodInfo = t.GetMethod("PublicMethod", New Type() {})
Dim str As String = CStr(mi.Invoke(target, New Object() {}))
Dim pi As PropertyInfo = t.GetProperty("PublicProperty")
pi.SetValue(target, 1, Nothing)
Dim num As Integer = CInt(pi.GetValue(target, Nothing))
Dim fi As FieldInfo = t.GetField("PrivateField", _
BindingFlags.Public Or BindingFlags.NonPublic Or _
BindingFlags.Instance)
fi.SetValue(target, -1)
num = CInt(fi.GetValue(target))
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| |
Type t = typeof(MyNamespace.MyClass);
ConstructorInfo ci = t.GetConstructor(new Type[] {typeof(int)});
object target = ci.Invoke(new object[] {5});
MethodInfo mi = t.GetMethod("PublicMethod", new Type[] {});
string str = (string) mi.Invoke(target, new object[] {});
PropertyInfo pi = t.GetProperty("PublicProperty");
pi.SetValue(target, 1, null);
int num = (int) pi.GetValue(target, null);
FieldInfo fi = t.GetField("PrivateField",
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance);
fi.SetValue(target, -1);
num = (int) fi.GetValue(target);
|
さらにインスタンスの作成は、AppDomain.CreateInstanceメソッド、Assembly.CreateInstanceメソッドや、Activator.CreateInstanceメソッド(配列を作成するときは、Array.CreateInstanceメソッド)などを使用することによっても可能です。(一般的によく使われているのは、Activator.CreateInstanceメソッドです。)
補足:
ここで紹介したように、コンパイル時に型が不明であるオブジェクトをObject型変数に代入し、実行時にそのメソッドやプロパティなどを呼び出す方法を、遅延バインディングと呼びます(これに対して、コンパイル時に型が明確に宣言された変数にオブジェクトが代入される時は、事前バインディングされます)。VB.NETでは、Option StrictステートメントをOffにすることにより(あるいは、コンパイラオプションに"/optionstrict+"を指定)、暗黙の遅延バインディングを使用できますので、InvokeMemberメソッドを使ってメソッドやプロパティを呼び出す必要がなくなります。
VB.NETの暗黙の遅延バインディングにより、メソッドやプロパティを呼び出す例を以下に示します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| |
Dim num As Integer
Dim str As String
Dim t As Type = GetType(MyNamespace.MyClass)
Dim target As Object = t.InvokeMember(Nothing, _
BindingFlags.CreateInstance, _
Nothing, _
Nothing, _
New Object() {100})
num = target.PublicMethod(12)
target.PublicProperty = 100
str = target(1)
|
.NET質問箱 †
「.NET質問箱」では、「どぼん!のプログラミング掲示板」に書き込まれた.NETプログラミングに関する投稿をQ&A形式にまとめ、紹介します。
フォームに配置されているコントロールを名前で探すには? †
【質問】
フォームに配置されているコントロールをその名前(文字列)で探し、アクセスしたいのですが、どのようにすればよいのでしょうか?
【回答】
Visual Studio .NETのフォームデザイナなどを使ってフォームにコントロールを追加した時、コントロールの"(Name)"プロパティに指定された名前と同じ名前のフィールドがフォームクラスに追加され、このフィールドにそのコントロールのオブジェクトが格納されます(*1)。つまり、フォームクラスのフィールドを名前で探し、見つかればそのフィールドの値を取得するという方法で問題が解決できそうです。そのためには、上で説明したリフレクションの知識が役に立ちます。
(*1)VB.NETでは、WithEventsが付いていると、フィールドの名前は"(Name)"の頭に"_"が付いた名前になり、"(Name)"はプロパティの名前となります。
フォームに配置されたコントロールを名前で探すメソッドの例を紹介します(FindControlByFieldNameメソッド)。このメソッドをコントロールを検索したいフォームのクラスに書き足してください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| | Public Function FindControlByFieldName(ByVal name As String) As Object
Dim t As System.Type = Me.GetType()
Dim pi As System.Reflection.PropertyInfo = _
t.GetProperty(name, _
System.Reflection.BindingFlags.Public Or _
System.Reflection.BindingFlags.NonPublic Or _
System.Reflection.BindingFlags.Instance Or _
System.Reflection.BindingFlags.DeclaredOnly)
If Not pi Is Nothing Then
Return pi.GetValue(Me, Nothing)
End If
Dim fi As System.Reflection.FieldInfo = _
t.GetField(name, _
System.Reflection.BindingFlags.Public Or _
System.Reflection.BindingFlags.NonPublic Or _
System.Reflection.BindingFlags.Instance Or _
System.Reflection.BindingFlags.DeclaredOnly)
If fi Is Nothing Then
Return Nothing
End If
Return fi.GetValue(Me)
End Function
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| | public object FindControlByFieldName(string name)
{
System.Type t = this.GetType();
System.Reflection.FieldInfo fi = t.GetField(
name,
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.DeclaredOnly);
if (fi == null)
return null;
return fi.GetValue(this);
}
|
次に、"TextBox1"という名前のテキストボックスコントロールを探し、そのTextの内容を変更する例を示します。
1
2
3
4
| | Dim tb As TextBox = CType(FindControlByFieldName("TextBox1"), TextBox)
tb.Text = "*" + tb.Text
|
1
2
3
4
| | TextBox tb = (TextBox) FindControlByFieldName("TextBox1");
tb.Text = "*" + tb.Text;
|
上記の方法で解決できると思いますが、別の方法として、コントロールのNameプロパティを調べる方法を紹介します。VS.NETのフォームデザイナでコントロールを追加すると、"(Name)"プロパティはそのコントロールのNameプロパティと同じになりますので、指定された文字列のNameプロパティを持つコントロールを探して返すメソッドを作成します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| | Public Function FindControlByName( _
ByVal ctr As System.Windows.Forms.Control, _
ByVal name As String) As System.Windows.Forms.Control
If ctr.Name = name Then
Return ctr
End If
Dim c As System.Windows.Forms.Control
For Each c In ctr.Controls
Dim res As System.Windows.Forms.Control = _
FindControlByName(c, name)
If Not (res Is Nothing) Then
Return res
End If
Next c
Return Nothing
End Function
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| | public System.Windows.Forms.Control FindControlByName(
System.Windows.Forms.Control ctr, string name)
{
if (ctr.Name == name)
return ctr;
foreach (System.Windows.Forms.Control c in ctr.Controls)
{
System.Windows.Forms.Control res = FindControlByName(c, name);
if (res != null)
return res;
}
return null;
}
|
使用例は、次のようになります。
1
2
3
4
| | Dim tb As TextBox = CType(FindControlByName(Me, "TextBox1"), TextBox)
tb.Text = "*" + tb.Text
|
1
2
3
4
| | TextBox tb = (TextBox) FindControlByName(this, "TextBox1");
tb.Text = "*" + tb.Text;
|
このFindControlByNameメソッドは、コントロールの子コントロールも検索しますが、フォームのcomponentsフィールドに追加されるコントロール(ImageList、ToolTip、NotifyIconなど)は検索しないことに注意してください。
○この記事の基になった掲示板のスレッド
.NET Compact Frameworkで二重起動を禁止するには? †
【質問】
DOBON.NETの「二重起動を禁止する」で紹介されている方法が、.NET Compact Frameworkでは使用できません。.NET Compact Frameworkで二重起動を禁止するには、どうすればよいのでしょうか?
【回答】
.NET Compact Frameworkのアプリケーションは、Pocket PCでは、自動的に二重起動されないようになっています。ただし、Windows CE .NETでは、自分で二重起動をチェックする必要があります。
.NET Compact Frameworkで二重起動を禁止するサンプルがGotDotNetにあります。
このサンプルでは、CreateMutex関数を使うことにより、二重起動のチェックを行っています。詳しい内容は、このサンプルをご覧ください。
○この記事の基になった掲示板のスレッド
コメント †
|