- PR -

未知のクラスの全てのFieldやPropertyを取得するには?

1
投稿者投稿内容
にー
常連さん
会議室デビュー日: 2006/04/30
投稿数: 35
投稿日時: 2006-04-30 12:17
未知のクラスの全てのFieldやPropertyを取得するには?


初めて投稿させて頂きます。
VS2005 C#を入手し、現在勉強中の身です。

件名については、今でも一応ある程度はできています。次のソースを実行すると、全ての変数名と値を取得することができます。

class A {
public int m1;
public string m2;
private string _m3;

public string m3 {
get { return _m3; }
set { _m3 = value; }
}
}

public partial class Form1: Form {
// 変数
private A a;

public Form1() {
InitializeComponent();

// クラスAのオブジェクト作成と値のセット
a = new A();
a.m1 = 123;
a.m2 = "ABC";
a.m3 = "あいうえお";
}

private void button1_Click( object sender, EventArgs e ) {
TestFunc( b );
}

private void TestFunc( object src ) {
Type srcType = src.GetType(); // 引数のクラス型を取得する

// 公開変数を取得する
foreach ( FieldInfo fi in srcType.GetFields() )
MessageBox.Show( fi.Name + ":" + fi.GetValue( src ) );

// 公開プロパティを取得する
foreach ( PropertyInfo pi in srcType.GetProperties() )
MessageBox.Show( pi.Name + ":" + pi.GetValue( src, null ) );
}
}



現在、できないのがTestFuncに渡すオブジェクトのクラス構造が

class A {
public int m1;
public string m2;
private string _m3;

public string m3 {
get { return _m3; }
set { _m3 = value; }
}
}

class B {
public A ma;
public int m4;
}

となっているBのオブジェクトを渡したときです。GetValueの結果がクラス名となり、maのm1, m2, m3を取得することができません。

このようなオブジォクト内のオブジォクトメンバの情報を取得する方法はあるのでしょうか? ご存知の方、よろしくお願いします。
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2006-04-30 12:31

GetValue の結果は名前じゃなくてオブジェクトインスタンスですよ。
ですから、再帰的にプロパティ/フィールドを取得すれば良いだけです。
// ある程度でキリ付けないと無限ループになりそうですが。
にー
常連さん
会議室デビュー日: 2006/04/30
投稿数: 35
投稿日時: 2006-04-30 12:36
Hongliangさん、返信ありがとうございます。
私は、ご指摘の「再帰的にプロパティ/フィールドを取得すれば良いだけ」の方法を知りたくて、投稿したと言っても良かったのかもしれません。

具体的には、どのようなやり方になるのでしょうか?
囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2006-04-30 12:37
Hongliangさんの言うとおり再帰的にやらないといけないので、Type.IsPrimitive 等を使ってどっかで止める判断をしないといけません。
_________________
囚人のジレンマな日々
にー
常連さん
会議室デビュー日: 2006/04/30
投稿数: 35
投稿日時: 2006-04-30 12:54
囚人さん、返信ありがとうございます。

IsPrimitiveを使ってやってみましたが...

private void TestFunc( object src ) {
Type srcType = src.GetType(); // 引数のクラス型を取得する

// 公開変数を取得する
foreach ( FieldInfo fi in srcType.GetFields() ) {
if ( !fi.GetType().IsPrimitive )
MessageBox.Show( "this is a class object!" );
MessageBox.Show( fi.Name + ":" + fi.GetValue( src ) );
}

// 「公開プロパティを取得する」は省略...
}

fi.GetType()は、FieldInfoそのものを指しているようで、この記述が間違っているのは分かるのですが、では、メンバの型情報を取得する方法はどのようなものなのでしょうか。
また、再帰的に呼び出す場合には、メンバがクラスオブジェクトの場合、そのオブジェクト(ポインタ?)を取得する必要が生じると思いますが、それはどうやれば良いのでしょうか? (それが分かれば、メンバの型情報も取得できると思いますが...)

よろしくお願いします。
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2006-04-30 12:56
引用:

私は、ご指摘の「再帰的にプロパティ/フィールドを取得すれば良いだけ」の方法を知りたくて、投稿したと言っても良かったのかもしれません。

具体的には、どのようなやり方になるのでしょうか?


コード:
public static void メンバ取得(object 対象) {
    foreach (PropertyInfo property in 対象のプロパティ群) {
        object propValue = プロパティの値取得
        表示
        if (propValue != null) {
            if (無限ループにならないような何らかの判断) {
                メンバ取得(propValue);
            }
        }
    }
    同じようにフィールドを取得
}


こんな感じですか。色々工夫する余地はありますが。
メソッドの引数に深度を加えて、再帰呼び出しをする度に深度をインクリメントしていき、ある程度以上の深さにならないようにチェックするとか。



引用:

囚人さんの書き込み (2006-04-30 12:37) より:

Hongliangさんの言うとおり再帰的にやらないといけないので、Type.IsPrimitive 等を使ってどっかで止める判断をしないといけません。


このメンバは知りませんでした(w;
これだけじゃ配列のプライベートフィールド掘っていったときとかに問題があったりと、なかなか終了条件をきっちり定義するのは難しそうですね。
// あれ、スレ主さんの場合は public だけだから問題ないかな?
にー
常連さん
会議室デビュー日: 2006/04/30
投稿数: 35
投稿日時: 2006-04-30 13:38
囚人さん、Hongliangさん、ありがとうございました。

かっこ悪いながらも、なんとか実現できました。

private void TestFunc( object src ) {
Type srcType = src.GetType(); // 引数のクラス型を取得する

// 公開変数を取得する
foreach ( FieldInfo fi in srcType.GetFields() ) {
object mem = fi.GetValue( src );
if ( !mem.GetType().IsPrimitive && mem.GetType() != typeof(string) )
TestFunc( mem );
else
MessageBox.Show( fi.Name + ":" + mem );
}

// 「公開プロパティを取得する」は省略...
}

stringもクラスになっている為、無限ループに陥ってしまいました。これを格好良く回避する方法は思いつきませんでしたが、未知のクラスで使用する変数の型の使用を制限すれば、これでも結構いけそうです。(あとはDateTimeぐらいかな?)

今回の、皆さんの教えで
object mem = fi.GetValue( src );
という書き方が、私にとって非常に勉強になりました。

ありがとうございました。
囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2006-04-30 13:42
引用:

stringもクラスになっている為、無限ループに陥ってしまいました。これを格好良く回避する方法は思いつきませんでしたが、未知のクラスで使用する変数の型の使用を制限すれば、これでも結構いけそうです。(あとはDateTimeぐらいかな?)


そうですね。汎用的に使おうと思うと、後は enum だとか、Hongliang さんも仰っていますが配列だとかを考慮しなければなりません。
_________________
囚人のジレンマな日々
1

スキルアップ/キャリアアップ(JOB@IT)