- PR -

プロパティ名の間接指定

投稿者投稿内容
豪雪地帯
常連さん
会議室デビュー日: 2004/06/08
投稿数: 34
投稿日時: 2005-05-09 15:31
コントロールやクラスのプロパティを間接的に指定したいのですが、
どのようにすれば良いのでしょうか。見当が付きません。

通常ならば、コーディングで、
 ClassC.PropertyA = ValueA
のようにコーディングしますが、
これをPropertyAの部分を変数的にしておいて、プログラム実行時にPropertyAを
確定して、ClassCのプロパティの値を設定したい、
つまり、イメージ的には、コーディングで
 ClassC.XX = ValueA のようにしておいて、(XXは変数的なもの)
実行時の処理中にXXの値はPropertyAだと決定し、上記のコーディング行に来たら、
 ClassCのPropertyAへValueAが代入される、
というような事をやりたいのです。

if文とかcase文で予めClassCの持ちえるプロパティを列挙して、条件に引っ掛ける
という方法もありますが、スマートでなく、また対象とするClass自体が実行時まで
未定の場合など、事前にコーディングできません。

よろしくお願いします。


[ メッセージ編集済み 編集者: 豪雪地帯 編集日時 2005-05-09 15:35 ]
nodera
大ベテラン
会議室デビュー日: 2003/09/08
投稿数: 200
投稿日時: 2005-05-09 16:12
こんにちは。

>これをPropertyAの部分を変数的にしておいて
変数ではありませんが、プロパティの名前を文字列で指定するというのであれば、TypeのInvokeMemberや、GetPropertyとかでいけます。

InvokeMemberを使ったサンプルは下記URLを参照
http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/cpref/html/frlrfsystemtypeclassinvokemembertopic.asp

.NETでは実行時に型の情報に対してアクセスする方法(遅延バインディング)をリフレクションという機能で実現していますので、そのあたりを調査するといろいろ分かるかと思います。
よねKEN
ぬし
会議室デビュー日: 2003/08/23
投稿数: 472
投稿日時: 2005-05-10 01:25
引用:

豪雪地帯さんの書き込み (2005-05-09 15:31) より:
通常ならば、コーディングで、
 ClassC.PropertyA = ValueA
のようにコーディングしますが、
これをPropertyAの部分を変数的にしておいて、プログラム実行時にPropertyAを
確定して、ClassCのプロパティの値を設定したい、



その実装により実現したい大きな目的がわからないので使えない手かもしれませんが、
もし名前と値を紐付けて扱いたいだけであれば、Hashtableを使う手があると思います。

#たぶん、そういうことじゃないのだろうとは思いますが
ほげた
ベテラン
会議室デビュー日: 2002/05/08
投稿数: 67
お住まい・勤務地: なごやん
投稿日時: 2005-05-10 03:05
はじめまして。

よねKANさんのHashtableによる手法でマッチするのであれば簡単そうですが、変更対象となるインスタンスのクラスまで動的にということならばnoderaさんの示唆するリフレクションを調べたほうがよさそうです。

こんな感じで、対象となるインスタンスの型も知らないまま、プロパティを変更できます。

  Type type = 対象インスタンス.GetType();
  PropertyInfo info = type.GetProperty(プロパティ名);
  if (info.CanWrite)
  {
    info.SetValue(対象インスタンス, 変更する値, null);
  }

リフレクションを使えば、set可能なプロパティだけを列挙するなど、いろいろなメタ情報を取得できます。
クイックスタート チュートリアルも参考になると思います。
http://ja.gotdotnet.com/quickstart/howto/doc/GetTypes.aspx
ya
大ベテラン
会議室デビュー日: 2002/05/03
投稿数: 212
投稿日時: 2005-05-10 07:48
こういう場合に型情報を直接たたく人が多いんですが、コンポーネントとの絡みで、System.ComponentModel.TypeDescriptorを使用したほうがいいような気がします。他の動的操作との整合性の意味で。
複雑なことをしなければ一緒なので、気持ちの問題なんですけど。
豪雪地帯
常連さん
会議室デビュー日: 2004/06/08
投稿数: 34
投稿日時: 2005-05-11 13:54
皆さん、こんにちは。
色々とアドバイスありがとうございます。

このような手法を遅延(レイト)バインディングと言うのですね。
ほげたさんの例を使って、とりあえず、文字型プロパティを動的に指定して値が書き込める簡単なものはできるようになりました。ありがとうございます。

他に紹介された、InvokeMemberや、TypeDescriptorやチュートリアルなど、私にはまだ敷居が高く時間がかかりそうです。

アドバイス感謝します。
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2005-05-11 14:07
引用:

yaさんの書き込み (2005-05-10 07:48) より:
こういう場合に型情報を直接たたく人が多いんですが、コンポーネントとの絡みで、System.ComponentModel.TypeDescriptorを使用したほうがいいような気がします。他の動的操作との整合性の意味で。


特にコントロールなんかのプロパティをいじる場合などはこの方がいいですね。
# まあその場合もどの程度までちゃんと対応するかってのはあるかもしれませんが。
ya
大ベテラン
会議室デビュー日: 2002/05/03
投稿数: 212
投稿日時: 2005-05-11 17:21
引用:


他に紹介された、InvokeMemberや、TypeDescriptorやチュートリアルなど、私にはまだ敷居が高く時間がかかりそうです。



ちょっと簡単に解説したほうがいいのかな?
えーと、CLRではバイナリイメージにおいて型情報(メタデータ)を自己記述するようになっています。これは、実行形式にクラスの情報がそのまま入っているとでも思ってください。これによって、実行時に型情報を参照することができるようになりました。
そして、この「型情報そのもの」を参照する、表現されたインターフェイスがTypeだのPropertyInfoです。
まず、「直接型情報を使って」操作することができます(このプロパティに値をセットする、このメソッドを実行する...)。

コード:

Type t = instance.GetType();
PropertyInfo pi = t.GetProperty(PropertyName);
pi.SetValue(instance, value, null);



次に、このことが出来るようになったため、コンポーネント(ソフトウェア部品)の基盤として、これが使えるようになりました。.NET Frameworkではこの型情報を基盤として、コンポーネントを表現する実装が施されています(CLRの型情報に類似したコンポーネントインターフェイス、またカスタマイズしなければそれがそのまま使われる等)。コンポーネント自体はクラスとして表現されているため、違いが分かりにくいのですが、例えばコンポーネントは「デザイナでは表示しないプロパティ」等もっと細かな指定をされますし、これによってデザイナで操作したり出来ます。

コード:

[Browsable(false)]
public bool SampleProperty { ... }



その視点(つまり一段上の抽象化)からアクセスするのがTypeDescriptor(型記述子)です。ようは、仮想化された「型」みたいな感じです。この「型」はプロパティとイベントを持ちます(PropertyDescriptor, EventDescriptor)。

これがフルに使われている例として例えばSystem.Data以下のDataRowViewがあります。DataSet関係のクラスはいわば動的にスキーマ(型情報)が変化、決定されるクラスであり、静的な型情報は存在しません。ASP.NETで何気なくDataSetをバインドして

コード:

<%# DataBinder.Eval(Container.DataItem, "PropertyName", "{0:format}") %>



と書くと思いますが、本来のDataRowViewにはそんな名前のプロパティはないのに普通に取得できます。これは、抽象化された動的型情報(TypeDescriptor)が動的に生成されたPropertyDescriptorを返しているからです(具体的にはICustomTypeDescriptorを実装する)。こういうこと(つまりCLRのネイティブの型と違ったコンポーネント型情報を持つクラスが存在する)も出来るように、.NETはワンクッションおいたフレームワークが存在している(つまりCLRの型情報を直接使うのではなく、TypeDescriptorで間接的にアクセスする)わけです。
そして、DataBindingやデザイナ等、すべてコンポーネントに動的にアクセスする場合には使われているため、こっちでアクセスするのがいいのではないか、という話につながるわけです。

長々と書きましたが、用はまず「CLRそのものの型情報」があって、その上に「コンポーネント的な型情報」もあると思っていただければいいかなと思います。
ちなみに、表題の件ですが、具体的にこの後者の方法を使って実現すると、

コード:

PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(component);
PropertyDescriptor property = properties.Find("PropertyName", true);
property.SetValue(component, value);



みたいになります。
PropertyDescriptorはまさに動的に操作するために存在しているので、型情報をそのままつかう場合に比べて高機能ですし、色々カスタマイズも出来ます。例えば以下のようにコンバータを通すことも出来ます。

コード:

object v = value.GetType()==property.PropertyType ? value : property.Converter.ConvertFrom(value);
property.SetValue(component, v);


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