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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C#

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

Windows

Windowsは、マイクロソフト社が開発したオペレーティングシステムです。当初は、MS-DOSに変わるOSとして開発されました。 GUIを採用し、主にインテル系のCPUを搭載したコンピューターで動作します。Windows系OSのシェアは、90%を超えるといわれています。 パソコン用以外に、POSシステムやスマートフォンなどの携帯端末用、サーバ用のOSもあります。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

Q&A

解決済

2回答

2521閲覧

usingのDispose実行タイミングについて

aglkjggg

総合スコア769

C#

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

Windows

Windowsは、マイクロソフト社が開発したオペレーティングシステムです。当初は、MS-DOSに変わるOSとして開発されました。 GUIを採用し、主にインテル系のCPUを搭載したコンピューターで動作します。Windows系OSのシェアは、90%を超えるといわれています。 パソコン用以外に、POSシステムやスマートフォンなどの携帯端末用、サーバ用のOSもあります。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

0グッド

0クリップ

投稿2018/09/06 11:47

編集2018/09/06 11:48

前提

以下のプログラムを実行するとエラーが発生します。
エラー内容は以下の通りです。

ハンドルされていない例外: System.ArgumentException: 使用されたパラメーターが有効ではありません。 場所 System.Drawing.Image.get_Width() 場所 System.Drawing.Bitmap.GetPixel(Int32 x, Int32 y) 場所 GetPixelColor.Program.<GetPixelColorsFromBitmap>d__1.MoveNext() 場所 C:\Program\ImageTest\ImageTest\Program.cs:行 21 場所 GetPixelColor.Program.Main(String[] args) 場所 C:\Program\ImageTest\ImageTest\Program.cs:行 58

usingの効果(Dispose実行)かなりが早い段階で行われている為、bmpオブジェクトのメソッド実行時にエラーが出力されたのかと推測します。
しかし、usingの書き方としては間違っていないと思っております。
なぜ以下のプログラムがエラーとなるか分かりません。

質問

Q. エラーの原因が不明です、なぜエラーが出力されたのでしょうか?

該当のソースコード

  • rgb.png

ダウンロード
※画像ファイルへのリンクをあえて貼っています。3ピクセルしか無い為見逃す可能性がある為です。

  • Program.cs

C#

1using System; 2using System.Collections.Generic; 3using System.Drawing; 4using System.Linq; 5using System.Text; 6using System.Threading.Tasks; 7 8namespace GetPixelColor 9{ 10 class Program 11 { 12 const string ImgFileName = "rgb.png"; 13 14 /// <summary> 15 /// 指定したBitmapオブジェクトからピクセルの色情報を取得する 16 /// </summary> 17 static IEnumerable<Color> GetPixelColorsFromBitmap(Bitmap bmp) 18 { 19 foreach (var x in Enumerable.Range(0, 3)) 20 { 21 yield return bmp.GetPixel(x, 0); 22 } 23 } 24 25 /// <summary> 26 /// 指定したファイル名からピクセルの色情報を取得する 27 /// </summary> 28 static IEnumerable<Color> GetPixelColorsFromFileName(string fileName) 29 { 30 // NG 31 using (var bmp = new Bitmap(ImgFileName)) 32 { 33 return GetPixelColorsFromBitmap(bmp); 34 } 35 36 /* 37 // OK1 38 using (var bmp = new Bitmap(ImgFileName)) 39 { 40 return GetColorsFromBitmap(bmp).ToArray(); 41 } 42 */ 43 44 /* 45 // OK2 46 var bmp = new Bitmap(ImgFileName); 47 return GetColorsFromBitmap(bmp); 48 */ 49 } 50 51 static void Main(string[] args) 52 { 53 foreach (var x in GetPixelColorsFromBitmap(new Bitmap(ImgFileName))) 54 { 55 Console.WriteLine($"({x.R}, {x.G}, {x.B})"); 56 } 57 58 foreach (var x in GetPixelColorsFromFileName(ImgFileName)) 59 { 60 Console.WriteLine($"({x.R}, {x.G}, {x.B})"); 61 } 62 } 63 } 64} 65

試したこと

  • OK1のようにプログラムを変更

期待する出力が得られましたがなぜこの場合エラーが消えたのか不明です。
ToArrayするとなぜusingのDipose実行タイミングが変化するのでしょうか。

  • OK2のようにプログラムを変更

usingを消した為、期待する出力が得られました。
この事からusingが原因ではないかという推測をたてることができます。

補足情報(FW/ツールのバージョンなど)

  • Windows 10 64bit 1709
  • Visual Studio 2017
  • .NET Framework 4.6.1
  • C# 7.3

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

これはyield returnの遅延実行される性質のためです。
NGの場合はusingブロックを抜けた後にIEnumerableが評価されるために、既にDiposeされてしまっています。
OK1ではusingブロック内のToArrayで評価されるのでエラーになりません。

遅延実行させたいのなら、このメソッドもyeild returnしてやると良いです。

C#

1static IEnumerable<Color> GetPixelColorsFromFileName(string fileName) 2{ 3 using (var bmp = new Bitmap(ImgFileName)) 4 { 5 foreach(var x in GetPixelColorsFromBitmap(bmp)) 6 { 7 yeild return ; 8 } 9 } 10}

投稿2018/09/06 12:22

編集2018/09/06 12:23
YamakawaJunichi

総合スコア630

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

aglkjggg

2018/09/06 13:54 編集

解説とサンプルコードありがとうございます! 原因は理解できたけど遅延評価として実装するにはどうすれば…?とちょうど考えていた所でした!
guest

0

ベストアンサー

Disposeのタイミングというより、yield return bmp.GetPixel(x, 0);の実行タイミングの問題ではないですかね?

NGの時の場合
yield return bmp.GetPixel(x, 0);が実行されるタイミングは実際に値が要求された場所
すなわちMainでのforeachのループ時になります。
ですので、GetPixelColorsFromFileNameはすでに抜けていて、usingの範囲外となりbitmapはDisposeされた後の実行となります。

OK1の場合は
ToArrayすることにより、ここで全ての要素を要求する形になります。
この為、yield return bmp.GetPixel(x, 0);が実行されるタイミングはusing内となります。

投稿2018/09/06 12:15

YAmaGNZ

総合スコア10242

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

aglkjggg

2018/09/06 13:52 編集

usingに固執して考えていた上に、遅延評価についての理解が十分ではありませんでした>< わかりやすくNG理由、OKな理由、評価タイミングについて書いていただきありがとうございます! 理解できました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問