2010-09-28T23:36:55の更新内容

programming/netfx2/overview/debugging/index.wiki.txt

current previous
1,863 1,420
 
${smdncms:title,デバッグと属性(System.Diagnostics)}
${smdncms:title,デバッグと属性(System.Diagnostics)}
+
${smdncms:keywords,#if,#define,#Const,Obsolete,Conditional,DebuggerDisplay,DebuggerStepThrough,DebuggerHidden,DebuggerNonUserCode}
 
${smdncms:meta,toc-amazonlivelink-keyword,books-jp,.net framework}
${smdncms:meta,toc-amazonlivelink-keyword,books-jp,.net framework}
~
System.Diagnostics名前空間にはデバッグ時に便利な属性がいくつか存在します。 ここではそのうちのいくつかを紹介します。
System.Diagnostics名前空間にはデバッグ時に便利な属性がいくつか存在します。 ここではそのうちのいくつかを紹介します。 また、それに先だって#Ifディレクティブとその用法を見ていきます。
 
#googleadunit
#googleadunit
 

        

        
~
*#ifディレクティブとConditional属性
*#Ifディレクティブ, #Constディレクティブ
~
**#ifディレクティブ
まずはじめに、属性ではありませんが#Ifディレクティブについて見てみます。 #Ifディレクティブは条件付きコンパイルを行うための構文で、例えばデバッグビルドの時だけ特定のコードを有効(または無効)にするといったような場合に使用します。 また、#Constディレクティブを用いて独自に条件付きコンパイル定数を定義することもできます。
~
Conditional属性の説明に入る前に、#ifディレクティブについて軽く触れておきます。 #ifディレクティブは条件付きコンパイルを行うための構文で、例えばデバッグビルドの時だけデバッグ用のコードを有効(または無効)にするといったような場合に使用します。 また、#defineディレクティブ(VBでは#Constディレクティブ)を用いてコード上で条件付きコンパイル定数(シンボル)を定義することもできます。

          
+

          
+
#tabpage(C#)
+
#code(cs){{
+
// イベントを記録する際に、時刻も記録する
+
//#define LogEventTime
+

          
+
using System;
+
using System.Threading;
+

          
+
class Sample
+
{
+
  // イベントを記録する
+
  static void LogEvent(string message)
+
  {
+
#if DEBUG
+
#if LogEventTime
+
    Console.WriteLine("{0}> {1}", DateTime.Now, message);
+
#else
+
    Console.WriteLine(message);
+
#endif
+
#endif
+
  }
+

          
+
  public static void Main()
+
  {
+
    // イベントを記録
+
    LogEvent("Start application");
+

          
+
    // 開始メッセージを出力
+
    Console.WriteLine("Begin calculation.");
+

          
+
    // いくつもの処理があるとする
+
    Thread.Sleep(1000);
+

          
+
    // 終了メッセージを出力
+
    Console.WriteLine("Calculation was finished.");
+

          
+
    // イベントを記録
+
    LogEvent("End application");
+
  }
+
}
+
}}
+
#tabpage(VB)
 
#code(vb){{
#code(vb){{
~
' イベントを記録する際に、時刻も記録する
Option Strict On
~
'#Const LogEventTime = True

          
-
' 時刻も出力する
-
#Const LogOutputTime = False
 

        

        
+
Imports System
 
Imports System.Threading
Imports System.Threading
 

        

        
~
Class Sample
Module AttributesForDebugging
~
  ' イベントを記録する

          
~
  Shared Sub LogEvent(ByVal message As String)
    ' イベントを記録する
~
#If DEBUG Then
    Sub LogEvent(ByVal ev As String)
~
#If LogEventTime Then

          
~
    Console.WriteLine("{0}: {1}", DateTime.Now, message)
#If Debug Then
-
#If LogOutputTime Then
-
        Console.WriteLine(ev + " Time: " + DateTime.Now.ToString())
 
#Else
#Else
~
    Console.WriteLine(message)
        Console.WriteLine(ev)
 
#End If
#End If
 
#End If
#End If
+
  End Sub
 

        

        
~
  Public Shared Sub Main()
    End Sub
~
    ' イベントを記録

          
~
    LogEvent("Start application")
    Sub Main()
-

          
-
        ' イベントを記録
-
        LogEvent("Start application")
-

          
-
        ' 開始メッセージを出力
-
        Console.WriteLine("Begin calculation.")
 

        

        
~
    ' 開始メッセージを出力
        ' いくつものややこしい処理があるとする
~
    Console.WriteLine("Begin calculation.")
        Thread.Sleep(3000)
 

        

        
~
    ' いくつもの処理があるとする
        ' 終了メッセージを出力
~
    Thread.Sleep(1000)
        Console.WriteLine("Calculation was finished.")
 

        

        
~
    ' 終了メッセージを出力
        ' イベントを記録
~
    Console.WriteLine("Calculation was finished.")
        LogEvent("End application")
 

        

        
~
    ' イベントを記録
    End Sub
~
    LogEvent("End application")

          
~
  End Sub
End Module
+
End Class
 
}}
}}
+
#tabpage-end
+

          
+
まず、このコードで#ifディレクティブを使用しているLogEvent()メソッドについて見てみます。 このメソッドの動作を簡単に説明すると、デバッグビルドの場合ではシンボル'DEBUG'が宣言されているのでメソッド内のコードがアクティブになります。 また、シンボル'LogOutputTime'が有効になっている場合は時刻を出力する方のコードがアクティブになり、 無効になっている場合はメッセージのみを出力する方のコードがアクティブになります。 コード上では#define(#Const)をコメントアウトしているためシンボル'LogOutputTime'は無効になっています。
 

        

        
~
リリースビルドとデバッグビルド、またシンボル'LogOutputTimeの値を変えた場合の実行結果は次のようになります。
まず、コードの説明をしますが、Main()のコードはそれほど重要ではありません。 実際に#Ifディレクティブを使用しているLogEvent()メソッドについて見てみます。 このメソッドの動作を簡単に説明すると、デバッグビルドの場合ではDebugスイッチが宣言されているので13行目から19 行目のコードはアクティブになります。 また、LogOutputTimeスイッチにTrueが指定されている場合は15行目がアクティブになり、 Falseの場合は17行目がアクティブになります。 リリースビルドとデバッグビルド、またそのそれぞれでLogOutputTimeの値を変えた場合の実行結果を次に示します。
 

        

        
~
#prompt(デバッグビルド、シンボル'LogOutputTime'が有効な場合){{
#prompt(デバッグビルド LogOutputTime = True){{
~
2003/03/30 11:42:46: Start application
Start application Time: 2003/03/30 11:42:44
 
Begin calculation.
Begin calculation.
 
Calculation was finished.
Calculation was finished.
~
2003/03/30 11:42:47: End application
End application Time: 2003/03/30 11:42:47
-
Press any key to continue
 
}}
}}
 

        

        
~
#prompt(デバッグビルド、シンボル'LogOutputTime'が無効な場合){{
#prompt(デバッグビルド LogOutputTime = False){{
 
Start application
Start application
 
Begin calculation.
Begin calculation.
 
Calculation was finished.
Calculation was finished.
 
End application
End application
-
Press any key to continue
 
}}
}}
 

        

        
~
#prompt(リリースビルドの場合){{
#prompt(リリースビルド){{
 
Begin calculation.
Begin calculation.
 
Calculation was finished.
Calculation was finished.
-
Press any key to continue
 
}}
}}
 

        

        
~
ここではこれ以上条件付きコンパイルについては踏み込まないので、より詳しくは以下のドキュメントを参照してください。
*Conditional属性
~
-&msdn(netfx,id,ed8yd1ha){C# プリプロセッサ ディレクティブ};
なぜ最初に#Ifディレクティブを紹介したかというと、#Ifディレクティブには欠点があるからです。 というのは、前の例のようにデバッグ時だけにあるメソッドを実行したいときにはメソッドの内のコードだけを#Ifディレクティブをくくらなければなりません。 メソッド宣言全体を#Ifでくくってしまうと、リリースビルドの時点ではこのメソッドはコンパイルされなくなり、呼び出しが無効になるからです。 これは呼び出し側にも#Ifディレクティブを適用すれば解決できますが、その数が多くなると非常に面倒です。 また、メソッド内のコードのみに適用した場合でも、リリースビルド時でもメソッドは呼び出されるので、無用なオーバーヘッドが生じます。
~
-&msdn(netfx,id,9ae6e432){条件付きコンパイル (Visual Basic)};

          
~

          
この問題を解決するにはConditional属性を使用することができます。 Conditional属性はメソッドに対して使用され、属性のコンストラクタに指定された条件付きコンパイル定数が定義されている時に限りそのメソッドが実際に呼び出されます。 それ以外の場合には実行されることはありません。 次のコードでその使用例を挙げます。
~
**#ifディレクティブの問題点

          
+
なぜ最初に#ifディレクティブを紹介したかというと、#ifディレクティブには不便な点があるからです。 というのは、前の例のようにデバッグ時のみあるメソッドを実行したいときには、メソッドの内のコードだけを#ifディレクティブをくくらなければなりません。 メソッド宣言全体を#ifでくくってしまうと、リリースビルドの時点ではこのメソッドはコンパイルされなくなり、呼び出しが無効になるからです。
+

          
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+

          
+
class Sample
+
{
+
  // デバッグ時のみイベントを記録したい
+
#if DEBUG
+
  static void LogEvent(string message)
+
  {
+
    Console.WriteLine(message);
+
  }
+
#endif
+

          
+
  public static void Main()
+
  {
+
    // リリースビルドではLogEventメソッドが存在しないためコンパイルエラーとなってしまう
+
    LogEvent("Start application");
+
  }
+
}
+
}}
+
#tabpage(VB)
 
#code(vb){{
#code(vb){{
~
Imports System
Option Strict On
-

          
-
' 時刻も出力する
-
#Const LogOutputTime = False
-

          
-
Imports System.Diagnostics
-
Imports System.Threading
-

          
-
Module AttributesForDebugging
 

        

        
~
Class Sample
    ' イベントを記録する
~
  ' デバッグ時のみイベントを記録したい
    <Conditional("Debug")> _
~
#If DEBUG Then
    Sub LogEvent(ByVal ev As String)
~
  Shared Sub LogEvent(ByVal message As String)

          
~
    Console.WriteLine(message)
#If LogOutputTime Then
~
  End Sub
        Console.WriteLine(ev + " Time: " + DateTime.Now.ToString())
-
#Else
-
        Console.WriteLine(ev)
 
#End If
#End If
 

        

        
~
  Public Shared Sub Main()
    End Sub
~
    ' リリースビルドではLogEventメソッドが存在しないためコンパイルエラーとなってしまう

          
~
    LogEvent("Start application")
    Sub Main()
~
  End Sub

          
~
End Class
        ' イベントを記録
~
}}
        LogEvent("Start application")
~
#tabpage-end

          
~

          
        ' 開始メッセージを出力
~
これは呼び出し側で#ifディレクティブを適用すれば解決できますが、その数が多くなると非常に面倒です。
        Console.WriteLine("Begin calculation.")
~

          

          
~
#tabpage(C#)
        ' いくつものややこしい処理があるとする
~
#code(cs){{
        Thread.Sleep(3000)
~
using System;

          
~

          
        ' 終了メッセージを出力
~
class Sample
        Console.WriteLine("Calculation was finished.")
+
{
+
  // デバッグ時のみイベントを記録したい
+
  static void LogEvent(string message)
+
  {
+
    Console.WriteLine(message);
+
  }
+

          
+
  public static void Main()
+
  {
+
    // これでコンパイルエラーにはならなくなるが
+
#if DEBUG
+
    LogEvent("Start application");
+
#endif
+

          
+
    Console.WriteLine("Hello, world!");
+

          
+
    // 呼び出しの数だけ#ifを記述するのは面倒
+
#if DEBUG
+
    LogEvent("End application");
+
#endif
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
 

        

        
~
Class Sample
        ' イベントを記録
~
  ' デバッグ時のみイベントを記録したい
        LogEvent("End application")
+
  Shared Sub LogEvent(ByVal message As String)
+
    Console.WriteLine(message)
+
  End Sub
+

          
+
  Public Shared Sub Main()
+
    ' これでコンパイルエラーにはならなくなるが
+
#If DEBUG Then
+
    LogEvent("Start application")
+
#End If
 

        

        
~
    Console.WriteLine("Hello, world!")
    End Sub
 

        

        
~
    ' 呼び出しの数だけ#Ifを記述するのは面倒
End Module
+
#If DEBUG Then
+
    LogEvent("End application")
+
#End If
+
  End Sub
+
End Class
 
}}
}}
+
#tabpage-end
 

        

        
~
また、メソッド内のコードのみに適用した場合でも、リリースビルド時でもメソッドは呼び出されるので、無用なオーバーヘッドが生じます。
#prompt(デバッグビルド){{
-
Start application
-
Begin calculation.
-
Calculation was finished.
-
End application
-
Press any key to continue
-
}}
 

        

        
~
#tabpage(C#)
#prompt(リリースビルド){{
~
#code(cs){{
Begin calculation.
~
using System;
Calculation was finished.
~

          
Press any key to continue
+
class Sample
+
{
+
  // デバッグ時のみイベントを記録したい
+
  static void LogEvent(string message)
+
  {
+
#if DEBUG
+
    // これでコンパイルエラーにもならず、記述もシンプルになるが
+
    Console.WriteLine(message);
+
#endif
+
  }
+

          
+
  public static void Main()
+
  {
+
    // リリースビルドでもメソッド呼び出しは行われてしまう
+
    LogEvent("Start application");
+

          
+
    Console.WriteLine("Hello, world!");
+

          
+
    LogEvent("End application");
+
  }
+
}
 
}}
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
 

        

        
~
Class Sample
この結果を見てわかるとおり、その機能は一見#Ifディレクティブと同じですが、リリースビルドの時、つまりDebugが定義されていない場合はメソッドの呼び出しが無視されます。 また、Conditional属性を適用したメソッドでも当然#Ifディレクティブは機能します。 ここで、属性のコンストラクタに渡す文字列は定数名で、大文字小文字が区別されます。 この定数は自分で定義した定数(#Constで定義した定数)でもかまいませんが、 Conditional属性は定数が定義されていて、なおかつ0以外の値を持つときのみ有効になります。
~
  ' デバッグ時のみイベントを記録したい

          
~
  Shared Sub LogEvent(ByVal message As String)
さらに、Conditional属性を適用するメソッドは値を返さないSubメソッドに限られます。 もし何らかの値を返したい場合は、ByRefを使うなどする必要があります。
~
#If DEBUG

          
~
    ' これでコンパイルエラーにもならず、記述もシンプルになるが
#code(vb,戻り値が必要な場合の例){{
~
    Console.WriteLine(message)
Option Strict On
+
#End If
+
  End Sub
 

        

        
~
  Public Shared Sub Main()
Imports System.Diagnostics
~
    ' リリースビルドでもメソッド呼び出しは行われてしまう
Imports System.Threading
+
    LogEvent("Start application")
 

        

        
~
    Console.WriteLine("Hello, world!")
Module AttributesForDebugging
 

        

        
~
    LogEvent("End application")
    ' イベントを記録する
~
  End Sub
    <Conditional("Debug")> _
~
End Class
    Sub LogEvent(ByVal ev As String, ByRef dtm As DateTime)
+
}}
+
#tabpage-end
 

        

        
~
**Conditional属性
        dtm = DateTime.Now
+
この問題を解決するには&msdn(netfx,type,System.Diagnostics.ConditionalAttribute){Conditional属性};を使用することができます。 Conditional属性はメソッドに対して使用され、属性のコンストラクタに指定された条件付きコンパイル定数が定義されている場合に限りそのメソッドが呼び出されます。 条件付きコンパイル定数が定義されていない場合は呼び出されなくなります。 次のコードでその使用例を挙げます。
 

        

        
~
#tabpage(C#)
        Console.WriteLine(ev + " Time: " + dtm.ToString())
+
#code(cs){{
+
using System;
+
using System.Diagnostics
 

        

        
~
class Sample
    End Sub
+
{
+
  // デバッグ時のみイベントを記録したい
+
  [Conditional("DEBUG")]
+
  static void LogEvent(string message)
+
  {
+
    Console.WriteLine(message);
+
  }
 

        

        
~
  public static void Main()
    Sub Main()
+
  {
+
    LogEvent("Start application");
 

        

        
~
    Console.WriteLine("Hello, world!");
        ' この時刻は何らかの理由で利用されるものとする
-
        Dim dtm As DateTime
 

        

        
~
    LogEvent("End application");
        ' イベントを記録
~
  }
        LogEvent("Start application", dtm)
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+
Imports System.Diagnostics
 

        

        
~
Class Sample
        ' いくつものややこしい処理があるとする
~
  ' デバッグ時のみイベントを記録したい
        Thread.Sleep(3000)
+
  <Conditional("DEBUG")>
+
  Shared Sub LogEvent(ByVal message As String)
+
    Console.WriteLine(message)
+
  End Sub
 

        

        
~
  Public Shared Sub Main()
        ' イベントを記録
~
    LogEvent("Start application")
        LogEvent("End application", dtm)
 

        

        
~
    Console.WriteLine("Hello, world!")
    End Sub
 

        

        
~
    LogEvent("End application")
End Module
+
  End Sub
+
End Class
 
}}
}}
+
#tabpage-end
 

        

        
~
#prompt(デバッグビルドの場合){{
#prompt(デバッグビルド){{
~
Start application
Start application Time: 2003/03/22:15:14
~
Hello, world!
End application Time: 2003/03/22:15:17
~
End application
Press any key to continue
 
}}
}}
 

        

        
~
#prompt(リリースビルドの場合){{
#prompt(リリースビルド){{
~
Hello, world!
Press any key to continue
 
}}
}}
 

        

        
~
この結果を見てわかるとおり、その機能は一見#ifディレクティブと同じですが、リリースビルドの時、つまりシンボル'DEBUG'が定義されていない場合はメソッドの呼び出しが無視されます。 Conditional属性が適用されているメソッドの呼び出しは、コンパイル時に無視されるようになるため、呼び出し自体も起こりません。
さらに、Conditional属性を複数指定することもできます。 ただ、その場合は定義されていない定数があるか、その定数の値が0であるようなものが一つでもあると、そのメソッドに対するConditional属性は全て無効になります。 その例を挙げますが、実行結果は省略します。 この例では、たとえリリースビルドであってもOutputLogがTrueであれば必ずログが出力されます。
 

        

        
~
Conditional属性は単に条件によって呼び出しを無視するだけなので、Conditional属性を適用したメソッド内でも当然#ifディレクティブは使用可能で、Conditional属性が適用されていない場合と同様に機能します。
#code(vb){{
-
Option Strict On
-

          
-
#Const OutputLog = True
-

          
-
Imports System.Diagnostics
-
Imports System.Threading
-

          
-
Module AttributesForDebugging
 

        

        
~
***Conditional属性に指定する条件付きコンパイル定数
    ' イベントを記録する
~
Conditional属性に指定するシンボル名は、大文字小文字が区別されます。 このシンボルは次のいずれかの方法で定義出来ます。 Conditional属性は、これらのシンボルが定義されている場合のみ有効になります。
    <Conditional("Debug"), Conditional("OutputLog")> _
~
-ソースコード中のディレクティブ (#define/#undefや#Constによる定義)
    Sub LogEvent(ByVal ev As String, ByRef dtm As DateTime)
+
-コンパイラのコマンドラインオプション (/defineオプションによる定義)
+
-シェルの環境変数
 

        

        
~
***Conditional属性を適用できるメソッド
        dtm = DateTime.Now
+
また、Conditional属性を適用するメソッドは値を返さないメソッド(void型/Subプロシージャ)に限られます。 Conditional属性でメソッド呼び出しが無視された場合、戻り値がどうなるか定義できないためです。
 

        

        
~
もしどうしても値を返したい場合は、returnの変わりに引数をref/ByRefにするなどの方法をとる必要があります(C#ではoutパラメータを使うことも出来ません)。 戻り値を返すメソッドや、引数にoutパラメータのあるメソッドに対してConditional属性を適用しようとするとコンパイルエラーとなります。
        Console.WriteLine(ev + " Time: " + dtm.ToString())
 

        

        
~
#tabpage(C#)
    End Sub
+
#code(cs){{
+
using System;
+
using System.Diagnostics;
 

        

        
~
class Sample
    Sub Main()
+
{
+
  // デバッグ時のみイベントを記録したい
+
  [Conditional("DEBUG")]
+
  static void LogEvent(string message, ref DateTime dateTime)
+
  {
+
    Console.WriteLine(message);
 

        

        
~
    dateTime = DateTime.Now;
        ' この時刻は何らかの理由で利用されるものとする
~
    //return DateTime.Now;
        Dim dtm As DateTime
+
  }
 

        

        
~
  public static void Main()
        ' イベントを記録
~
  {
        LogEvent("Start application", dtm)
+
    DateTime dt;
 

        

        
~
    LogEvent("Start application", ref dt);
        ' いくつものややこしい処理があるとする
-
        Thread.Sleep(3000)
 

        

        
~
    Console.WriteLine("Hello, world! {0}", dt);
        ' イベントを記録
-
        LogEvent("End application", dtm)
 

        

        
~
    LogEvent("End application", ref dt);
    End Sub
~
  }

          
~
}
End Module
 
}}
}}
~
#tabpage(VB)

          
-
*Obsolete属性
-
Obsolete属性は古くなったメソッドなどに対して適用し、それが使用された場合に警告を発することができる属性です。
-

          
 
#code(vb){{
#code(vb){{
~
Imports System
Option Strict On
-

          
 
Imports System.Diagnostics
Imports System.Diagnostics
 

        

        
~
Class Sample
Module AttributesForDebugging
~
  ' デバッグ時のみイベントを記録したい

          
~
  <Conditional("DEBUG")> _
    <Obsolete("このバージョンは使用しないで下さい。 NewMethodを使用して下さい。", True)> _
~
  Shared Sub LogEvent(ByVal message As String, ByRef dateTime As DateTime)
    Sub OlderMethod(ByVal msg As String)
+
    Console.WriteLine(message)
 

        

        
~
    dateTime = DateTime.Now
        Debug.WriteLine("より古いバージョン: " + msg)
+
    ' Return DateTime.Now
+
  End Sub
 

        

        
~
  Public Shared Sub Main()
    End Sub
+
    Dim dt As DateTime
 

        

        
~
    LogEvent("Start application", dt)
    <Obsolete("将来サポートされなくなる可能性があります。 NewMethodを使用して下さい。")> _
-
    Sub OldMethod(ByVal msg As String)
 

        

        
~
    Console.WriteLine("Hello, world! {0}", dt)
        Console.WriteLine("古いバージョン: " + msg)
 

        

        
~
    LogEvent("End application", dt)
    End Sub
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
 

        

        
~
#prompt(デバッグビルドの場合){{
    Sub NewMethod(ByVal msg As String)
+
Start application
+
Hello, world! 2003/03/30 22:15:14
+
End application
+
}}
 

        

        
~
#prompt(リリースビルドの場合){{
        Console.WriteLine("新しいバージョン: " + msg)
+
Hello, world! 0001/01/01 0:00:00
+
}}
 

        

        
~
***複数のConditional属性
    End Sub
+
Conditional属性を複数指定することもできます。 複数指定した場合は、定義されているシンボルが1つでもある場合、そのメソッドに対する呼び出しは行われます。 つまり、OR条件でConditional属性が有効になります。 次の例では、シンボル'DEBUG'もしくはシンボル'TEST'が定義されている場合に、ログが出力されるようになります。 コメントアウトしてあるシンボルの定義を有効にすると結果が変わります。
 

        

        
~
#tabpage(C#)
    Sub Main()
+
#code(cs){{
+
//#define TEST
 

        

        
~
using System;
        OlderMethod("メソッドを呼び出します")
+
using System.Diagnostics;
 

        

        
~
class Sample
        OldMethod("メソッドを呼び出します")
+
{
+
  [Conditional("DEBUG"), Conditional("TEST")]
+
  static void LogEvent(string message)
+
  {
+
    Console.WriteLine(message);
+
  }
 

        

        
~
  public static void Main()
        NewMethod("メソッドを呼び出します")
+
  {
+
    LogEvent("Start application");
 

        

        
~
    Console.WriteLine("Hello, world!");
    End Sub
 

        

        
~
    LogEvent("End application");
End Module
+
  }
+
}
 
}}
}}
~
#tabpage(VB)

          
-
このコードをコーディングしているときには次のような警告が表示されます。
-
#ref(0.png,表示された警告)
-

          
-
さらに、コンパイル時にはこれと同じ警告が発せられます。 このように、Obsolete属性は互換性のために残してあるような仕様の古くなったコードに対して、使用しないように促す警告を発することができます。 この例ではメソッドに対してObsolete属性を適用していますが、アセンブリ、モジュール、パラメータ、戻り値以外の要素に対して適用することができます。 また、警告ではなくエラーとして扱う場合には次のようにします。
-

          
 
#code(vb){{
#code(vb){{
~
' #Const TEST = True
Option Strict On
 

        

        
+
Imports System
 
Imports System.Diagnostics
Imports System.Diagnostics
 

        

        
~
Class Sample
Module AttributesForDebugging
~
  <Conditional("DEBUG"), Conditional("TEST")> _

          
~
  Shared Sub LogEvent(ByVal message As String)
    <Obsolete("このバージョンは使用しないで下さい。 NewMethodを使用して下さい。", True)> _
~
    Console.WriteLine(message)
    Sub OlderMethod(ByVal msg As String)
~
  End Sub

          
~

          
        Debug.WriteLine("より古いバージョン: " + msg)
~
  Public Shared Sub Main()

          
~
    LogEvent("Start application")
    End Sub
~

          

          
~
    Console.WriteLine("Hello, world!")
    <Obsolete("将来サポートされなくなる可能性があります。 NewMethodを使用して下さい。")> _
~

          
    Sub OldMethod(ByVal msg As String)
~
    LogEvent("End application")

          
~
  End Sub
        Console.WriteLine("古いバージョン: " + msg)
~
End Class

          
-
    End Sub
-

          
-
    Sub NewMethod(ByVal msg As String)
-

          
-
        Console.WriteLine("新しいバージョン: " + msg)
-

          
-
    End Sub
-

          
-
    Sub Main()
-

          
-
        OlderMethod("メソッドを呼び出します")
-

          
-
        OldMethod("メソッドを呼び出します")
-

          
-
        NewMethod("メソッドを呼び出します")
-

          
-
    End Sub
-

          
-
End Module
 
}}
}}
+
#tabpage-end
 

        

        
~
*Obsolete属性
つまり、属性のコンストラクタの二つ目の引数にTrueを指定します。 こうすると完全にエラーとなり、この要素を使用したコードをコンパイルしようとするとコンパイルエラーが発生します。
~
&msdn(netfx,type,System.ObsoleteAttribute){Obsolete属性};は古くなったメソッドやクラスなどに対して適用し、それが使用された場合に警告を発することができる属性です。 例えば、互換性維持のために古い実装を残しておきたいが将来的には廃止する予定のため、新たに書くコードでは新しい実装を使うよう警告を出す場合にこの属性が使われます。 .NET Frameworkのクラスライブラリでも、&msdn(netfx,method,System.Uri.MakeRelative){Uri.MakeRelativeメソッド};や&msdn(netfx,method,System.IO.Path.InvalidPathChars){Path.InvalidPathCharsフィールド};などはObsolete属性が適用されています。 (参照:&msdn(netfx,id,ee461503){互換性のために残されている型};、&msdn(netfx,id,ee471421){互換性のために残されているメンバー};)

          
-
#ref(1.png,表示されたエラー・警告)
 

        

        
~
もちろん、自分のコードにObsolete属性を適用することも出来ます。
さらに、構造体に対してObsolete属性を適用した例を次に挙げます。
 

        

        
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+
using System.Diagnostics;
+

          
+
class Sample
+
{
+
  [Obsolete("将来サポートされなくなる可能性があります。 NewMethodを使用して下さい。")]
+
  static void OldMethod(string message)
+
  {
+
    Console.WriteLine("古いバージョン: " + message);
+
  }
+

          
+
  static void NewMethod(string message)
+
  {
+
    Console.WriteLine("新しいバージョン: " + message);
+
  }
+

          
+
  public static void Main()
+
  {
+
    OldMethod("メソッドを呼び出します");
+

          
+
    NewMethod("メソッドを呼び出します");
+
  }
+
}
+
}}
+
#tabpage(VB)
 
#code(vb){{
#code(vb){{
~
Imports System
Option Strict On
-

          
 
Imports System.Diagnostics
Imports System.Diagnostics
 

        

        
~
Class Sample
Module AttributesForDebugging
~
  <Obsolete("将来サポートされなくなる可能性があります。 NewMethodを使用して下さい。")> _

          
~
  Shared Sub OldMethod(ByVal message As String)
    <Obsolete("System.Drawing.Colorを使用して下さい。")> _
~
    Console.WriteLine("古いバージョン: " + message)
    Structure ARGB
~
  End Sub

          
~

          
        Dim R As Byte
~
  Shared Sub NewMethod(ByVal message As String)
        Dim G As Byte
~
    Console.WriteLine("新しいバージョン: " + message)
        Dim B As Byte
~
  End Sub
        Dim A As Byte
~

          

          
~
  Public Shared Sub Main()
    End Structure
~
    OldMethod("メソッドを呼び出します")

          
~

          
    Sub Main()
~
    NewMethod("メソッドを呼び出します")

          
~
  End Sub
        Dim col1 As ARGB
~
End Class

          
~
}}
        Dim col2 As System.Drawing.Color
~
#tabpage-end

          
~

          
    End Sub
~
このコードをコーディングしているときには、IDE上に次のような警告が表示されます。

          
~
#ref(0.png,Sub OldMethod&#x28;message As String&#x29;は旧形式です: '将来サポートされなくなる可能性があります。 NewMethodを使用して下さい。')
End Module
+

          
+
コンパイル時にもこれと同じ警告が発せられます。 このように、Obsolete属性は互換性のために残してあるような仕様の古くなったコードに対して、使用しないように促す警告を発することができます。
+

          
+
また、Obsolete属性が適用されているメソッドを使用した場合に、警告ではなくコンパイルエラーとしたい場合には次のようにします。
+

          
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+
using System.Diagnostics;
+

          
+
class Sample
+
{
+
  [Obsolete("このバージョンは使用しないで下さい。 NewMethodを使用して下さい。", true)]
+
  static void OlderMethod(string message)
+
  {
+
    Console.WriteLine("より古いバージョン: " + message);
+
  }
+

          
+
  [Obsolete("将来サポートされなくなる可能性があります。 NewMethodを使用して下さい。")]
+
  static void OldMethod(string message)
+
  {
+
    Console.WriteLine("古いバージョン: " + message);
+
  }
+

          
+
  static void NewMethod(string message)
+
  {
+
    Console.WriteLine("新しいバージョン: " + message);
+
  }
+

          
+
  public static void Main()
+
  {
+
    OlderMethod("メソッドを呼び出します");
+

          
+
    OldMethod("メソッドを呼び出します");
+

          
+
    NewMethod("メソッドを呼び出します");
+
  }
+
}
 
}}
}}
~
#tabpage(VB)

          
-
#ref(2.png,表示される警告)
-

          
-
*DebuggerStepThrough属性
-
DebuggerStepThrough属性は構造体、クラス、メソッド、コンストラクタに対して適用し、適用された要素はデバッグ対象からはずれます。
-

          
 
#code(vb){{
#code(vb){{
~
Imports System
Option Strict On
-

          
 
Imports System.Diagnostics
Imports System.Diagnostics
 

        

        
~
Class Sample
Module AttributesForDebugging
~
  <Obsolete("このバージョンは使用しないで下さい。 NewMethodを使用して下さい。", True)> _

          
~
  Shared Sub OlderMethod(ByVal message As String)
    <DebuggerStepThrough()> _
~
    Console.WriteLine("より古いバージョン: " + message)
    Sub DebuggedMethod()
~
  End Sub

          
~

          
        ' デバッグ済みのコードがあるとする
~
  <Obsolete("将来サポートされなくなる可能性があります。 NewMethodを使用して下さい。")> _

          
~
  Shared Sub OldMethod(ByVal message As String)
        Console.WriteLine("処理が完了しました。")
~
    Console.WriteLine("古いバージョン: " + message)

          
~
  End Sub
    End Sub
~

          

          
~
  Shared Sub NewMethod(ByVal message As String)

          
~
    Console.WriteLine("新しいバージョン: " + message)
    Sub Main()
+
  End Sub
+

          
+
  Public Shared Sub Main()
+
    OlderMethod("メソッドを呼び出します")
+

          
+
    OldMethod("メソッドを呼び出します")
+

          
+
    NewMethod("メソッドを呼び出します")
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
OlderMethodで指定しているObsolete属性のように、コンストラクタの二つ目の引数にtrueを指定します。 このようにすると、このメソッドを使用しようとした場合は警告ではなくエラーとなり、このメソッドを使用したコードをコンパイルしようとするとコンパイルエラーが発生します。
+

          
+
#ref(1.png,IDE上に表示されたコンパイルエラー・警告)
+

          
+
ここまでの例ではメソッドに対してObsolete属性を適用してきましたが、クラスや構造体など(アセンブリ、モジュール、パラメータ、戻り値以外)に対して適用することもできます。 次の例は、構造体に対してObsolete属性を適用したものです。
+

          
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+
using System.Diagnostics;
+
using System.Drawing;
+

          
+
[Obsolete("System.Drawing.Colorを使用して下さい。")]
+
struct ARGB
+
{
+
  byte R;
+
  byte G;
+
  byte B;
+
  byte A;
+
}
+

          
+
class Sample
+
{
+
  public static void Main()
+
  {
+
    ARGB col1;
+
    Color col2;
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+
Imports System.Diagnostics
+
Imports System.Drawing
 

        

        
~
<Obsolete("System.Drawing.Colorを使用して下さい。")> _
        DebuggedMethod()
+
Structure ARGB
+
  Dim R As Byte
+
  Dim G As Byte
+
  Dim B As Byte
+
  Dim A As Byte
+
End Structure
+

          
+
Class Sample
+
  Public Shared Sub Main()
+
    Dim col1 As ARGB
+
    Dim col2 As Color
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
IDE上には次のような警告が表示されます。
+

          
+
#ref(2.png,ARGBは旧形式です: 'System.Drawing.Colorを使用して下さい。')
+

          
+
*IDEでのデバッグ操作に関わる属性
+

          
+

          
+

          
+
**DebuggerDisplay属性
+
&msdn(netfx,type,System.Diagnostics.DebuggerDisplayAttribute){DebuggerDisplay属性};は、変数をポイントしたときやウォッチウィンドウに表示されるテキストのフォーマットを指定するための属性です。
+

          
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+
using System.Diagnostics;
+

          
+
[DebuggerDisplay("RGB({R}, {G}, {B}), Alpha = {A}")]
+
struct ARGB
+
{
+
  byte R;
+
  byte G;
+
  byte B;
+
  byte A;
+

          
+
  public ARGB(byte r, byte g, byte b, byte a)
+
  {
+
    R = r;
+
    G = g;
+
    B = b;
+
    A = a;
+
  }
+

          
+
  public override string ToString()
+
  {
+
    return string.Format("R={0}, G={1}, B={2}, A={3}", R, G, B, A);
+
  }
+
}
+

          
+
class Sample
+
{
+
  static void Main()
+
  {
+
    ARGB col = new ARGB(0xff, 0x80, 0x00, 0x80);
+

          
+
    Console.WriteLine(col);
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+
Imports System.Diagnostics
 

        

        
~
<DebuggerDisplay("RGB({R}, {G}, {B}), Alpha = {A}")> _
    End Sub
+
Structure ARGB
+
  Dim R As Byte
+
  Dim G As Byte
+
  Dim B As Byte
+
  Dim A As Byte
+

          
+
  Public Sub New(ByVal r As Byte, ByVal g As Byte, ByVal b As Byte, ByVal a As Byte)
+
    Me.R = r
+
    Me.G = g
+
    Me.B = b
+
    Me.A = a
+
  End Sub
+

          
+
  Public Overrides Function ToString() As String
+
    Return String.Format("R={0}, G={1}, B={2}, A={3}", R, G, B, A)
+
  End Function
+
End Structure
+

          
+
Class Sample
+
  Public Shared Sub Main()
+
    Dim col As New ARGB(&hff, &h80, &h00, &h80)
+

          
+
    Console.WriteLine(col)
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
DebuggerDisplay属性が適用されると、デバッグ時にはそのコンストラクタに指定されたフォーマットで表示されるようになります。 このコードをビルドし、デバッグモードでステップ実行しながら変数colをポイントしたりウォッチウィンドウに追加すると、"RGB(255, 128, 0), Alpha = 128"のように表示されるようになります。 オーバーライドしたToString()が返す値とは異なる点に注目してください。
+

          
+
この例では構造体にDebuggerDisplay属性を適用していますが、クラスや列挙型、プロパティ・フィールドなどにも適用することができます。 また、この例では単純にフィールドの値を表示しているだけですが、簡単な条件式やメソッド呼び出しの結果を表示させるようにすることも出来ます。 詳しくは&msdn(netfx,id,x810d419){DebuggerDisplay 属性の使用};を参照してください。
+

          
+
**DebuggerStepThrough属性
+
&msdn(netfx,type,System.Diagnostics.DebuggerStepThroughAttribute){DebuggerStepThrough属性};は構造体、クラス、メソッド、コンストラクタに対して適用し、適用された要素はデバッグ時にステップ・インしなくなります。 例えば次のコードのDebuggerMethod()メソッドの中にブレークポイントを設定し、メソッドにステップ・インしようとしても、ステップ・インせずにメソッドの処理が終了し、メソッド呼び出しのあった次の行に進むようになります。
+

          
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+
using System.Diagnostics;
+

          
+
class Sample
+
{
+
  [DebuggerStepThrough]
+
  static void DebuggedMethod()
+
  {
+
    // デバッグ済みのコードがあるとする
+
    Console.WriteLine("処理が完了しました。");
+
  }
+

          
+
  public static void Main()
+
  {
+
    DebuggedMethod();
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+
Imports System.Diagnostics
 

        

        
~
Class Sample
End Module
+
  <DebuggerStepThrough> _
+
  Shared Sub DebuggedMethod()
+
    ' デバッグ済みのコードがあるとする
+
    Console.WriteLine("処理が完了しました。")
+
  End Sub
+

          
+
  Public Shared Sub Main()
+
    DebuggedMethod()
+
  End Sub
+
End Class
 
}}
}}
+
#tabpage-end
 

        

        
~
このように、バグの原因を探すために一行ずつ実行するときにデバッグ済みのコードも一行ずつ実行しなければならないのは非常に不便なので、デバッグが完了したメソッドやクラスにDebuggerStepThrough属性を適用することでデバッグ対象から外すことができ、効率的なデバッグが行えるようになります。
例えばこのコードの19行目にブレークポイントを設定し、メソッドにステップ・インしようとしても、ステップ・インせずにメソッドの処理が終了し、メソッド呼び出しのあった次の行に進んでしまいます。 このように、バグの原因を探すために一行ずつ実行するときにデバッグ済みのコードも一行ずつ実行しなければならないのは非常に不便なので、デバッグが完了したメソッドやクラスにDebuggerStepThrough属性を適用することでデバッグ対象から外すことができ、効率的なデバッグが行えるようになります。
 

        

        
 
ただし、一行ずつ実行することはできなくても、ブレークポイントを設定することができ、実行時には設定されたところで停止します。 また言うまでもありませんが、リリースビルドではDebuggerStepThrough属性を指定していようといなかろうと無関係です。
ただし、一行ずつ実行することはできなくても、ブレークポイントを設定することができ、実行時には設定されたところで停止します。 また言うまでもありませんが、リリースビルドではDebuggerStepThrough属性を指定していようといなかろうと無関係です。
 

        

        
~
**DebuggerHidden属性
*DebuggerHidden属性
~
&msdn(netfx,type,System.Diagnostics.DebuggerHiddenAttribute){DebuggerHidden属性};はDebuggerStepThrough属性と似た属性で、メソッド及びプロパティに対して使用します。 ただDebuggerStepThrough属性とは異なり、使用されたメソッドの中にブレークポイントを設定することはできても、デバッグ時にはその要素が'''見えなく'''なり、ブレークポイントが到達してもそのメソッドの外部(=呼び出し元)で停止します。
DebuggerHidden属性は、基本的にはDebuggerStepThrough属性と同様のもので、メソッド及びプロパティに対して使用します。 ただDebuggerStepThrough属性とは異なり使用されたメソッドの中ではブレークポイントを設定することができません。 厳密に言うと、ブレークポイント自体の設定はできるもののそのメソッドの外部で停止します。
 

        

        
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+
using System.Diagnostics;
+

          
+
class Sample
+
{
+
  [DebuggerHidden]
+
  static void DebuggedMethod()
+
  {
+
    // デバッグ済みのコードがあるとする
+
    Console.WriteLine("処理が完了しました。");
+
  }
+

          
+
  public static void Main()
+
  {
+
    DebuggedMethod();
+
  }
+
}
+
}}
+
#tabpage(VB)
 
#code(vb){{
#code(vb){{
~
Imports System
Option Strict On
-

          
 
Imports System.Diagnostics
Imports System.Diagnostics
 

        

        
~
Class Sample
Module AttributesForDebugging
~
  <DebuggerHidden> _

          
~
  Shared Sub DebuggedMethod()
    <DebuggerHidden()> _
~
    ' デバッグ済みのコードがあるとする
    Sub DebuggedMethod()
~
    Console.WriteLine("処理が完了しました。")

          
~
  End Sub
        ' デバッグ済みのコードがあるとする
~

          

          
~
  Public Shared Sub Main()
        Console.WriteLine("処理が完了しました。")
~
    DebuggedMethod()

          
~
  End Sub
    End Sub
~
End Class

          
~
}}
    Sub Main()
~
#tabpage-end

          
~

          
        DebuggedMethod()
~
**DebuggerNonUserCode属性

          
~
&msdn(netfx,type,System.Diagnostics.DebuggerNonUserCodeAttribute){DebuggerNonUserCode属性};はDebuggerStepThrough属性とDebuggerHidden属性を組み合わせたような属性です。 この属性が適用されると、デバッグ時にはその要素が見えなくなり、またステップ・インしなくなります。
    End Sub
~
通常は、IDEが自動的に生成した非ユーザーコード・デザイナーのコードなどをデバッガ上から隠すためにこの属性が用いられますが、独自のコードにもこの属性を適用することは出来ます。

          
~

          
End Module
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+
using System.Diagnostics;
+

          
+
class Sample
+
{
+
  [DebuggerNonUserCode]
+
  static void HiddenProcess()
+
  {
+
    // デバッガで表示する必要のないコードがあるとする
+
    Console.WriteLine("処理が完了しました。");
+
  }
+

          
+
  public static void Main()
+
  {
+
    HiddenProcess();
+
  }
+
}
 
}}
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+
Imports System.Diagnostics
 

        

        
~
Class Sample
DebuggerHiddenAttributeという名前が表すとおり、デバッグ時にはその要素が「見えなくなる」と解釈するといいと思います。
+
  <DebuggerNonUserCode> _
+
  Shared Sub HiddenProcess()
+
    ' デバッガで表示する必要のないコードがあるとする
+
    Console.WriteLine("処理が完了しました。")
+
  End Sub
+

          
+
  Public Shared Sub Main()
+
    HiddenProcess()
+
  End Sub
+
End Class
+
}}
+
#tabpage-end