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

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

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

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

Q&A

解決済

2回答

1420閲覧

C# クラスのメンバ変数を匿名クラスとして宣言する方法

ElecDove

総合スコア254

C#

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

0グッド

0クリップ

投稿2018/01/25 12:43

お世話になります.

だいぶ初歩的な質問ですがご容赦ください.
色々調べたのですがわかりませんでした

C#

1public class HogeClass{ 2 public class FugaClass{ 3 public int a,b; 4 } 5 public FugaClass Fuga; 6 public int c,d; 7} 8

たとえばこんな構造のクラスがあるとします.
fugaというクラスは単体で使用されることはなく必ずhogeのメンバ?中身?要素?として使われます.

そこで,メソッド内で匿名クラスを使用するように,以下のような書き方ができればいいのですが・・・.

C#

1public class HogeClass{ 2 public var Fuga {public int a,b}; 3 public int c,d; 4}

このような感じで,匿名クラスとして方を宣言することはできますでしょうか.

このクラスはJSONシリアライズの際の雛形として使用するものなので,できるだけすっきりと書きたいのです.


ところで,本件質問内容とは直接関係ありませんが,FugaClassを単体でインスタンス化できないようにするにはどうすれば良いでしょうか.privateをつけるとFugaClass Fugaが宣言できなくなりますし・・・.

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

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

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

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

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

guest

回答2

0

ベストアンサー

その気持ち、とてもよくわかります!
そもそもFugaClassを定義しているのは、C#のJsonパーサーがクラスのインスタンスを作ってくれる方式だからですよね。
もしフィールド名だけで値を取得できれば、Jsonのデシリアライズのためだけに無駄なクラスを作る必要はないのに!

そんなときはこちらをお勧めします。
https://github.com/mtschoen/JSONObject
Unity用ですが、JsonObject.csのみで動くので、Unity以外でも使用することができます。
ライセンスはMITです。スクリプト内に記載されているので目を通しておきましょう。

使用方法は以下のような形式です。
変数名が気持ち悪かったり、DateTime型へのパースが多かったりするならば自作クラスでラップしましょう。

var jsonObj = new JSONObject(jsonText); string name= jsonObj.GetField("name").str; int age = (int)jsonObj.GetField("age").i; float weight = jsonObj.GetField("weight").f;

追記:
問題の本質は「Jsonのシリアライズ/デシリアライズをすっきり書きたい」ことであり、「匿名クラスを定義したい」ことではないと思います。

上記の解決策が最善とは限りませんが、
・問題の本質
・現在採用している、もしくは採用したいと思っている手段
・上記手段の問題点
を分けて考えるとより回答が付きやすくなるかと思います。
ご参考までに!


さらに追記:
上記のスクリプトを使わせてもらう場合、ラップした方が使いやすいと思うので例を載せておきます。

// ラップ例 public class JsonReader { JSONObject jsonObject; public JsonReader(string jsonText) { this.jsonObject = new JSONObject(jsonText); } // 内部的に使います。指定したキーが存在しなければ例外を発生させています。 JSONObject GetField(string key) { if (!jsonObject.HasField(key)) { var message = "Jsonオブジェクトにキー[" + key + "]が見つかりませんでした。"; throw new KeyNotFoundException(message); } return jsonObject.GetField(key); } public string GetString(string key) { return GetField(key).str; } public int GetInt(string key) { return (int)GetField(key).i; } // 基本型以外もすきな方法で取得できます。 public DateTime GetDateTime(string key) { var dateTimeString = GetString(key); DateTime result; if (!DateTime.TryParse(dateTimeString, out result)) { var message = "文字列「" + dateTimeString + "」を日付時間型に変換できませんでした。"; throw new ArgumentException(message); } return result; } public DateTime GetDate(string key) { return GetDateTime(key).Date; } }
// 使用例 public class Hoge { // テストのために適当なJsonを読み込ませておく JsonReader fuga = new JsonReader("{\"name\": \"名前\", \"birthday\": \"1956-07-08\"}"); public string Name { get { return fuga.GetString("name"); } } public DateTime BirthDay { get { return fuga.GetDate("birthday"); } } public int Age { get { var birthday = this.BirthDay; var today = DateTime.Today; var age = today.Year - birthday.Year; return age - (birthday > today.AddYears(age) ? 1 : 0); } } }

投稿2018/01/25 21:44

編集2018/01/26 07:51
Ushimaru

総合スコア69

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

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

ElecDove

2018/01/26 02:59

回答ありがとうございます 以前PerlでJSONを扱っていた時は連想配列にぶっこまれていたので まさにこんな感じでその場でフィールド名を指定しながら値を取得していました とりあえずJSONを取得して値が読みたい,みたいな時は数行書けば読めましたしとても良かったです ただ,今回はできればガチガチの型付でやりたいな,と今は思っています フィールドをあらかじめ定義しておくので存在しないフィールド名でアクセスしたりとかそういう エラーがなくなるのは利点だと思っています
Ushimaru

2018/01/26 07:52

せっかくC#を使っているのだから、ガチガチに型制限しようというのはとても良い方針だと思います! web系の言語で存在しない変数名使ってもエラーにならなくて、タイプミスで謎のバグ出て苦しんだ思いとかも死ぬほどわかります・・・ 一点気になったのが >存在しないフィールド名でアクセスしたりとかそういうエラーがなくなるのは利点だと思っています の部分です。 正確には、fugaに対して想定外の動作をさせることができないのは利点。という意図だと思われますが、 fugaはhogeのフィールドとしてカプセル化されているので、すでにこの点はクリアされています。 これは例えばList型の変数をメンバとして持っている場合、ListにはClearというメソッドが用意されていますが、 カプセル化されていれば外部からList.Clear()を呼ぶことはできないので、勝手にリストが初期化されることはないのと同じです。 なので今回のスコープであれば、フィールドを定義するのもキー名で取得するのも制約度は変わらないです。 むしろフィールドで定義してしまうと、 ・値を読み取り専用にできない(fuga->hoge間でだけなのでそこまで気にしなくて良いけど) ・JSONにないフィールドを定義してもデフォルト値が取得できてしまう(キーで取得する場合は例外を出すなど対策できます) などの問題があります。 どちらもエラーが出ずに進行できてしまう可能性があり、気づかないバグを生むことに繋がりやすいです。 とはいえJsonObjectをそのままフィールドとして持つのも不便なので、使いやすくラップしましょう。例を回答の方に追記しておきます!
guest

0

クラスメンバの型として匿名型を使うことはできません。
目的に合うかどうかは知りませんが、ジェネリック型を使って次のようなインスタンスを作ることはできます。

C#

1using System; 2 3public class HogeClass<T> 4{ 5 public HogeClass(T fuga) 6 { 7 Fuga = fuga; 8 } 9 public T Fuga; 10 public int c, d; 11} 12 13public static class HogeFactory 14{ 15 public static HogeClass<T> Create<T>(T fuga) 16 { 17 return new HogeClass<T>(fuga); 18 } 19} 20 21var hoge = HogeFactory.Create(new { a = 1, b = 2 }); 22Console.WriteLine(hoge.Fuga.a); 23

また、new できないクラスを作るには通常 プライベート コンストラクター を使います。
しかしこれも Activator.CreateInstance() を使えばインスタンス化できます。

追記

C#7.0 以降になりますが、ValueTuple を使えば次のように書けます。

C#

1using System; 2 3public class HogeClass 4{ 5 public (int a, int b) Fuga; 6 public int c, d; 7} 8 9var hoge = new HogeClass() { Fuga = (1, 2) }; 10Console.WriteLine(hoge.Fuga.a);

投稿2018/01/25 13:55

編集2018/01/25 14:43
Zuishin

総合スコア28660

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

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

ElecDove

2018/01/26 02:54

回答ありがとうございます. ジェネリック型を使うほうはちょっと読みづらいなぁと感じました ValueTupleなんて物があるのですね,C#7.0・・・. 調べてみたら値形なんですね,これ. 色々試してみたいと思います
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問