質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

3回答

12874閲覧

(C#) 配列要素をプロパティにマップする方法

gitiiii222

総合スコア10

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2018/10/09 12:08

オブジェクトのマッピングに関しまして質問があります。

以下のような配列要素をプロパティにマップする場合に何か良い方法はないでしょうか。

リフレクションを使用する事も検討しましたが、GetPropertiesメソッドで取得できるプロパティ一覧が、プロパティ宣言の順番でない場合がある事が調べて分かり断念しています。

お手数をお掛けしますが、よろしくお願いします。

C#

1public class Student{ 2 public string Name { get; set; } 3 public string Age { get; set; } 4 : 5 : 6 //stringのプロパティが大量にあるとします。 7} 8 9public static void main(string[] args){ 10 11 //データ 12 List<string> list = new List<string>(){"TARO","17",....}; 13 14 //案1:コンストラクタ、もしくはプロパティを逐一更新。(これは避けたいです。) 15 Student taro = new Student(list[0],list[1],....); 16 Student taro = new Student(){Name=list[0], Age=list[0],...}; 17 18 //案2:インデックスとプロパティの順番の整合性が取れない場合がある? 19 Student obj = new Student(); 20 foreach(var prop in typeof(Student).GetProperties().Select((item,idx)=>(item, idx))){ 21 prop.SetValue(obj, list[idx]); 22 } 23 24}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2018/10/09 13:09

方法はありますが、何のためにこんなことをするんですか? もっと適したやり方があるかもしれません。
gitiiii222

2018/11/03 04:03

外部接続してデータを取得するのにライブラリを使用しており、1番目要素はTimestamp,2番目はレコードIDという具合にstring配列を返すためです。
gitiiii222

2018/11/03 04:04

公開されているAPI自体がこういった仕様のため方法を模索していました。
Zuishin

2018/11/03 04:27

私の回答ではうまくいきませんか?
Zuishin

2018/11/03 04:28

うまくいかない場合、どのようにうまくいかないのかを書いてください。でなければわかりません。
guest

回答3

0

ベストアンサー

素直に言われた通り実装するなら、次のように序数をプロパティ名に変換する段階を踏めばいいと思います。
applyList に並んだ順になります。
このソースでは配列を使っていますが、既存のオブジェクトのプロパティに適用する Apply も、新しくオブジェクトを作って適用して返す ParseList も IReadOnlyList<string> 型の引数を取るので、配列でも List<string> でも使えます。

C#

1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Reflection; 5 6namespace ConsoleApp1 7{ 8 public class Student 9 { 10 public string Name { get; set; } 11 public string Age { get; set; } 12 13 public static Student ParseList(IReadOnlyList<string> list) 14 { 15 var result = new Student(); 16 result.Apply(list); 17 return result; 18 } 19 20 public void Apply(IReadOnlyList<string> list) 21 { 22 if (list.Count != applyList.Count) 23 { 24 throw new ArgumentException(nameof(list)); 25 } 26 for (int i = 0; i < list.Count; i++) 27 { 28 properties[applyList[i]].SetValue(this, list[i]); 29 } 30 } 31 32 private static readonly Dictionary<string, PropertyInfo> properties = typeof(Student) 33 .GetProperties() 34 .ToDictionary(a => a.Name); 35 private static readonly List<string> applyList = new List<string>() 36 { 37 nameof(Name), nameof(Age) 38 }; 39 } 40 41 class Program 42 { 43 static void Main(string[] args) 44 { 45 var students = new[] 46 { 47 new[] { "山田太郎", "17" }, 48 new[] { "山田二郎", "17" }, 49 new[] { "佐藤花子", "16" }, 50 }; 51 foreach (var list in students) 52 { 53 var student = Student.ParseList(list); 54 Console.WriteLine($"{student.Name}: {student.Age}"); 55 } 56 Console.ReadKey(); 57 } 58 } 59}

なぜ追記修正依頼でこのようなことをする理由を聞いたかと言うと、データソースが List<string> ではなくデータベースや JSON や Excel や CSV ならこのようなことをする必要がないからです。

今回のソースのようにデータを直接コードに埋め込むのをハードコーディングと言いますが、ハードコーディングしたデータはコンパイルしなおさなければアップデートできませんので、良い方法ではありません。
データは Excel などで管理し、それを必要に応じて読み込む方法が推奨されます。
読み込む際には List<string> でなければならない理由はないので、このようなことをする必要がないのです。

投稿2018/10/10 03:43

Zuishin

総合スコア28660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gitiiii222

2018/11/03 12:21

ありがとうございます、返信ができておりませんでした。
guest

0

T4テンプレートを使う。
ちまちま書くのが面倒なら、ちまちま書くことをプログラミングすればいいじゃない?という方針。
リフレクションをつかうのより、T4のようなコード生成のメタプログラミングのほうがよさげ。
パフォーマンス的には最強。

投稿2018/10/09 18:37

編集2018/10/09 18:41
kiichi54321

総合スコア1984

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

初期化時にパラメーターを割り当てるのを避けたい理由はなんでしょうか?
コード量が多かったりパラメーター管理が面倒ということでしょうか?
個人的にはこういうケースの場合、ここを省略すると後々面倒になることが多いのでファクトリメソッドとかにして切り出してます。

それはひとまず置いといて、案を2つ考えました。

案1.Studentの値を配列で持つ

C#

1public class Student { 2 public List<string> Values { get; } = new List<string>(); 3 public string Name { get => Values[0], set => Values[0] = value } 4 public string Age { get => Values[1], set => Values[1] = value } 5}

それでデータソース側にも対応するインデックスを割り当てればValues[index]で回せませんかね?
インデックスの割当管理はStudent内で行うことになります。

案2.indexとプロパティ名を対応したリストを作る

C#

1Dictionary<int, string> propertyNames = new Dictionary<int, string>(); 2propertyNames.Add(0, "Name"); 3propertyNames.Add(1, "Age");

件数にもよりますが頭で一度だけ対応リストを作っておくなら大したコストにもならないと思います。
index→propertyName→リフレクションで設定という流れです。
必要に応じてHashSetでもいいと思います。

投稿2018/10/09 13:47

gate

総合スコア15

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問