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

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

ただいまの
回答率

90.51%

  • C#

    7112questions

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

1対多のモデルで1のインスタンス変数を多のクラスに引き渡す有効な方法

解決済

回答 1

投稿

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

tkow

score 1173

とても些細な質問なのですが、お答えしていただけるとありがたいです。

現在C#で書いているプログラムの中であるクラスのインスタンスのメンバにDictionaryで管理された多のモデルがあります。

1のモデルのインスタンス生成時に、そのインスタンス内で管理されている多のモデルのインスタンスのみで有効な共通の変数を保持して多のモデルで定義されているメソッド内で使用したいのですが、多のモデルのインスタンス変数にこの値を持たせるのは同じインスタンスのメンバ間では冗長で,staticにすると1のモデルのインスタンス間で個別に設定することができないので、関数の引数で渡すという方法でやるのが普通なのかなと思いました。

しかし,その多のモデルの関数の中でその設定値を用いた再帰を行う必要がある処理があり,これによって引き渡された値は不変であるにも関わらず再帰の中でメモリ保持されて多数作られてしまうのではないかという懸念があり,(この質問書いてるうちに解決策を思いついてしまったのですが)変数への参照を引数に渡せばいいのだと思いましたが、もっといい方法ありますでしょうか。

ご意見いただけると嬉しいです。よろしくお願いいたします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

0

ご提示の状況を正しく解釈できてるか自信がないですが…
だいたい以下のような感じという認識であってますか??
(ご質問の際は書きかけでもいいので具体的なソースを載せていただいたほうが話が早いと思います)

//1のモデルとなるクラス
public class SingleModel
{
    private readonly Dictionary<int, MultiModel> multiDic;

    private readonly DateTime singleValue;

    public SingleModel()
    {
        //共通で使いたい変数値(仮にDateTimeとする)
        this.singleValue = DateTime.Now;

        //何らかのキーでDictionaryにして保持?
        this.multiDic = Enumerable.Range(1, 10)
                                  .ToDictionary(n => n, n => new MultiModel(n));
    }

    public void CallDoSomething()
    {
        //多モデルオブジェクトすべてに対して関数呼び出し
        foreach (var multi in multiDic.Values)
        {
            multi.DoSomething(this.singleValue);
        }
    }
}

//多のモデルとなるクラス
public class MultiModel
{
    public MultiModel(int key)
    {
    }

    public void DoSomething(DateTime date)
    {
        //何かする
        ...

        //何らかの条件で再帰したりする
        //→再帰する度にDateTimeが生成されて無駄なのでは??
        if (aCondition) DoSomething(date);
    }
}

そのうえで、「変数への参照を引数に渡せばいいのだ」というのは、
上記の MultiModel.DoSomething メソッドを
public void DoSomething(ref DateTime date) という形にするという意味でおっしゃってますか?

もしそういう意味でしたら、個人的には賛成できません。
「多のモデルの関数の中でその設定値を用いた再帰を行う必要がある」というのは
多モデルクラスの内部実装の都合であって、
内部実装の都合によって外部からの呼び出しI/Fが決められてしまうのはスマートでないように思います。
特に「ref」は「引数で渡した値が変更されるかもしれないよ」ということを表現する目的で
使うのが一般的なので、コードの保守に際しては混乱の元にもなるかと思います。

どうしても参照で渡したければ、

  • publicなDoSomethingメソッドで受け取った後に、そこからprivateな別メソッドを呼び出す際に
    初めて引数をrefにする(そっちのprivateメソッドのほうで再帰する)

または

  • その値のみをメンバに持つクラスを作成し、引数としてそのクラスのオブジェクトを渡す
    (structでなくclassならば参照渡しになる)
public class SingleValue  //実際の用途にあわせて意味のある名前をつける
{
    public DateTime Value { get; private set; }

    public SingleValue(DateTime value)
    {
        this.Value = value;
    }
}

などの方法をとるほうが良いかなと思います。

ただ、多少再帰するからといって
パフォーマンスに影響するほどではないのでは?という気もします。
「予測するな、計測せよ」と言われるとおり、パフォーマンスについては
実際に動かしてみて初めて分かる部分も大きいです。
まずは素直な形で作って、動かしてみて、問題があれば初めて最適化を考えるでも良いかもしれません。

もっというと、不変な値なのであれば素直にコンストラクタで渡してしまっても良いようにも思いました。
「多のモデルのインスタンス変数にこの値を持たせるのは同じインスタンスのメンバ間では冗長」
という気持ちも確かにわかりますが、readonlyなメンバにしておけば整合が取れなくなる心配はありませんし、
オブジェクトの状態が安定するメリットのほうが大きいのではないかと思いました。

いろいろ書きましたが、正直にいうとケースバイケースです。
具体的な状況がわからない以上、あくまで「基本的にはこう考えます」程度の意見だと思ってください。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/01/16 10:21

    ありがとうございます。回答に載せていただいたプログラムの例の解釈で合っています。

    プログラムを載せるべきかと思いはしたのですが、ほぼ自分の中で答えが出てしまっていたことと、解る人には文章でも正しく伝わる内容(現にoikaさんには正しく伝わっています)であると思ったので、載せるのを怠りました。説明足らずで余計な労力をお掛けしてしまい申し訳ありません。

    実験用のプログラムでビッグデータを突っ込むため、メモリは常に大量に使っているため最適化できるところを最適化したいという意図です。関数は一時変数生成し,実行終了まではガベージコレクタが働かない点で、値型で極端にサイズが大きなデータ(BigInteger)などを渡して再帰を行なった場合再帰回数によっては深刻になるのではないかという点で疑問に思いました。このような実装の違いの影響は実際些細だとは思うのですが,再帰を教えるような教材は値型でデータを渡すのでそれって本当は良くないのではとふと疑問に思った次第です。

    >「予測するな、計測せよ」と言われるとおり、パフォーマンスについては
    実際に動かしてみて初めて分かる部分も大きいです。

    これはおっしゃる通りですね。一通りコードのリファクタリングが終わったらどれくらい変わったか計測したいと思います。

    キャンセル

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

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

関連した質問

  • 解決済

    FuelPHPでモデル側でバリデートを行うにはどうすればいいでしょうか?

    FuelPHPをフレームワークとして使い始めました。 私はもともとCakePHPを使っていたので、Cakeの処理の仕方で便利な部分がたくさんありました。 例えば、バリデーションの

  • 解決済

    なぜstaticメソッドを使えばメンバ変数とは関係ない?

    現在「リーダブルコード」を読んでいるのですが、その中でなぜそうなるかわからない文章がありました。 それの説明をしていただきたく投稿しました。 その内容は以下です。 p117 

  • 解決済

    インスタンス化でnullpointerexception

    38行目のTextView mTextView = new TextView(this);でエラーが起きているようです。表示する段階でnullならエラーが出るのがまだ分かるですが、

  • 解決済

    Cakephp3 $thisの意味

    Cakephp3(おそらくCakephp3に限ったことではない)において $thisの使われ方で、以下のコードをよく見かけます。 class PostsController

  • 解決済

    javaに関する基本的な質問

    ※かなり基本的な質問になります<(_ _)> import java.io.BufferedReader; import java.io.InputStreamReader;

  • 解決済

    Java:Container contentPane = getContentPane();

    Javaについて初歩的な質問です。 Container contentPane = getContentPane(); の宣言ですが、メインメソッド内で宣言するとエラー

  • 受付中

    異なるウィンドウ間で値を参照できない

    前提・実現したいこと JavaFXでメインウィンドウと別ウィンドウに設定画面を持ったシステムを作っています。 どちらも同じファイルControllerとしています。 設定画面

  • 受付中

    C++ カプセル化のこと

    こんにちは。 最下部に5つ質問がありますので、答えられる範囲で お願いします。全部解答してくださる方大歓迎です(#^^#) C++だとカプセル化ができますが、 ゲッ

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

  • C#

    7112questions

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