- - PR -
FieldInfoのSetValueメソッド
1
投稿者 | 投稿内容 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2003-04-08 18:20
こんにちは。
訳あって、FieldInfoクラスを使用して値の入出力を行いたいのですが、 SetValueメソッドが期待する動きをしてくれません。 以下がそのソースですが、最終行で"ABC"とメッセージボックスに表示 されません。 書き方間違ってますか? ちなみにp.Name="ABC"として値を設定後、GetValueメソッドを呼び出すと 正常に"ABC"が表示されます。 よろしくお願いします。
[ メッセージ編集済み 編集者: やぎ 編集日時 2003-04-08 18:24 ] | ||||||||||||
|
投稿日時: 2003-04-08 19:01
こんにちは。
Personを構造体でなく、クラスにするわけにはいきません? | ||||||||||||
|
投稿日時: 2003-04-08 19:13
Qooさん、ありがとうございます。
VB6からの移行で、構造体が半端な量ではないため、 できればクラスにはしたくないのですが・・・ やっぱりクラスでないと無理なのでしょうか? | ||||||||||||
|
投稿日時: 2003-04-08 22:09
すみません、直接の返答ではありません。どうやらVB.NETではやぎさんの求める処理はできないようです。
実は、C#ではできるんです。問題は、Qooさんがおっしゃっているとおり、値型の参照型への変換、すなわちボックス化です。
ここと、
ここでpを2度ボックス化しています。そのため、最初と2回目では対象となるオブジェクトが変わってしまい、最初のコードで設定したはずの値が反映されていないように見えるのです。実際には操作対象が変わってしまっているのが原因です。 そこで、SetValueメソッドを呼び出す前に、次のコードでボックス化をしてしまいます。
そして、SetValue、GetValueは両方ともpではなくoに対して呼び出します。そうすると、同じオブジェクトに対して操作が行われるので、ABCが表示されるはず・・・でした。実際、同じコードをC#で書き直して、この戦略をとれば、ABCが表示されるのです。 ですが、VB.NETは私たちの上を行っていました。VB.NETでは、オブジェクトを引数にしてメソッドを呼び出すと、System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValueメソッドを呼び出して、対象のオブジェクトが実は値型をボックス化したものではないのか?ということを調査しています。そうだった場合はあらためてボックス化を行ってくれちゃいます。そういうわけで、C#とは異なる動作をします。 VB.NETがGetObjectValueを呼び出す理由は、値型のインスタンスを扱っている場合、ボックス化されていようといまいと同じ動作をすべきだから、ということのようです。 | ||||||||||||
|
投稿日時: 2003-04-08 22:11
C#だと、ボクシングすると出来るみたいです。
using System; using System.Reflection; struct Person { public int Age; public string Name; } class Class1 { [STAThread] static void Main(string[] args) { Object p = new Person(); Type t = p.GetType(); FieldInfo fi = t.GetField("Name"); fi.SetValue(p, "ABC"); Console.WriteLine("{0}", fi.GetValue(p)); } } こんな感じで、ABCと表示されました。 ところが、VB.NETで等価な(つもりの)コードを書いても結果が異なります。 Imports System Imports System.Reflection Module Module1 Public Structure Person Public Age As Integer Public Name As String End Structure Sub Main() Dim p As Object = New Person() Dim t As Type = p.GetType() Dim fi As FieldInfo = t.GetField("Name") fi.SetValue(p, "ABC") Console.WriteLine("{0}", fi.GetValue(p)) End Sub End Module 結果はブランク。 不思議に思ってVBで作ったモジュールを逆コンパイルしてみたところ、 [STAThread] public static void Main() { FieldInfo local0; object local1; Type local2; Person local3; <{ class ILEngineer:ps::MSIL::InitObj }>; local1 = local3; local2 = local1.GetType(); local0 = local2.GetField("Name"); local0.SetValue(RuntimeHelpers.GetObjectValue(local1), "ABC"); Console.WriteLine("{0}", RuntimeHelpers.GetObjectValue(local0.GetValue(RuntimeHelpers.GetObjectValue(local1)))); } と、なっていまして、 C#に比べて、RuntimeHelpers.GetObjectValueが余計でした。 VBとC#って微妙に結果が変わることもあるのですねぇ〜 #それとも、VB側で別の書き方があるのでしょうか・・・ | ||||||||||||
|
投稿日時: 2003-04-08 22:14
NothingButXMLInfoSetさんと、ほとんど一緒の内容ですね(汗)
タイミング的にかぶってしまった・・・ | ||||||||||||
|
投稿日時: 2003-04-09 20:46
皆さん、ありがとうございます。
C#だとボックス化するとできるんですね。 VB.NETでできればベストだったんですが、仕方ないですね。 その部分だけC#でクラス化して使おうと思います。 しかし、今後それがC#のバグだったということで、VB.NETの ような仕様にはならないですよね? ちょっと心配です。 |
1