🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C#

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

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

Q&A

解決済

5回答

12012閲覧

【C#】Listに匿名型のメンバを追加したい

inari_ken

総合スコア34

C#

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

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

0グッド

3クリップ

投稿2019/10/31 02:46

編集2019/10/31 04:44

こんにちは。今回もよろしくお願いします。

前提・実現したいこと

2つのListを突合し、1つ目のリストに足りない項目を2つ目のリストからAddして補完する
関数を作成しましたが、実行時エラーによりプログラムが停止してしまいます。
原因はある程度特定できたと思うのですが、解決方法が分かりません。

<Listの構造>
突合したいList(第1引数)は、LINQにて作成しました。下図のような構成になっています。
イメージ説明

突合したいList(第2引数)も同じ構成ですが、.result.Choiseの内容が一部異なります。
下図のように、赤丸の部分が第1引数にない項目のため、第1引数の.resultにAddし、戻り値として第1引数を返します。
イメージ説明

該当のソースコード

C#

1 private List<dynamic> List_Comp(List<dynamic> target ,List<dynamic> reference) 2 { 3 4 int list_count = 0; 5 bool Contains_flg = false; 6 foreach (dynamic dy in reference) 7 { 8 foreach (var item in dy.result) 9 { 10 Contains_flg = false; 11 foreach (var it in target[list_count].result) 12 { 13 if(item.Choise == it.Choise) 14 { 15 Contains_flg = true; 16 } 17 } 18 if(Contains_flg == false) 19 { 20 // 選択肢の追加 21 // 実行時エラー 22 target[list_count].result.Add(new { Choise = item.Choise.ToString(), Count = 0 }); 23 24 // 試したこと① 正常にListのresultにAddされた 25 // target[list_count].result.Add(new { Choise = "aaa", Count = 0 }); 26 // 試したこと② 正常にメッセージ"aaa"が表示された 27 // MessageBox.Show(item.Choise); 28 } 29 } 30 list_count++; 31 } 32 33 return (target); 34 } 35

発生している問題・エラーメッセージ

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: ''System.Collections.Generic.List<<>f__AnonymousType0<string,int>>.Add(<>f__AnonymousType0<string,int>)' に最も適しているオーバーロード メソッドには無効な引数がいくつか含まれています'

原因の考察

試したこと①から、そもそもエラーがでた行の全体的な記述は間違ってないことは分かりました。
また、固定値でのAddなら動作したため、Choise = item.Choise.ToString()の部分が問題であることは特定できました。

試したこと②から、item.Choise自体はきちんと取得できていることは分かりました。

エラーメッセージの内容から、itemが匿名型のためitem.Choiseの.Choiseをメソッドとコンパイラが勘違いしているのでは?と思っています。
上記から、itemが匿名型であることが問題で、2回目のforeachのvarを何かしらに変更すればきちんと動作するのではと考えましたが、具体的対策が思いつきません。

完全独学なので、突っ込みどころ満載かと思いますがビシビシ突っ込んでもらえるとありがたいです。

補足情報(FW/ツールのバージョンなど)

Microsoft Visual Studio Community 2017
Version 15.7.3
VisualStudio.15.Release/15.7.3+27703.2026
Microsoft .NET Framework
Version 4.7.03190

サンプルプログラム(2019年10月31日13時43分追記)

再現しないとご指摘があったため、List生成から呼び出しまでのサンプルプログラムを作成しました。
渡しの環境では以下プログラムで同じエラーが発生します。

C#

1using System.Collections.Generic; 2using System.Linq; 3 4namespace ConsoleApp3 5{ 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 11 List<string[]> data1 = new List<string[]>(); 12 List<string[]> data2 = new List<string[]>(); 13 14 List<dynamic> agg1 = new List<dynamic>(); 15 List<dynamic> agg2 = new List<dynamic>(); 16 17 data1.Add(new string[4] { "1", "2", "3", "4" }); 18 data1.Add(new string[4] { "2", "2", "3", "4" }); 19 data1.Add(new string[4] { "3", "2", "3", "4" }); 20 21 data2.Add(new string[4] { "1", "2", "3", "4" }); 22 data2.Add(new string[4] { "2", "2", "3", "4" }); 23 data2.Add(new string[4] { "3", "2", "3", "4" }); 24 data2.Add(new string[4] { "aaa", "2", "3", "4" }); 25 26 int item_count = 0; 27 for (int j = 0; j < 2; j++) 28 { 29 30 agg1.Add(new 31 { 32 title = j.ToString(), 33 result = data1.GroupBy(x => x[item_count]) 34 .Select(x => new { Choise = x.Key, Count = x.Count() }) 35 .OrderBy(x => x.Choise).ToList() 36 }); 37 38 item_count++; 39 40 } 41 42 item_count = 0; 43 for (int j = 0; j < 2; j++) 44 { 45 46 agg2.Add(new 47 { 48 title = j.ToString(), 49 result = data2.GroupBy(x => x[item_count]) 50 .Select(x => new { Choise = x.Key, Count = x.Count() }) 51 .OrderBy(x => x.Choise).ToList() 52 }); 53 54 item_count++; 55 56 } 57 58 59 agg1 = List_Comp(agg1, agg2); 60 61 62 63 64 } 65 private static List<dynamic> List_Comp(List<dynamic> target, List<dynamic> reference) 66 { 67 68 int list_count = 0; 69 bool Contains_flg = false; 70 foreach (dynamic dy in reference) 71 { 72 foreach (var item in dy.result) 73 { 74 Contains_flg = false; 75 foreach (var it in target[list_count].result) 76 { 77 if (item.Choise == it.Choise) 78 { 79 Contains_flg = true; 80 } 81 } 82 if (Contains_flg == false) 83 { 84 // 選択肢の追加 85 // 実行時エラー 86 target[list_count].result.Add(new { Choise = item.Choise, Count = 0 }); 87 } 88 } 89 list_count++; 90 } 91 92 return (target); 93 } 94 95 96 97 } 98 99 100} 101

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

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

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

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

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

guest

回答5

0

完全独学なので、突っ込みどころ満載かと思いますがビシビシ突っ込んでもらえるとありがたいです。

とのことなので、突っ込みます。

dynamic は便利ですが、そのような用途で使ってはいけません。
そのような用途ではValueTuple を使うか クラスを定義して使ってください。

C# は、厳密に型指定された静的型付け言語ですが、dynamic を多用すると
静的型付け言語のメリットを失い、動的型付け言語のデメリットが発生します。
結果、静的型付け言語と動的型付け言語両方のデメリットがあり、メリットがない酷いプログラムになってしまいます。

投稿2019/10/31 04:12

編集2019/10/31 04:19
hihijiji

総合スコア4152

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

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

inari_ken

2019/10/31 04:48

ご回答ありがとうございます。 内容咀嚼します。取り急ぎ御礼まで。
inari_ken

2019/10/31 07:23

今回は動的型付けによるデメリットをもろに受けた事例ということが理解できました。 C#の型については様々な種類があるので苦手意識を持っており、dynamicに頼っていましたが今回の件でドツボにはまったので、脱却する良い経験になりました。もちろん、dynamicも使える時は使ったらいいと思いますが今回のことが起こらないよう、吟味するようにします。ありがとうございました。
guest

0

ベストアンサー

サンプルプログラムの下記に示す行で、コンパイラは Choise メンバの型をdynamic 型として静的に解決します。
しかし、List<T> の動的型は、ここで推論された匿名型と異なるので Add メソッドの呼び出しは失敗します。

csharp

1target[list_count].result.Add(new { Choise = item.Choise, Count = 0 });

これを回避するには、 item.Choiseを string 型に明示的キャストをするか、 agg1 の生成時に下記のように dynamic 型のメンバにする必要があります。

csharp

1.Select(x => new { Choise = (dynamic)x.Key, Count = x.Count() })

~~ 該当のソースコード では ToString メソッドが呼ばれているので、サンプルプログラムとは状況が異なるかと思います。~~ ← 誤りだったため訂正です。 ToString メソッドを呼んだとしても静的に解決される型は、 dynamic なので同じことです。

投稿2019/10/31 05:40

編集2019/10/31 08:02
yuto_club

総合スコア60

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

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

inari_ken

2019/10/31 07:50

サンプルプログラムの方にToStringメソッドをつけ忘れてしまっていました。失礼しました。 stringに明示的キャストしてAddする方向でプログラムを修正し、正常に動作することを確認しました。 ご説明によりエラーとなった理由が理解できました。 そもそもこのListをクラス化することも念頭にいれつつ、作業を進めていきます。ありがとうございました。
guest

0

2つのListを突合し、1つ目のリストに足りない項目を2つ目のリストからAddして補完する

それは和集合なので、求めるにはLINQUnionを使ってください。

diff

1- agg1 = List_Comp(agg1, agg2); 2+ agg1 = agg1.Union(agg2).ToList();

下記回答はZuishinさんに指摘される前の回答で、同じことが実現できますが、冗長的な実装です。


二つの配列の差分はLINQExceptを使うことで求めることができます。その差分をAddRangeで追加すれば解決です。

C#

1var src = new List<Hoge>(); 2var src2 = new List<Hoge>(); 3 4//TODO:src,src2の初期化をする 5 6src.AddRange(src2.Except(src));

余談

hihijijiさんの回答には概ね同意で、本来はdynamicな型ではなくValueTupleを使うかクラスを定義するべきと思いますが、このような書き方をすることで匿名型の空リストを定義できるようです。

C#

1var agg1 = Enumerable.Range(0,0) 2 .Select(x => new 3 { 4 title = "", 5 result = new [] 6 { 7 new 8 { 9 Choise = "", 10 Count = 0 11 } 12 }.ToList() 13 }) 14 .ToList(); 15var agg2 = agg1.ToList();

投稿2019/10/31 04:47

編集2019/10/31 05:38
BluOxy

総合スコア2663

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

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

Zuishin

2019/10/31 04:56

それは Union ではないでしょうか。
BluOxy

2019/10/31 05:00

やっていることが和集合であることに気づいていませんでした。 ありがとうございます。 何か特別なメソッドを作ることも必要なさそうですね。
inari_ken

2019/10/31 07:39

ご回答ありがとうございます。 本件では、親Listの"agg1"をUnionするのではなく、子リストの"agg1.result"内の和集合を取る認識です。 agg1 = agg1[0].result.Union(agg2l[0].result).ToList(); (エラーになってしまいますがイメージとしてはこんな感じです。) また、Addしたagg1の.result.Countを0にするような仕様としたいので、今回はUnion1行で済ませることは難しいと判断しています。 余談については、非常に勉強になりました。どこかで使えそうです。ありがとうございました。
guest

0

item.Choise.ToString()の戻り値の型がdynamicになってるはずなので、

Choise = item.Choise.ToString()

Choise = (string)item.Choise.ToString()

にしてみてください。

投稿2019/10/31 04:55

編集2019/10/31 06:31
yudedako67

総合スコア2047

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

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

inari_ken

2019/10/31 07:41

ご回答ありがとうございます。 上記のとおり書き換えたところ、正常にaddできました。 Tostring()しなのに、戻り値はdynamicなことに驚いておりますが、取り急ぎ御礼まで。
guest

0

試しに下記のような呼び出しをしてみましたが、エラーが再現しませんね。

csharp

1List_Comp( 2 new List<dynamic>() { 3 new { 4 result = new List<dynamic>() 5 { 6 new { Choise ="a", Count = 1} 7 }, 8 title = "1" 9 } 10 }, 11 new List<dynamic>() { 12 new { 13 result = new List<dynamic>() 14 { 15 new { Choise ="b", Count = 1} 16 } 17 ,title = "2" 18 } 19 } 20); 21 22

エラーが再現する最小の呼び出しコードを書いてもらうことはできますか?

投稿2019/10/31 03:34

yuto_club

総合スコア60

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

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

inari_ken

2019/10/31 04:44

ご回答ありがとうございます。 本文にサンプルプログラムを追記しましたので、ご確認をお願いします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問