C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[4]: using 文とインスタンスの破棄


(過去ログ 171 を表示中)

[トピック内 6 記事 (1 - 6 表示)]  << 0 >>

■98337 / inTopicNo.1)  using 文とインスタンスの破棄
  
□投稿者/ 星は昴 (11回)-(2021/11/02(Tue) 19:36:10)

分類:[C#] 

 98334 に続くインスタンスの生成と破棄シリーズ第3弾です(笑)。連投恐れ入ります。

 Pb はデザイナーで貼り付けたPictureBox です。以下の@ABに対する疑問に答えていただければ幸いです。

public partial class Form1 : Form
{
  Bitmap bmp;//各イベントハンドラで共有して使うためここで宣言

  private void BtnFileOpen_Click(object sender, EventArgs e)
  {
    OpenFileDialog dialog = new OpenFileDialog();
    if (dialog.ShowDialog() == DialogResult.OK)
    {
      try
      {
        bmp = new Bitmap(dialog.FileName);
        if (Pb.Image != null)
        {
          Pb.Image.Dispose();
        }
        Pb.Image = bmp;
      }
      catch
      {
        MessageBox.Show("これは画像ファイルではありません", "エラー",
                MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
      }
    }
  }

  //小さな画像を PictureBox.BackgroundImage に敷き詰める
  private void BtnTile_Click(object sender, EventArgs e)
  {
    if (bmp.Width < 60 || bmp.Width > 120) { return; }
    if (bmp.Height < 60 || bmp.Height > 120) { return; }

    Bitmap backbmp = new Bitmap(bmp.Width, bmp.Height);
    Graphics g = Graphics.FromImage(backbmp); // @new はなぜ不要なのか?
    g.DrawImage(bmp, 0, 0, bmp.Width, bmp.Height);
    Pb.Width = bmp.Width * 10;
    Pb.Height = bmp.Height * 8;
    Pb.BackgroundImage = backbmp;
    Pb.BackgroundImageLayout = ImageLayout.Tile;
    Pb.Invalidate();
    g.Dispose(); //これはOKで
    //backbmp.Dispose(); が実行エラーとなるのはなぜか A
    
    /* インスタンスの破棄は C# のGCに任せればいいのだろうが、やはり気になるのでネット
    を検索したら using 文で囲めば Dispose は不要とあったので、見よう見まねで以下のよ
    うにコーディングしたら、あっさり実行エラー B
    using (var backbmp = new Bitmap(bmp.Width, bmp.Height))
    {
      Graphics g = Graphics.FromImage(backbmp);
      g.DrawImage(bmp, 0, 0, bmp.Width, bmp.Height);
      Pb.Width = bmp.Width * 10;
      Pb.Height = bmp.Height * 8;
      Pb.BackgroundImage = backbmp;
      Pb.BackgroundImageLayout = ImageLayout.Tile;
      Pb.Invalidate();
    }
    */
  }
}

引用返信 編集キー/
■98338 / inTopicNo.2)  Re[1]: using 文とインスタンスの破棄
□投稿者/ 魔界の仮面弁士 (3204回)-(2021/11/02(Tue) 22:30:06)
No98337 (星は昴 さん) に返信
> Graphics g = Graphics.FromImage(backbmp); // @new はなぜ不要なのか?

Grapchis クラスには public なコンストラクタが無いため、そもそも new できません。
そのかわり Grapchis クラスには、新しいインスタンスを返すための static メソッドが用意されています。

Graphics.FromHdc
Graphics.FromHwnd
Graphics.FromHwndInternal
Graphics.FromImage

これらは自分で Dispose する必要があります。


一方、自分でインスタンスを作成するのではなく、ライブラリ側から提供される
既存の Grapchis インスタンスを操作することもあります。
Paint イベントの e.Graphics などがそれにあたりますが、
こちらは自分が生成したものでは無いため、勝手に Dispose してはいけません。


> //backbmp.Dispose(); が実行エラーとなるのはなぜか A
backbmp はまだ、Pb.BackgroundImage によって使用されているためです。

今回のコードにある
 Bitmap backbmp = new Bitmap(bmp.Width, bmp.Height);
 Pb.BackgroundImage = backbmp;
という処理は、
 1) new Bitmap により、Bitmap のインスタンスが生成される
 2) そのインスタンスが、変数 backbmp から参照された状態になる
 3) そのインスタンスが、Pb.BackgroundImage からも参照された状態になる
という流れになります。

Pb を含めて、もはやどこからも利用されなくなった後であれば、Dispose しても構いません。


> あっさり実行エラー B
> using (var backbmp = new Bitmap(bmp.Width, bmp.Height))
> {
これもAと同じ理屈です。
Pb が使用中のオブジェクトを取り上げて、廃棄しちゃったらアカン。
引用返信 編集キー/
■98339 / inTopicNo.3)  Re[1]: using 文とインスタンスの破棄
□投稿者/ 魔界の仮面弁士 (3205回)-(2021/11/02(Tue) 22:53:19)
No98337 (星は昴 さん) に返信
> 98334 に続くインスタンスの生成と破棄シリーズ第3弾です(笑)。連投恐れ入ります。

IDisposable なオブジェクトは、PictureBox や Form や Timer、
OpenFileDialog や DataSet、Font や Brush や Graphics などなど多岐にわたりますが、
Dispose する必要が無いオブジェクトもあれば、利用者が迂闊に Dispose できないものもあるので
慣れるまでは分かりにくいですよね…。

とはいえ基本的には、「自分で生成した物は、もう使わなくなったら処分しろ」というルールです。
そして「他人が生成したもの」や「他でまだ使っているオブジェクト」は、勝手に処分してはいけません。


>  if (Pb.Image != null)
>  {
>    Pb.Image.Dispose();
>  }

ちなみに C#6 以降のバージョンでは、上記を
 Pb.Image?.Dispose();
と一行で書けます。


> private void BtnTile_Click(object sender, EventArgs e)
> {
>   if (bmp.Width < 60 || bmp.Width > 120) { return; }
>   if (bmp.Height < 60 || bmp.Height > 120) { return; }

ガード句として
 if (bmp == null) { return; }
も追加しておくことをお奨めします。

BtnFileOpen が押される前に BtnTile が押された場合、
NullReferenceException になってしまうと思うので。


> Graphics g = Graphics.FromImage(backbmp);
ここは
 using (Graphics g = Graphics.FromImage(backbmp))
を使った方が良いですね。
Bitmap と違って、他の場所で利用されているものでは無いため、
その場で処分してしまって構いません。


> Pb.Invalidate();
この行はあっても無くても同じかな。
引用返信 編集キー/
■98340 / inTopicNo.4)  Re[2]: using 文とインスタンスの破棄
□投稿者/ 星は昴 (12回)-(2021/11/02(Tue) 23:50:29)
No98339 (魔界の仮面弁士 さん) に返信

 詳細かつ丁寧な解説、まことにありがとうございました。まだ消化しきれていませんが、大変勉強になります。

 using 文の記述について追加質問です。

  using (var backbmp = new Bitmap(bmp.Width, bmp.Height)) //var をつけないと文法エラー

に対し回答の中にあった

  using (Graphics g = Graphics.FromImage(backbmp))

は var がありません。「c# var using」で検索したのですが、いまいちよくわかりません。

引用返信 編集キー/
■98341 / inTopicNo.5)  Re[3]: using 文とインスタンスの破棄
□投稿者/ 魔界の仮面弁士 (3206回)-(2021/11/03(Wed) 00:19:43)
No98340 (星は昴 さん) に返信
> 「c# var using」で検索したのですが、いまいちよくわかりません。

int a = 10;
string b = "x";
char c = 'X';

これらのコードは

var a = 10;
var b = "x";
var c = 'X';

と同義です。
https://atmarkit.itmedia.co.jp/fdotnet/csharp30/csharp30_03/csharp30_03_01.html

var 宣言されたローカル変数の宣言では、
かならず「=」による初期化子が必要です。
そしてその右辺のデータ型によって、
左辺の変数の型が、コンパイル時に確定されます。
(この自動判断を「ローカル変数の型推論」と呼ぶことがあります。)


なお蛇足ですが、変数の事を英語で variable と呼びます。
変数宣言に var キーワードが使われた由来。
引用返信 編集キー/
■98342 / inTopicNo.6)  Re[4]: using 文とインスタンスの破棄
□投稿者/ 星は昴 (13回)-(2021/11/03(Wed) 06:31:18)
No98341 (魔界の仮面弁士 さん) に返信
 丁寧な回答まことにありがとうございました。var について検索してもよくわからなかったのですが、とてもよくわかりました。

 紹介していただいた

> https://atmarkit.itmedia.co.jp/fdotnet/csharp30/csharp30_03/csharp30_03_01.html

の記事も興味深く拝読しました。


解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -