C#開発における落とし穴~リソース編~
かなり久しぶりにブログを投稿します。
技術担当の大林です。
ちょっと思うところがあったので、C#の開発における落とし穴、所謂うっかりやらかしたり勘違いしやすい部分について少し書こうと思います。
※当記事はVisualStudioでの開発を想定して執筆しております。
開発環境次第では当てはまらない事もある事を、あらかじめご了承ください。
さて、タイトルにも書いていますが、今回はリソースについて少し触れようと思います。
プロジェクトを作ると、Propertiesの所に出来てるアレのことです。
↓アレ
試しに使ってみましょう。
画像だけを持たせるような以下のようなTestObjを作成し、単純に生成するだけのコードを実行してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class TestObj { private Image image = Properties.Resources.TestImage; } private void TestObj_Create() { int max = 10000; var list = new List<TestObj>(max); for (int i = 0; i < max; i++) { list.Add(new TestObj()); } } |
そして、実行結果をタスクマネージャーで確認……(WindowsFormsApp2.exe)
結果は「約139MB」!
ただ1万個程度のクラスを実体化しただけなのに、かなりのメモリ消費量です。
うっかり「そのまま使えるだろう!きっと!!!」などというノリで変数に持たせてしまうと、痛い目を見る事になってしまいます。
基本的にこちらのリソース、定義の方を見てみると、下記のように「ResourceManager」クラスを通してデータを利用する形になっているのですが、ResourceManagerのGetObjectで、その都度オブジェクトが生成されているようです。
この辺りは私見になりますが、基本的にロケーション等での差し替えを管理してるだけで、リソース自体を効果的にキャッシュ・開放等はしてくれて無さそうな感じなのかなと。
(Managerとは何だったのか……)
/// <summary>
/// 型 System.Drawing.Bitmap のローカライズされたリソースを検索します。
/// </summary>
1 2 3 4 5 6 | internal static System.Drawing.Bitmap TestImage { get { object obj = ResourceManager.GetObject("TestImage", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } |
さて、読み込んだ画像を個別に色々編集したりするならまだしも、流石に同じものを持たせたいだけの使い道なのに個数分確保されてしまうというのはちょっとやり過ぎなので、少し小細工をして、TestObjクラスの方を書き換えてみます。
具体的には、イメージをstaticで一度だけ取得し、使いまわすような形でリソースを持たせる形にしてみます。
1 2 3 4 5 6 | public class TestObj { //↓画像キャッシュ用にstatic変数を噛ませる private static Image imageCache = Properties.Resources.TestImage; private Image image = imageCache; } |
1 2 3 4 5 6 7 8 9 | private void TestObj_Create() { int max = 10000; var list = new List<TestObj>(max); for (int i = 0; i < max; i++) { list.Add(new TestObj()); } } |
そして、再度、実行結果をタスクマネージャーで確認……(WindowsFormsApp2.exe)
結果は「約8.9MB」!
単純な小細工ではありますが、メモリ使用量が格段に減った事が分かりますね。
ケースバイケースなので、一概に使い回せばOK!という訳ではないですが、オーナードロー等を行うような部品を作る等、複数個所で使うようなクラスを作成する際は、この辺りを留意して開発しておかないと「いつの間にかめっちゃメモリ使用量増えてる!?」みたいな事になってしまいかねません。
やむをえない場合を除き、画像等のリソースを使用する時は、基本的に「Resource⇒キャッシュ用の静的変数(または管理クラス等)⇒使用」といった流れを取る癖をつけておくと、無駄の少ない軽快なアプリが作成できると思います。
後、リソースのDispose忘れによるリーク等の回避にもなりますし、管理上も有用だと思います。
ただし、この手法を使う場合は逆に、うっかりキャッシュをDisposeしてしまわないようには要注意です。
では!
見ていただき、ありがとうございました!