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

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

ただいまの
回答率

90.50%

  • C#

    7116questions

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

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

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 398

ElecDove

score 228

お世話になります.

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

public class HogeClass{
  public class FugaClass{
    public int a,b;
  }
  public FugaClass Fuga;
  public int c,d;
}

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

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

public class HogeClass{
  public var Fuga {public int a,b};
  public int c,d;
}

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

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


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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

その気持ち、とてもよくわかります!
そもそも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/26 11:59

    回答ありがとうございます

    以前PerlでJSONを扱っていた時は連想配列にぶっこまれていたので
    まさにこんな感じでその場でフィールド名を指定しながら値を取得していました
    とりあえずJSONを取得して値が読みたい,みたいな時は数行書けば読めましたしとても良かったです

    ただ,今回はできればガチガチの型付でやりたいな,と今は思っています
    フィールドをあらかじめ定義しておくので存在しないフィールド名でアクセスしたりとかそういう
    エラーがなくなるのは利点だと思っています

    キャンセル

  • 2018/01/26 16:52

    せっかくC#を使っているのだから、ガチガチに型制限しようというのはとても良い方針だと思います!
    web系の言語で存在しない変数名使ってもエラーにならなくて、タイプミスで謎のバグ出て苦しんだ思いとかも死ぬほどわかります・・・

    一点気になったのが
    >存在しないフィールド名でアクセスしたりとかそういうエラーがなくなるのは利点だと思っています
    の部分です。

    正確には、fugaに対して想定外の動作をさせることができないのは利点。という意図だと思われますが、
    fugaはhogeのフィールドとしてカプセル化されているので、すでにこの点はクリアされています。

    これは例えばList型の変数をメンバとして持っている場合、ListにはClearというメソッドが用意されていますが、
    カプセル化されていれば外部からList.Clear()を呼ぶことはできないので、勝手にリストが初期化されることはないのと同じです。

    なので今回のスコープであれば、フィールドを定義するのもキー名で取得するのも制約度は変わらないです。
    むしろフィールドで定義してしまうと、
    ・値を読み取り専用にできない(fuga->hoge間でだけなのでそこまで気にしなくて良いけど)
    ・JSONにないフィールドを定義してもデフォルト値が取得できてしまう(キーで取得する場合は例外を出すなど対策できます)
    などの問題があります。
    どちらもエラーが出ずに進行できてしまう可能性があり、気づかないバグを生むことに繋がりやすいです。
    とはいえJsonObjectをそのままフィールドとして持つのも不便なので、使いやすくラップしましょう。例を回答の方に追記しておきます!

    キャンセル

+1

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

using System;

public class HogeClass<T>
{
    public HogeClass(T fuga)
    {
        Fuga = fuga;
    }
    public T Fuga;
    public int c, d;
}

public static class HogeFactory
{
    public static HogeClass<T> Create<T>(T fuga)
    {
        return new HogeClass<T>(fuga);
    }
}

var hoge = HogeFactory.Create(new { a = 1, b = 2 });
Console.WriteLine(hoge.Fuga.a);

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

追記

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

using System;

public class HogeClass
{
    public (int a, int b) Fuga;
    public int c, d;
}

var hoge = new HogeClass() { Fuga = (1, 2) };
Console.WriteLine(hoge.Fuga.a);

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/26 11:54

    回答ありがとうございます.

    ジェネリック型を使うほうはちょっと読みづらいなぁと感じました

    ValueTupleなんて物があるのですね,C#7.0・・・.
    調べてみたら値形なんですね,これ.
    色々試してみたいと思います

    キャンセル

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

  • ただいまの回答率 90.50%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • C#

    7116questions

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