アプリ版:「スタンプのみでお礼する」機能のリリースについて

C#のメモリ解放についてご教授ください。
以下の例のAAAクラスで、CCCクラスのListを破棄およびメモリ解放するにはどのようにすれば良いでしょうか。
null代入だけで解放されるのでしたら御の字なのですが・・・。
※newはあまり好ましくありません。
例)
class AAA{
BBB b;
AAA(){ b = new BBB(); }
add(string s){ b.add(s); }
clear(){ ※ここでCCCクラスのListデータを破棄し、メモリ解放されるようにしたい。 }
}

class BBB{
CCC c;
BBB(){ c = new CCC(); }
add(string s){ c.add(s); }
}

class CCC{
List<string> list;
CCC(){ list = new List<string>(); }
add(string s){ list.add(s); }
}

A 回答 (6件)

>clearはAAAで問答無用で削除ならチェック不要という考えがありました。



すみません誤解していました。clearはAAAが持つインスタンスの参照を手放すのですね。
それなら、nullチェックは不要です。

ちなみに、C#では「インスタンスの参照を手放す」=「メモリが解放される」ではありません。
あくまでBBBのインスタンスたるbをもう使えなくなるというだけで、
インスタンスがいつ破壊される(メモリが解放される)かはユーザにはわかりません(GCのお仕事)。

で、ここから誤解だといけないのですが、先に回答したように
BBBがCCCやDDDをコントロールする、AAAはBBBを通じてのみそれを行う…
という理解でいいんですよね?(そういう意味でMediatorという言葉を使いました)
ならば、AAAがCCCやDDDの内部を知ってしまうのはどのような意味でも良くないでしょう。
クラス階層をわざわざ破壊するようなことはすべきではないです。

>BBBからCCCデータなどを消し、AAAに戻ってきて改めてBBBを消すという流れは無駄を感じていたため

それを無駄と感じるのは
・実は不必要なクラス設計にしている
・クラス階層の必要性についての理解に乏しい
のどちらかだと思います。

何事にも例外はありますが、今回のケースは例外扱いするメリットが感じられないです。
    • good
    • 0
この回答へのお礼

すみません、遅くなりました。
あれから設計見直しが入りました。
CCCのポジションにあたる処理が、実際はファイル読み込み、解析、抽出を行うような処理で、そこそこ時間がかかる兼ね合いで、AAAを呼び出すアプリ側がその間動かなくなるのは困る、という話になり、BBBでスレッド処理からCCCを呼び出すことになりました。
また、これによりAAAの呼び出しをアプリ側が複数回したいということになり、CCCのインスタンスは複数個持ち、clearは指定されたもののみに変更となったため、BBBでCCCの精査を行う必要がでたため、clearはAAAでのBBB破棄からかわることとなりました。
何度もご解答ありがとうございました。

お礼日時:2018/08/03 08:50

質問のソースを見る限りでは


b=new BBB();
が最もまっとうでしょうね。
bの差していた古いBBBのインスタンスは、それが抱えている
ものも含めて、いずれGCされます。

BBBをnewするだけでメモリを馬鹿食いする設計なのでしょうか?
もしそうなら
b=null;
でも仕方ないですが、bのnullチェックコ―ドがクラス全体に蔓延して
汚ならしいコードになるのでご注意を。私なら見直します。

それからGC.collectは超遅いので、細かな処理の中に散りばめたりしないこと。
全く使わないことを強く推奨します。
    • good
    • 0

クラスAAA内で、


public void Clear() { b = null; }
としただけではクラスBBB、クラスCCCおよびクラスCCC内のlist変数はメモリ解放されません。
ガベージコレクタが走行した時に初めて解放されます。
これはデストラクタに何かコードを書いてみれば分かります。
b = null;としただけでは、BBBクラス、CCCクラスともにデストラクタは走行しません。

明示的なメモリ解放が必要ならば、ガベージコレクタを明示的に実施する必要があります。
GC.Collect();


余談ですが、よく経緯を分かっていないのでよく分からないんですが、
AAAクラスをインスタンス化する前に、AAAクラスのClear()メソッドは呼べないと思うんですが。
それと、AAA.Clear()後に、AAA.Add()をしたら死にますね。
public void Add(string s)
{
if (b == null) b = new BBB();
b.Add(s);
}
となっていなければ。
もしくは、AAAクラスがBBBクラスやCCCクラスに依存する形ならば、AAA.Clear()を実施した時に、
Add()している値だけがクリアされ、クラスインスタンスまで破棄されてはならないという仕組みになりませんかね?
必要になった時、都度BBBクラス、CCCクラスインスタンスが新たに作られるのはコスト高くありませんか?

AAAクラス、BBBクラス、CCCクラスの設計を見直された方がよいのでは。
    • good
    • 0

確認ですが、AAAがインスタンスを作っているのはBBBクラスだけなんですよね?


それだったらCCCとDDDのどちらかだろうが、両方だろうが、
AAAがそのことを知るべきではなく、CCCとDDDを扱っているBBBの責任で面倒見るべきです。
そうなっていないのなら、クラス構造から見直したほうがいいです。

>ユーザーがインスタンス生成前にclearを呼ぶと、

インスタンス生成とインスタンスへのアクセスが前後する可能性があるなら、nullチェックは必須でしょう。
CCCとDDDの面倒はBBBが見るのですから、BBBのインスタンスができているかどうかチェックすれば足りるはずです。

>AAAでbをnullすれば

元のご質問はCCCクラスのListオブジェクトを破壊したいということだったかと思いますが、
おっしゃるようにすればAAAはBBBクラスのオブジェクトをすべて手放します。
それでご希望にかなってます?
    • good
    • 0
この回答へのお礼

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

〉インスタンス生成とインスタンスへのアクセスが前後する可能性があるなら、nullチェックは必須でしょう。

→やはりnullチェックした方がいいですか・・・。add側でチェックする気はあったのですが、clearはAAAで問答無用で削除ならチェック不要という考えがありました。予定ではBBBインスタンスも含めた完全削除を考えていたため、BBBからCCCデータなどを消し、AAAに戻ってきて改めてBBBを消すという流れは無駄を感じていたため、AAAのみでデータ破棄、メモリ解放が完結させられるならそうしたかった、という気持ちです。やはりこういう場合でも、BBBでCCC削除などを実施した方がよいのでしょうか?

お礼日時:2018/07/30 14:51

No.1です。



>①AAAは呼び出し元クラスのIFの立ち位置として、実処理を置きたくなかった。

その設計は良いと思います。
さらに言えばAAAは「実処理は同実装されているか」も知らなくて

②本来CCC以外にDDDなどがあり、BBBでどのインスタンス生成を行うかなどの分岐があります。①の都合により、BBBを連携用として作っています。

ああ、じゃ、BBBはMediatorの役割なのですね。
AAAは実際に使われているのはCCCなのかDDDなのかわからないという理解でいいでしょうか?

であれば、なおのことCCCのメンバーデストラクトはBBBの役割で、
AAAから直接CCCをコントロールすべきでないと思います。

AAA.clear()は単純にBBB.clear()を呼んで、
実際は何をすればいいのかはBBBに任せるように設計すべきかと思います。
    • good
    • 0
この回答へのお礼

回答ありがとうございます。
確かにBBBでという考えも分かるのですが、CCC、DDDのどちらかを消したいという主旨ではなく、CCC、DDD含め、完全消去をしたいのです。
で、ここも伝え漏れなのですが、本来はAAAでBBBをnewするのはコンストラクタではなく、別場所になります。ですので、ユーザーがインスタンス生成前にclearを呼ぶと、インスタンスが無いの事象が発生します。かといって、clearでインスタンスがあるかチェックとかも避けたいところがあるので、問答無用でAAA内で全てを捨てたい、という考えのもとの結果が今になります。

話が戻って恐縮ですが、先の質問お礼にも挙げたnull代入については、AAAでbをnullすれば良いの解釈でよろしかったでしょうか?
何度もすみません。

お礼日時:2018/07/30 14:04

Listは参照型ですから、null代入すれば参照を手放し、メモリはガベージとなっていずれGCに掃除されます。


でも、class BBBを飛ばしてclass AAAにやらせたいのですか?

現在はAAAはCCCがListを使っていることなんて知らないし、それどころかCCCの存在も知らないですよね。
Listをインタフェースとして見せるようにすればAAAからやるのも可能でしょうが、
現在の(それなりに)疎結合になっているクラス間依存を一気に大きくしてしまう気がします。

Listの開放はCCC自身にやらせて、BBB(を経由してAAA)にインタフェースを提供するほうがデザイン的にはいいと思います。クラス間依存も複雑になりませんし。
    • good
    • 1
この回答へのお礼

回答ありがとうございます。
null代入というのは、AAAでbにnullを入れればという解釈でよろしかったでしょうか?

例について、AAA→BBB→CCCといった作りになっているのは、以下の要因からです。
①AAAは呼び出し元クラスのIFの立ち位置として、実処理を置きたくなかった。
②本来CCC以外にDDDなどがあり、BBBでどのインスタンス生成を行うかなどの分岐があります。①の都合により、BBBを連携用として作っています。

本質問の意図を伝えるために省いたり、作り替えたりしてる部分が多々ありました。

お礼日時:2018/07/30 13:03

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています