ProcessのインスタンスってDisposableだよね?
だから…
Process proc = Process.GetCurrentProcess();
Console.WriteLine(proc.Id);
proc.Dispose();
って書けば良いよね
でも、こういう時の為にusing
構文がある
using(Process proc = Process.GetCurrentProcess()) {
Console.WriteLine(proc.Id);
}
みたいな感じでスッキリおっけー
対象が配列だったらどうすれば…
つまり…
foreach(Process proc in Process.GetProcesses()) {
Console.WriteLine(proc.Id);
proc.Dispose();
}
んー
だと、Dispose()
がいまいち不安なので、敢えて書くとこんな感じ?
foreach(Process proc in Process.GetProcesses()) {
try {
Console.WriteLine(proc.Id);
}
finally {
proc.Dispose();
}
}
あれー? なんか一世代巻き戻った感じ…
こういう時の為にusing
構文があるんだけど、どうやって組み込めば良いんだろう?
海外でも疑問に思われる方達はいっぱいおられる様で、例えばここら辺
でも、大丈夫
ここにヒントがある
別にusingステートメント
でインスタンスを取得しなくても、既出のインスタンスをただ置くだけ…
と云う書き方も可能
foreach(Process proc in Process.GetProcesses()) {
using(proc) {
Console.WriteLine(proc.Id);
}
}
ただし、これはベストプラクティスではない、とも書いてある
何故なら
foreach(Process proc in Process.GetProcesses()) {
using(proc) {
Console.WriteLine(proc.Id);
} // <== procはここでDisposeされる ので、procの中身は無効
// <== ここでprocにはアクセス可能 な為、不正なprocを参照できちゃう
}
うん、じゃぁこうしよう
foreach(Process proc in Process.GetProcesses())
using(proc) {
Console.WriteLine(proc.Id);
}
foreachの中身をusingステートメント単体だけに簡素化する事で、procのDispose後の隙間を作らない
まぁ、コーディングレベルの規約となるので「ベスト」な「プラクティス」ではない、と言われればそうかも知れないですけど…
ま、私個人としては、次善の策としてProcessインスタンスのDispose問題には結論が出たかな?
と一段落
もうちょっと踏み込んでみる
でも、本当にProcessインスタンスのDisposeは有効なんですよね?
MSのサイトにそう書いてあるんだから疑う必要はないんだろうけど、内部ではいったい何が起こっているかを掘り下げて、もう一段留飲を下げてみたいな、と
そこで、以下の単純なスニペットをILレベルで比較してみたいと思う
using(Process proc = Process.GetCurrentProcess()) {
Console.WriteLine(proc.Id);
}
と
Process proc = Process.GetCurrentProcess();
using(proc) {
Console.WriteLine(proc.Id);
}
ILの比較
ここでは、ILソースを手修正して比較し易く改変してあります
.method private hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2050
// Header size: 12
// Code size: 35 (0x23)
.maxstack 1
.entrypoint
.locals init (
[0] class [System]System.Diagnostics.Process proc
)
// {
IL_0000: nop
// using Process process = Process.GetCurrentProcess();
IL_0001: call class [System]System.Diagnostics.Process [System]System.Diagnostics.Process::GetCurrentProcess()
IL_0006: stloc.0
.try
{
// Console.WriteLine(process.Id);
IL_0009: nop
IL_000a: ldloc.0
IL_000b: callvirt instance int32 [System]System.Diagnostics.Process::get_Id()
IL_0010: call void [mscorlib]System.Console::WriteLine(int32)
IL_0015: nop
IL_0016: nop
IL_0017: leave.s IL_0024
} // end .try
finally
{
IL_0019: ldloc.0
IL_001a: brfalse.s IL_0023
IL_001c: ldloc.0
IL_001d: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0022: nop
IL_0023: endfinally
} // end handler
IL_0024: ret
} // end of method Program::Main
.method private hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2050
// Header size: 12
// Code size: 37 (0x25)
.maxstack 1
.entrypoint
.locals init (
[0] class [System]System.Diagnostics.Process proc,
==> [1] class [System]System.Diagnostics.Process
)
// {
IL_0000: nop
// Process currentProcess = Process.GetCurrentProcess();
IL_0001: call class [System]System.Diagnostics.Process [System]System.Diagnostics.Process::GetCurrentProcess()
IL_0006: stloc.0
// using (currentProcess)
==> IL_0007: ldloc.0
==> IL_0008: stloc.1
.try
{
// Console.WriteLine(currentProcess.Id);
IL_0009: nop
IL_000a: ldloc.0
IL_000b: callvirt instance int32 [System]System.Diagnostics.Process::get_Id()
IL_0010: call void [mscorlib]System.Console::WriteLine(int32)
IL_0015: nop
IL_0016: nop
IL_0017: leave.s IL_0024
} // end .try
finally
{
==> IL_0019: ldloc.1
IL_001a: brfalse.s IL_0023
==> IL_001c: ldloc.1
IL_001d: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0022: nop
IL_0023: endfinally
} // end handler
IL_0024: ret
} // end of method Program::Main
snippet#2
の行の先頭に==>
を付けたところが、有意に異なる場所(5ヶ所)
- 一つ目は、内部変数を一個余計に定義している
- 二つ目三つ目で、内部変数をコピーしている
- 四つ目と五つ目は、Dispose絡みの処理で、変数の参照先が異なっている
なんて、文字で書いても分かり難いので、図示してみる
snippet#1
こんな感じ?
using
抜ける時にSlot[0]
の変数に対してDispose()
掛けて、どっかにあるアンマネージドなリソースの破棄も行われている(筈)
snippet#2
Slot[0]
のローカル変数(GetCurrentProcess()
したデータ)を、usingステートメント
がSlot[1]
に単純コピー
なので、Process
の唯一のインスタンスを、二つのSlot
で共有している
using
を抜ける時には、Slot[1]
の変数経由でDispose()
を掛けている
でも、Slot[0]
の変数が残っている、けど、Process
のインスタンスはDispose
済み、と云う例の状態がIL
レベルでも読み取れる
てな訳で
次善の策としてのコーディング規約を導入することで、プロセス列挙時においてもusingを使ったProcessインスタンスのDisposeを安心して実装できる、と結論付けたい