5

「using」ブロック内での再初期化は、常に避けるべき悪い考えです。それでも私はこれを尋ねるつもりです:

「using」呼び出しが、最後の参照または再初期化ではなく、元の値で破棄されるのはなぜですか (try finally ブロックが使用されている場合に発生します)。

MyClass b = new MyClass();// implements Idisposable
MyClass c = new MyClass();
MyClass a ; 

 using (a = new MyClass())
 {
                a = b;
                a = c;
 }

上記のコードでは、dispose は元の参照で呼び出され、新しい参照では呼び出されません。これは、dispose メソッドでコンソールに何かを出力することで簡単に確認できます。

ただし、try{} finally コードでは、最後の参照破棄メソッドが呼び出されます。

try
{
   a = new MyClass();
   a = b;
   a = c;
 }
  finally 
   {
   a.Dispose();
  }

MSDN : using ステートメントにより、オブジェクトのメソッドを呼び出しているときに例外が発生した場合でも Dispose が呼び出されることが保証されます。

using (Font font1 = new Font("Arial", 10.0f)) 
{
    byte charset = font1.GdiCharSet;
}

基本的に「使用する」は次のように変換されます。

{
  Font font1 = new Font("Arial", 10.0f);
  try
  {
    byte charset = font1.GdiCharSet;
  }
  finally
  {
    if (font1 != null)
      ((IDisposable)font1).Dispose();
  }
}
4

6 に答える 6

8

usingC#仕様で定義されているステートメントには、次の2つの形式があります。

using-statement:
    using   (    resource-acquisition   )    embedded-statement
resource-acquisition:
    local-variable-declaration
    expression

があればlocal-variable-declaration、質問はありません。変数はusingブロックで読み取り専用になり、変更することはできません。仕様によると:

8.13usingステートメント

[...]どちらの拡張でも、埋め込みステートメントではリソース変数は読み取り専用です。

ここでは、2番目の形式を扱っています。ここで、はであり、でresource-acquisitionexpressionありませんlocal-variable-declaration。その場合、C#仕様は明確に次のように述べています。

フォームのusingステートメント

 using (expression) statement

には同じ2つの可能な展開がありますが、この場合、ResourceTypeは暗黙的に式のコンパイル時型であり、リソース変数は埋め込みステートメントではアクセスできず、見えません[強調鉱山]

明らかに、非表示でアクセスできない変数を変更することはできません。その値は、usingresource-acquisition句でのみ割り当てられます。したがって、新しい値ではなく、変数の古い値が使用されます。

すでに宣言されている変数への割り当てを処理するときは、この形式のusingステートメントを使用しています。次のような変数に値を割り当てているという事実

using ( x = something )

無関係です。全体x = somethingが式として扱われ、その式の値のみが重要です。ここで「リソース変数」は「x」ではないことを知っておくことが重要です。それは目に見えない変数です。コンパイラーの観点からは、以下の構成に大きな違いはありません。

using ( something ) 
using ( x = something )
using ( y = x = something )

すべての場合において、式は実行され、値は変数ではなく、確実に破棄されるものです。これが定義された動作ではなく、上記のブロックに3行目を書き込んだ場合、コンパイラは何をするはずですか?処分しxますか?y?両方とも?ない?現在の動作は理にかなっています。

于 2010-02-22T13:00:14.583 に答える
3

usingは、usingで宣言されたオブジェクトに破棄された呼び出しを呼び出すことを約束するものと見なすことができます。これは、私見、理にかなっている唯一のことです!

再割り当てされた値に対してdisposeを呼び出すと、元の値は破棄されません。

于 2010-02-22T12:57:27.070 に答える
3

コンパイラは次のコードを生成します。

MyClass b = new MyClass();
MyClass a;
MyClass cs$3$000 = a = new MyClass();
try {
  a = b;
}
finally {
  if (cs$3$000 != null) cs$3$000.Dispose();
}

自動生成されたcs$3$000ローカル変数がコントラクトを実装します。

于 2010-02-22T13:08:33.440 に答える
1

「using」は、参照を格納するための独自の変数を作成しているようです。この変数は「a」に初期化され、次にオブジェクトの最初のインスタンスに初期化されます。後で「a」を変更しても、「using」ステートメントに格納されていた元の参照は実際には変更されません。usingは、変数ではなく、usingステートメントで言及されている実際のオブジェクトを破棄する責任があるため、これはかなり優れた機能のようです。

于 2010-02-22T12:58:52.650 に答える
1

ええ、それは面白いです。

だから私は逆コンパイルされたコードを見ました:

  IL_0000:  nop
  IL_0001:  newobj     instance void ConsoleApplication17.Foo1::.ctor()
  IL_0006:  dup
  IL_0007:  stloc.0
  IL_0008:  stloc.1 // 1. note this
  .try
  {
    IL_0009:  nop
    IL_000a:  newobj     instance void ConsoleApplication17.Foo2::.ctor()
    IL_000f:  stloc.0 // 2. and this
    IL_0010:  nop
    IL_0011:  leave.s    IL_0023
  }  // end .try
  finally
  {
    IL_0013:  ldloc.1 // 3. and this
    IL_0014:  ldnull
    IL_0015:  ceq
    IL_0017:  stloc.2
    IL_0018:  ldloc.2
    IL_0019:  brtrue.s   IL_0022
    IL_001b:  ldloc.1
    IL_001c:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_0021:  nop
    IL_0022:  endfinally
  }  // end handler
  IL_0023:  nop

したがって、実際に参照をコピーし、後でそのコピーを使用して、ファイナライザーに関して、実際には何も達成せずに変数をリセットできるようにします。

実際、usingステートメントで「読み取り専用」変数しか使用できないと便利です。少し紛らわしいです。そして確かに、MSDNは誤解を招くものです。

于 2010-02-22T13:05:13.840 に答える
0

disposeは、using句の引数で参照されているオブジェクトに対して呼び出されます。とても簡単です。

于 2010-02-22T12:57:34.617 に答える