【C#】プロパティを動的に検索して値を取得したり変更する!

みなさんこんにちは!!ひろぽんです!

突然ですがC#のコードを書いていてこう思ったことないですか?

プロパティを動的に検索したい!で、値を自由に変更したり取得したりしたい!と!

できます!

PropertyInfoという型を取得することができればできます!

あわせて読みたい
PropertyInfo クラス (System.Reflection) プロパティの属性を検出し、プロパティ メタデータへのアクセスを提供します。

では早速!

目次

プロパティを動的に検索して値を取得してみる

💡 業務系で Form 間のデータ受け渡しでプロパティが効くパターンは別記事 FormとFormの間で値の共有・受け渡し で書いてます。

まずは下記のようなクラスを用意します。

    public class User
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }

    public User(int id, string name, string address)
    {
        ID = id;
        Name = name;
        Address = address;
    }

    public User()
    {
        
    }
}</code></pre></div>

でこのクラスのインスタンスを作ります。

    class Program
    {
        static void Main(string[] args)
        {
            var user = new User(1,"鈴木","東京都墨田区");
        }
    }

でこのインスタンスの値を動的に変更してみましょう。

    class Program
    {
        static void Main(string[] args)
        {
            var user = new User(1,"鈴木","東京都墨田区");
            PropertyInfo[] props = typeof(User).GetProperties();
            var addressProp = props.FirstOrDefault(item => item.Name == nameof(User.Address));
            Console.WriteLine(addressProp.GetValue(user));
            Console.ReadLine();

    }
}</code></pre></div>

こんな感じでコードを書いて実行すると!こう!

ちゃんと出てる!

簡単に解説

まずは下記のコード

PropertyInfo[] props = typeof(User).GetProperties();

これは見てわかるとおりUserのTypeを取得し、そのTypeが持っているプロパティの型を全て取得します。

今回の場合UserクラスにはIDとNameとAddressがあるので、これらのプロパティの「情報」(値ではない)が入っています。

本当に取得できているのか確認すると下記のようになります。

            PropertyInfo[] props = typeof(User).GetProperties();
            var propNames = props.Select(prop => prop.Name);
            Console.WriteLine(String.Join(",",propNames.ToArray()));
            Console.ReadLine();

PropertyInfoの配列から名前のみを抽出し文字列としてくっつけています。

こんな感じでちゃんとプロパティの情報が取得できていることが分かります。

で、この情報をもとに値などの実態が存在するインスタンスを投げて、値を取得しているのです!

            // ここでAddressのプロパティの情報を抜き出して
            var addressProp = props.FirstOrDefault(item => item.Name == nameof(User.Address));

        // ここで上記プロパティ情報にインスタンスを投げてデータを取得してる。
        Console.WriteLine(addressProp.GetValue(user));</code></pre></div>

プロパティを動的に検索して値を変更してみる

この方法を使えば、プロパティを動的に検索して値を変更するということもできます。

例えば下記のようなコードを実行すると!

            var user = new User(1, "鈴木", "東京都墨田区");
            PropertyInfo[] props = typeof(User).GetProperties();
            // ここでAddressのプロパティの情報を抜き出して
            var addressProp = props.FirstOrDefault(item => item.Name == nameof(User.Address));

        // ここで上記プロパティ情報にインスタンスを投げてデータを取得してる。
        Console.WriteLine(addressProp.GetValue(user));

        // ここで値を変える
        addressProp.SetValue(user,&quot;東京都中央区&quot;);
        Console.WriteLine(addressProp.GetValue(user));
        Console.ReadLine();

こんな感じで値が変わっています!

PropertyInfoの中はどうなっている?

ではこのPropertyInfoの中身がどうなっているのか気になる人も多いと思うので、ウォッチリストに追加して見てみましょう!!

文字が細かくて申し訳ないですが、これを見る限りプロパティ名のみならず、プロパティのデータ型も取得することができています。

またもうちょっと深い階層には自身が所属しているクラスの名前とNamespaceも持っていることが分かります。

PropertyInfoは結構便利そうな型なので是非難なく使えるようになっておきたいですね!

GitHubに今回のサンプルコード置いています!

下記リポジトリの中にCSharpというフォルダがありその中のPropertyDemo2が今回のコードです!

是非ローカルで試してみてください!

GitHub
GitHub - HayashiyamaHiroshi/blog-source-demo Contribute to HayashiyamaHiroshi/blog-source-demo development by creating an account on GitHub.

💡 補足: 業務系の現場でよくハマるパターン

俺もこのプロパティ get/set、業務でハマってきたところを3つ並べておきます。

① 自動プロパティと完全プロパティの使い分けミス

public int Age { get; set; } の自動プロパティで OK な場面と、 setter にバリデーション入れたいから完全プロパティにする場面の判断。 INotifyPropertyChanged 実装する時は完全プロパティ必須。

② readonly プロパティと init-only の混同

C# 9 から { get; init; } でコンストラクタ後の代入を禁止できる。 readonly フィールドより使い勝手が良い。 不変オブジェクトを作る時は init で。

③ リフレクション PropertyInfo の SetValue で値の型が合わない

DataRow から動的に値取って SetValue する時、 DBNull や数値型のボックス化で型不一致例外。 Convert.ChangeType を噛ます or 明示的なキャストで防御。今回のサンプルコードの応用編。

❓ よくある質問

Q1. プロパティ vs フィールド どっち使う?

A. public はプロパティ一択。 private はフィールドで OK。 プロパティは将来の変更(バリデーション追加・通知)に強い。

Q2. 計算プロパティ(expression-bodied member)の使いどころ?

A. public string FullName => $"{FirstName} {LastName}"; のような派生プロパティ。読み取り専用で計算結果を返したい時に簡潔。

Q3. INotifyPropertyChanged の実装は?

A. setter 内で PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Foo))) を呼ぶ。 WPF / WinForms のデータバインディングで必須。

Q4. Form 間でプロパティ経由でデータ共有する時の注意点は?

A. 親 Form のプロパティを子 Form から直接触ると密結合。 EventHandler 経由 or 共通の ViewModel クラス経由で疎結合に。 FormとForm間の値受け渡し でパターン紹介。

Q5. リフレクションでプロパティ列挙する時のパフォーマンスは?

A. 重い。 ループ内で typeof(T).GetProperties() 呼ぶと型情報を毎回構築する。 静的キャッシュ or Lazy<T> で1回だけ取得。 .NET 6+ なら Source Generator 検討。

📚 関連記事

この記事が気に入ったら
いいねしてね!

どんどんシェア待ってるぜ!!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次