2010-11-03T00:33:52の更新内容

programming/netfx/classlibrary/0_howtocreate/index.wiki.txt

current previous
1,615 1,215
~
${smdncms:title,クラスライブラリの作成}
${smdncms:title,クラスライブラリの作り方}
+
${smdncms:keywords,作り方,作成方法,アセンブリ,プロジェクト}
 
${smdncms:meta,toc-amazonlivelink-keyword,books-jp,.net framework}
${smdncms:meta,toc-amazonlivelink-keyword,books-jp,.net framework}
+
${pragma,parser-options,non-limited-list}
 

        

        
 
#navi(..)
#navi(..)
 

        

        
~
ここでは.NET Frameworkでのクラスライブラリについてと、その作り方について解説します。 名前空間やクラスの基本など、クラスライブラリを作成する上で必要になってくる言語の要素については解説しないので、必要に応じて以下のページなどを参照してください。
ここでは、そもそもクラスライブラリとは何かということはおいておいて、その作り方と要点だけを見ていきます。 今回はC#でクラスライブラリを作り、それをVB.NETで利用するという方法をとりますが、もちろんその逆もできますし、C#のクラスライブラリをC#で使ったりVB.NET同士で作ったり使ったりすることもできます。 (個人的にはVB.NETでクラスライブラリを作る気にはなりませんが…) さらに、詳しいことはあとで説明しますが、.NET FrameworkのCLS(共通言語仕様)に準拠した言語同士ならこのクラスライブラリを使用することができます。
-
#googleadunit
-
----
 

        

        
~
-関連するページ
*ソリューションの準備
~
--[[programming/netfx/attributes]]
それでは早速クラスライブラリを作りましょうといきたいところですが、その前にソリューションの準備を行います。 基本的にはダイアログに従って進めていくだけなので、特別な手順はほんの少しです。
+
--[[programming/netfx2/overview/assemblyembedresource]]
+
--[[programming/tips/get_assembly_version_info]]
+
--Visual Basic
+
---[[programming/vb.net/basics]]
+
----[[programming/vb.net/basics/00_console]]
+
----[[programming/vb.net/basics/08_module]]
+
----[[programming/vb.net/basics/10_namespace]]
+
----[[programming/vb.net/basics/11_accessibility]]
+
----[[programming/vb.net/basics/12_class]]
+

          
+
#hr
+

          
+
*.NET Frameworkにおけるライブラリの位置づけ
+
クラスライブラリの作り方を見ていく前に、.NET Frameworkにおけるクラスライブラリの位置づけと関連する概念についてざっと触れておきます。
+

          
+
.NET Frameworkにおいては、実行可能ファイル(.exe)やライブラリ(.dll)はアセンブリという構成単位で扱われます。 アセンブリには、アプリケーションプロセスを起動するためのエントリポイント(Mainメソッド)を含む実行可能なアセンブリと、エントリポイントは持たずアプリケーションや他のライブラリから使用されるクラスを含むライブラリアセンブリがあります。 .NET Frameworkにおけるクラスライブラリは、ライブラリアセンブリとしてビルドされることになります。
+

          
+
各アセンブリには、アセンブリの実行コードの他、アセンブリが外部に公開する型の情報やアセンブリ自身が依存する他のアセンブリについての参照情報などの''メタデータ''が含まれています。 そのため、ビルド・実行時にはヘッダーファイルやlibファイルといったファイルは不要で、アセンブリ本体から必要な情報が取得されます。 また、必要に応じてアセンブリにリソースを含めることも出来ます。
+

          
+
アセンブリについての詳細は、以下のドキュメントを参照してください。
+
-&msdn(netfx,id,hk5f40ct){共通言語ランタイムのアセンブリ};
+
--&msdn(netfx,id,k3677y81){アセンブリの概要};
+
--&msdn(netfx,id,6h38y9z9){アセンブリの利点};
+
--&msdn(netfx,id,zst29sk2){アセンブリの内容};
+

          
+
.NET Frameworkのクラスライブラリは、C#やVB.NETなどの言語で記述・使用することが出来ます。 例えばC#で記述したライブラリアセンブリを参照してVB.NETで使用するといったことが可能です。 この点について詳しくは[[programming/netfx/classlibrary/1_interoperability]]で解説します。
+

          
+
*ライブラリアセンブリの作成
+
それではクラスライブラリの作り方について見ていきます。 ここでは、コマンドラインを使った場合と、IDEを使った場合の二つの方法でのクラスライブラリの作り方について紹介します。 なお、以下の手順では次のような構成のクラスライブラリを作成することにします。
+

          
+
:アセンブリ名|Smdn.Utils (Smdn.Utils.dll)
+
:ルート名前空間|Smdn.Utils
+
:アセンブリに含める型|以下の2つのクラスを含める
+
::StringUtilsクラス|文字列操作に関する以下のユーティリティメソッドを提供する
+
:::Reverseメソッド|引数で指定された文字列の並びを逆転した文字列を返すメソッド
+
:::SplitBySpaceメソッド|引数で指定された文字列を全角および半角の空白で区切った配列で返すメソッド
+
::OrderByLengthComparerクラス|Comparer<string>を継承し、文字列の長さに従って並べ替えるようにオーバーライドしたクラス
 

        

        
~
**&aname(samplecodes){ソースファイルの内容};
**クラスライブラリ用のプロジェクトを用意する
~
以下の手順では、一つのソースファイルに一つのクラスを含めるようにします。 個々のソースファイルに記述する内容は、以下の内容を使います。
まず、何もソリューションが開かれていない状態で、「ファイル」メニューから「新規作成」「プロジェクト」をクリックします。 現れた「新しいプロジェクト」ダイアログで「Visual C# プロジェクト」の「クラスライブラリ」を選択します。 これでないと当然クラスライブラリを作ることはできません。 後は、場所とプロジェクト名に適当な名前を入れてやります。 ここでは「ClassLibrary」としました。
-
#ref(00.png,「新しいプロジェクト」ダイアログ)
 

        

        
~
#tabpage(C#)
後はOKボタンを押してやればプロジェクトが作成されるはずです。 一部名前を変えていますが、プロジェクトの構成は図のような感じになります。 これ以降このプロジェクトはクラスライブラリとしてビルドされます。
~
#code(cs,StringUtils.cs){{
#ref(01.png,作成したプロジェクトの構成)
+
using System;
+

          
+
namespace Smdn.Utils
+
{
+
  public static class StringUtils
+
  {
+
    public static string Reverse(string str)
+
    {
+
      if (str == null) throw new ArgumentNullException("str");
+

          
+
      char[] chars = str.ToCharArray();
+

          
+
      Array.Reverse(chars);
 

        

        
~
      return new string(chars);
**デバッグ用のプロジェクトを用意する
~
    }
つぎに、デバッグ用のプロジェクトを先ほどのソリューションに追加します。 クラスライブラリにはエントリーポイントはなく、実行のしようがないので、作成したクラスライブラリを読み込みデバッグするためのプロジェクトを作成します。
-
#ref(02.png,「新しいプロジェクト」ダイアログ)
 

        

        
~
    public static string[] SplitBySpace(string str)
先ほどと同じ要領で「新しいプロジェクト」ダイアログを開き、「Visual Basic プロジェクト」の「コンソール アプリケーション」を選択し、プロジェクト名と場所を指定します。  ここでは「SampleProject」としました。 このとき、必ず「ソリューションに追加」にチェックを入れてください。 そうしないと新しいソリューションのプロジェクトとして作成されてしまいます。 ちなみに、作成するクラスライブラリの用途によっては「Windows アプリケーション」でデバッグすべき場合等もあるので用途に応じて適切なものを選択してください。
+
    {
+
      if (str == null) throw new ArgumentNullException("str");
 

        

        
~
      return str.Split(new char[] {' ', ' '}, StringSplitOptions.None);
後はOKボタンを押してやるだけです。 プロジェクトが作成されると、ソリューション構成は図のようになります。
~
    }
#ref(03.png,ソリューション構成)
+
  }
+
}
+
}}
 

        

        
~
#code(cs,OrderByLengthComparer.cs){{
**ソリューションのプロパティを設定する
~
using System;
続いて、作成されたソリューションのプロパティ設定をします。 まず、「ソリューション エクスプローラ」でソリューションを右クリックし、ソリューションのプロパティページを表示させます。
+
using System.Collections.Generic;
 

        

        
~
namespace Smdn.Utils
まず、「共通プロパティ」の「スタートアッププロジェクト」をクリックします。 そこで、「シングル スタートアップ プロジェクト」を選択し、プロジェクトをデバッグ用のプロジェクトに設定します。
~
{
#ref(04.png,スタートアッププロジェクト)
+
  public class OrderByLengthComparer : Comparer<string>
+
  {
+
    public override int Compare(string x, string y)
+
    {
+
      return x.Length - y.Length;
+
    }
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb,StringUtils.vb){{
+
Imports System
 

        

        
~
Namespace Smdn.Utils
次に、「プロジェクト依存関係」をクリックします。 このタブでは、プロジェクト同士の依存関係を設定します。 この例ではデバッグ用のプロジェクトはクラスライブラリのプロジェクトに依存するので、「プロジェクト」にSampleProjectを設定し、表示されている依存先の「ClassLibrary」にチェックを入れます。
~
  Public Module StringUtils
#ref(05.png,プロジェクト依存関係)
+
    Public Function Reverse(ByVal str As String) As String
+
      If str Is Nothing Then Throw New ArgumentNullException("str")
 

        

        
~
      Dim chars() As Char = str.ToCharArray()
一応、ここまででソリューションのプロパティの設定は完了しているのですが、クラスライブラリのコーディングがある程度完成していて、デバッグを行うたびにライブラリのビルドを行う必要がなくなった場合などには、「構成プロパティ」の「構成」タブで、プロジェクトをビルドするかしないかを設定することができます。
-
#ref(06.png,構成プロパティ)
 

        

        
~
      Array.Reverse(chars)
**デバッグプロジェクトの参照にクラスライブラリを追加する
-
ここまでで一見すべての作業が終わったように見えますが、重要な事が残っています。 クラスライブラリを使うためには、「参照の追加」でデバッグプロジェクトにクラスライブラリを追加しなければなりません。 「ソリューションエクスプローラ」でデバッグプロジェクトの「参照設定」を右クリックして、表示される「参照の追加」ダイアログの「プロジェクト」タブから、クラスライブラリのプロジェクトを追加します。
-
#ref(07.png,参照の追加)
 

        

        
~
      Return New String(chars)
後はOKを押してやるだけです。 デバッグ用のプロジェクトにクラスライブラリへの参照が追加され、ソリューションエクスプローラでは図のようなソリューション構成になるはずです。
~
    End Function
#ref(08.png,ソリューション構成)
 

        

        
~
    Public Function SplitBySpace(ByVal str As String) As String()
**実際にインポートしてみる
~
      If str Is Nothing Then Throw New ArgumentNullException("str")
このようにして追加されたクラスライブラリをデバッグプロジェクトから呼び出すには、普通の.NET Framework クラスライブラリを呼び出すときと同様に行います。 たとえば、名前空間をインポートしようとすると、図のように選択候補が表示され自作のクラスライブラリを参照できることがわかります。
-
#ref(09.png,名前空間のインポート)
 

        

        
~
      Return str.Split(New Char() {" "c, " "c}, StringSplitOptions.None)
そして、このときのソリューション構成を見てみると図のようになっているはずです。 ちなみに気づいた方もいるかもしれませんが、スタートアッププロジェクトはボールド体で表示されます。 スタートアッププロジェクトを設定する前のソリューション構成の図と見比べてみてください。
~
    End Function
#ref(10.png,準備が整ったソリューションの構成)
+
  End Module
+
End Namespace
+
}}
 

        

        
~
#code(vb,OrderByLengthComparer.vb){{
*クラスライブラリのコーディング
~
Imports System
それでは準備が整ったところで早速クラスライブラリのコーディングを開始してみたいと思います。
+
Imports System.Collections.Generic
+

          
+
Namespace Smdn.Utils
+
  Public Class OrderByLengthComparer
+
    Inherits Comparer(Of String)
+

          
+
    Public Overrides Function Compare(ByVal x As String, ByVal y As String) As Integer
+
      Return x.Length - y.Length
+
    End Function
+
  End Class
+
End Namespace
+
}}
+
#tabpage-end
 

        

        
~
また、クラスライブラリを作成した後、動作テストをするために以下のコードを使います。
**クラスライブラリのコーディング
-
今回の例ではクラスライブラリのほうはC#で記述しますが、エントリーポイントがない以外はほとんどいつものコーディングと変わりありません。 クラスライブラリが提供すべきクラスをコーディングするだけでよいのです。 まず手始めに次のようなクラスをいくつか作成しました。
 

        

        
~
#tabpage(C#)
#code(cs,クラスライブラリのコーディング){{
+
#code(cs,test.cs){{
 
using System;
using System;
+
using Smdn.Utils;
 

        

        
~
class Test
namespace ClassLibrary
 
{
{
~
  static void Main()
    // メッセージクラス
~
  {
    public class MessageClass
~
    string s1 = "The quick brown fox jumps over the lazy dog";
    {
~
    string s2 = "かごめかごめ かごのなかのとりは いついつでやる";
        public readonly string Message;
 

        

        
~
    // StringUtils.Reverseメソッドのテスト
        public MessageClass( string message )
~
    Console.WriteLine("[StringUtils.Reverse]");
        {
~
    Console.WriteLine(StringUtils.Reverse(s1));
            Message = message;
~
    Console.WriteLine(StringUtils.Reverse(s2));
        }
-

          
-
        public void ShowMessage()
-
        {
-
            Console.WriteLine( Message );
-
        }
-
    }
 

        

        
~
    // StringUtils.SplitBySpaceメソッドのテスト
    // 自作の0除算例外クラス
~
    string[] w1 = StringUtils.SplitBySpace(s1);
    public class OriginalDivideByZeroException : Exception
~
    string[] w2 = StringUtils.SplitBySpace(s2);
    {
~

          
        public OriginalDivideByZeroException( string message ) : base( message )
~
    Console.WriteLine("[StringUtils.SplitBySpace]");
        {
-
        }
-
    }
 

        

        
~
    Print(w1);
    // 計算クラス (全メソッドはstatic)
~
    Print(w2);
    public class CalculationClass
+

          
+
    // OrderByLengthComparerクラスのテスト
+
    Console.WriteLine("[OrderByLengthComparer]");
+

          
+
    OrderByLengthComparer comparer = new OrderByLengthComparer();
+

          
+
    Array.Sort(w1, comparer);
+
    Array.Sort(w2, comparer);
+

          
+
    Print(w1);
+
    Print(w2);
+
  }
+

          
+
  static void Print(string[] words)
+
  {
+
    foreach (string word in words)
 
    {
    {
~
      Console.WriteLine(word);
        public static int Add( int x, int y )
-
        {
-
            return x + y;
-
        }
-

          
-
        public static int Subtract( int x, int y )
-
        {
-
            return x - y;
-
        }
-

          
-
        public static int Multiply( int x, int y )
-
        {
-
            return x * y;
-
        }
-

          
-
        public static int Divide( int x, int y )
-
        {
-
            if ( y == 0 ) throw new OriginalDivideByZeroException( "0で除算しました。" );
-
            return x / y;
-
        }
 
    }
    }
~
  }

          
 
}
}
 
}}
}}
+
#tabpage(VB)
+
#code(vb,test.vb){{
+
Imports System
+
Imports Smdn.Utils
+

          
+
Class Test
+
  Shared Sub Main()
+
    Dim s1 As String = "The quick brown fox jumps over the lazy dog"
+
    Dim s2 As String = "かごめかごめ かごのなかのとりは いついつでやる"
+

          
+
    ' StringUtils.Reverseメソッドのテスト
+
    Console.WriteLine("[StringUtils.Reverse]")
+
    Console.WriteLine(StringUtils.Reverse(s1))
+
    Console.WriteLine(StringUtils.Reverse(s2))
+

          
+
    ' StringUtils.SplitBySpaceメソッドのテスト
+
    Dim w1() As String = StringUtils.SplitBySpace(s1)
+
    Dim w2() As String = StringUtils.SplitBySpace(s2)
+

          
+
    Console.WriteLine("[StringUtils.SplitBySpace]")
+

          
+
    Print(w1)
+
    Print(w2)
+

          
+
    ' OrderByLengthComparerクラスのテスト
+
    Console.WriteLine("[OrderByLengthComparer]")
+

          
+
    Dim comparer As New OrderByLengthComparer()
+

          
+
    Array.Sort(w1, comparer)
+
    Array.Sort(w2, comparer)
+

          
+
    Print(w1)
+
    Print(w2)
+
  End Sub
+

          
+
  Shared Sub Print(ByVal words() As String)
+
    For Each word As String In words
+
      Console.WriteLine(word)
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
 

        

        
~
*&aname(build_commandline){コマンドラインでの作成手順};
これらのクラスの概要を説明すると、まずMessageClassはコンストラクタで指定された文字列を、ShowMessage()メソッドによりコンソールに出力するクラスです。 本来ならクラスライブラリでConsole.WriteLine()は呼び出すべきではありませんが、あくまでサンプルなので使っています。 続いて、OriginalDivideByZeroExceptionクラスは、独自に定義した0による除算の例外です。 .NET FrameworkにもSystem.DivideByZeroExceptionというクラスがありますが、これもまたサンプルとしてあえて自作しました。 最後に、すべて静的メソッドであるCalculationClassは、簡単な計算を行うための静的メソッドを提供するためのクラスです。  Divide()メソッドで0による除算を行おうとした場合には先ほどのOriginalDivideByZeroException例外をスルーさせます。 それでは、これらのクラスを実際に使用してみます。
+
ここではクラスライブラリの作成に必要最低限の手順を理解するためにコマンドラインでのビルド手順を解説します。 コマンドラインでの操作に興味がない場合は、[[IDEを使ったビルド手順>#build_ide]]まで読み飛ばしてください。
 

        

        
~
**csc, vbcを使った例
**デバッグ用プロジェクト側のコーディング
~
ここではMicrosoftのC#コンパイラであるcsc.exeとVisual Basicコンパイラであるvbc.exeを使ってクラスライブラリを作成する手順を見ていきます。 クラスライブラリをビルドする上で最低限必要なコンパイルオプションは以下のとおりです。
デバッグ用プロジェクト、クラスライブラリを使う方のコーディングはクラスライブラリを作るよりも単純です。 なぜなら、.NET Frameworkのクラスを利用する場合と全く変わらないからです。 説明するよりも実際にコードと実行結果を見た方が早いです。
 

        

        
~
:/target:library|出力するアセンブリの種類をライブラリアセンブリにする
#code(vb,デバッグ用プロジェクト側のコーディング){{
~
:/out:Smdn.Utils.dll|出力ファイル名を"Smdn.Utils.dll"にする
Imports ClassLibrary
+
:(入力ファイル)|ビルドするライブラリアセンブリのソースファイル([[先に挙げたライブラリのソースファイル2つ>#samplecodes]])を指定する
 

        

        
~
vbcでは、必要に応じて/optionstrict+や/optionexplicit+などのオプションも指定します。
Module SampleProject
 

        

        
~
#tabpage(csc)
    Sub Main()
+
#prompt{{
+
D:\classlibrary\cs>csc /target:library /out:Smdn.Utils.dll StringUtils.cs OrderByLengthComparer.cs
+
Microsoft (R) Visual C# 2008 Compiler version 3.5.30729.1
+
for Microsoft (R) .NET Framework version 3.5
+
Copyright (C) Microsoft Corporation. All rights reserved.
 

        

        
-
        ' MessageClassのインスタンスを作成
-
        Dim msg As New MessageClass("クラスライブラリからのメッセージ")
 

        

        
~
D:\classlibrary\cs>dir /B
        msg.ShowMessage()
+
OrderByLengthComparer.cs
+
Smdn.Utils.dll
+
StringUtils.cs
+
}}
+
#tabpage(vbc)
+
#prompt{{
+
D:\classlibrary\vb>vbc /optionstrict+ /optionexplicit+ /target:library /out:Smdn.Utils.dll StringUtils.vb OrderByLengthComparer.vb
+
Microsoft (R) Visual Basic Compiler Version 9.0.30729.1
+
Copyright (C) Microsoft Corporation. All rights reserved.
 

        

        
-
        ' CalculationClassクラスの共有メソッドを呼び出し
-
        Console.WriteLine(CalculationClass.Add(1, 2))
-
        Console.WriteLine(CalculationClass.Subtract(5, 3))
-
        Console.WriteLine(CalculationClass.Multiply(3, 4))
-
        Console.WriteLine(CalculationClass.Divide(6, 2))
 

        

        
~
D:\classlibrary\vb>dir /B
        ' 自作例外OriginalDivideByZeroExceptionの捕獲
~
OrderByLengthComparer.vb
        Try
+
Smdn.Utils.dll
+
StringUtils.vb
+
}}
+
#tabpage-end
 

        

        
~
ビルドが完了すると、上記のとおりライブラリアセンブリSmdn.Utils.dllが作成されているはずです。 なお、環境変数PATHを設定していない場合はcsc、vbcを実行出来ないので、以下のコマンドでパスを通しておく必要があります。
            Console.WriteLine(CalculationClass.Divide(1, 0))
 

        

        
~
#prompt{{
        Catch ex As OriginalDivideByZeroException
+
set PATH=C:\WINDOWS\Microsoft.NET\Framework\v3.5;%PATH%
+
}}
 

        

        
~
**mcs, vbncを使った例
            Console.WriteLine(ex.Message)
+
ここではMonoのC#コンパイラであるmcs(gmcs)とVisual Basicコンパイラであるvbncを使ってクラスライブラリを作成する手順を見ていきます。 基本的にはcsc・vbcと同じで、クラスライブラリをビルドする上で最低限必要なコンパイルオプションは以下のとおりです。
 

        

        
~
:/target:library|出力するアセンブリの種類をライブラリアセンブリにする
        End Try
+
:/out:Smdn.Utils.dll|出力ファイル名を"Smdn.Utils.dll"にする
+
:(入力ファイル)|ビルドするライブラリアセンブリのソースファイル([[先に挙げたライブラリのソースファイル2つ>#samplecodes]])を指定する
 

        

        
~
vbncでは、必要に応じて/optionstrict+や/optionexplicit+などのオプションも指定します。
    End Sub
 

        

        
~
#tabpage(gmcs)
End Module
+
#prompt{{
+
[~/classlibrary/cs]$ gmcs /target:library /out:Smdn.Utils.dll StringUtils.cs OrderByLengthComparer.cs
+
[~/classlibrary/cs]$ ls -1
+
OrderByLengthComparer.cs
+
Smdn.Utils.dll
+
StringUtils.cs
 
}}
}}
+
#tabpage(vbnc)
+
#prompt{{
+
[~/classlibrary/vb]$ vbnc /optionstrict+ /optionexplicit+ /target:library /out:Smdn.Utils.dll StringUtils.vb OrderByLengthComparer.vb
+
Visual Basic.Net Compiler version 0.0.0.5943
+
Copyright (C) 2004-2010 Rolf Bjarne Kvinge. All rights reserved.
 

        

        
~

          
#prompt(実行結果){{
~
Assembly 'Smdn.Utils, Version=0.0, Culture=neutral, PublicKeyToken=null' saved successfully to '/home/smdn/classlibrary/vb/Smdn.Utils.dll'.
クラスライブラリからのメッセージ
~
Compilation successful
3
~
Compilation took 00:00:00.9039670
2
~
[~/classlibrary/vb]$ ls -1
12
~
OrderByLengthComparer.vb
3
~
Smdn.Utils.dll
0で除算しました。
~
StringUtils.vb
Press any key to cotinue
 
}}
}}
+
#tabpage-end
+

          
+
ビルドが完了すると、上記のとおりライブラリアセンブリSmdn.Utils.dllが作成されているはずです。
+

          
+
**ビルドしたライブラリのテスト
+
ここまでの手順で作成したクラスライブラリを使って実行可能ファイルを作り、動作テストをしてみます。 ライブラリアセンブリを使って実行可能ファイルをビルドする上で最低限必要なコンパイルオプションは以下のとおりです(すべてcsc・vbcおよびmcs・vbncで共通するオプションです)。
 

        

        
~
:/r:Smdn.Utils.dll|ライブラリアセンブリ(作成したSmdn.Utils.dll)を参照するように指定する
先ほど述べたように、普通にクラスを利用する場合と全く変わりません。 動作についても説明するまでもないと思います。
+
:(入力ファイル)|実行可能ファイルのソースファイル([[先に挙げたテスト用のソースファイル>#samplecodes]])を指定する
 

        

        
~
vbc・vbncでは、必要に応じて/optionstrict+や/optionexplicit+などのオプションも指定します。
**出力結果とbinフォルダの中身
-
ビルド後の出力ペインへのビルド出力を見ると次のようになっていました。
 

        

        
+
#tabpage(csc)
 
#prompt{{
#prompt{{
~
D:\classlibrary\cs>csc /r:Smdn.Utils.dll test.cs
------ ビルド開始 : プロジェクト : ClassLibrary, 構成 : Debug .NET ------
+
Microsoft (R) Visual C# 2008 Compiler version 3.5.30729.1
+
for Microsoft (R) .NET Framework version 3.5
+
Copyright (C) Microsoft Corporation. All rights reserved.
 

        

        
-
リソースを準備しています...
-
参照を更新しています...
-
メイン コンパイルを実行しています...
 

        

        
~
D:\classlibrary\cs>dir /B
プロジェクトは最新のものです
~
OrderByLengthComparer.cs
サテライト アセンブリをビルドしています...
+
Smdn.Utils.dll
+
StringUtils.cs
+
test.cs
+
test.exe
+
}}
+
#tabpage(vbc)
+
#prompt{{
+
D:\classlibrary\vb>vbc /optionstrict+ /optionexplicit+ /r:Smdn.Utils.dll test.vb
+
Microsoft (R) Visual Basic Compiler Version 9.0.30729.1
+
Copyright (C) Microsoft Corporation. All rights reserved.
 

        

        
 

        

        
+
D:\classlibrary\vb>dir /B
+
OrderByLengthComparer.vb
+
Smdn.Utils.dll
+
StringUtils.vb
+
test.exe
+
test.vb
+
}}
+
#tabpage(gmcs)
+
#prompt{{
+
[~/classlibrary/cs]$ gmcs /r:Smdn.Utils.dll test.cs
+
[~/classlibrary/cs]$ ls -1
+
OrderByLengthComparer.cs
+
Smdn.Utils.dll
+
StringUtils.cs
+
test.cs
+
test.exe
+
}}
+
#tabpage(vbnc)
+
#prompt{{
+
[~/classlibrary/vb]$ vbnc /optionstrict+ /optionexplicit+ /r:Smdn.Utils.dll test.vb 
+
Visual Basic.Net Compiler version 0.0.0.5943
+
Copyright (C) 2004-2010 Rolf Bjarne Kvinge. All rights reserved.
 

        

        
-
------ ビルド開始 : プロジェクト : SampleProject, 構成 : Debug .NET ------
 

        

        
~
Assembly 'test, Version=0.0, Culture=neutral, PublicKeyToken=null' saved successfully to '/home/smdn/classlibrary/vb/test.exe'.
リソースを準備しています...
~
Compilation successful
参照を更新しています...
~
Compilation took 00:00:00.9068700
メイン コンパイルを実行しています...
~
[~/classlibrary/vb]$ ls -1
サテライト アセンブリをビルドしています...
+
OrderByLengthComparer.vb
+
Smdn.Utils.dll
+
StringUtils.vb
+
test.exe
+
test.vb
+
}}
+
#tabpage-end
+

          
+
ビルドが完了したら、上記のとおり実行可能ファイルtest.exeが作成されているはずです。 このファイルを実行すると、次のようなテスト結果が得られるはずです。
+

          
+
#prompt{{
+
[StringUtils.Reverse]
+
god yzal eht revo spmuj xof nworb kciuq ehT
+
るやでついつい はりとのかなのごか めごかめごか
+
[StringUtils.SplitBySpace]
+
The
+
quick
+
brown
+
fox
+
jumps
+
over
+
the
+
lazy
+
dog
+
かごめかごめ
+
かごのなかのとりは
+
いついつでやる
+
[OrderByLengthComparer]
+
the
+
fox
+
The
+
dog
+
over
+
lazy
+
quick
+
brown
+
jumps
+
かごめかごめ
+
いついつでやる
+
かごのなかのとりは
+
}}
+

          
+
*&aname(build_ide){IDEでの作成手順と設定};
+
ここではIDEを使ってクラスライブラリを作成する手順と、作成したクラスライブラリを使用する手順について解説します。 ここで紹介する手順では、一つのソリューションにクラスライブラリ用のプロジェクトとテスト用のプロジェクトを含めることにします。 IDEを使わずコマンドラインで作成したい場合は[[コマンドラインを使ったビルド手順>#build_commandline]]を参照してください。
+

          
+
**Visual Studio 2008での例
+
***ソリューションとクラスライブラリ用のプロジェクトの作成
+
まずは、ソリューションとクラスライブラリ用のプロジェクトを作成します。 [ファイル]メニューから[新規作成]→[プロジェクト]を選択します。
+
#image(vs2008-00.png,60%,[ファイル]→[新規作成]→[プロジェクト]メニュー)
+

          
+
次に、種類の一覧で[Visual C#]や[Visual Basic]などクラスライブラリを作成する上で使用する言語を指定し、[クラス ライブラリ]を選択します。 [プロジェクト名]の欄にはクラスライブラリのプロジェクト名となる"Smdn.Utils"を指定します。 ここではソリューション名を"classlibrary"としました。
+
#image(vs2008-01.png,60%,ソリューションとプロジェクトの新規作成)
+

          
+
ここまでの手順で、ソリューションとクラスライブラリ用のプロジェクト、ソースファイルのテンプレートが自動生成されます。 以降、このプロジェクトをビルドすると、ライブラリアセンブリが作成されます。 テンプレートファイル(Class1)は不要なので削除しておくと良いでしょう。
+

          
+
続いて、クラスのコードを記述するためファイルを追加します。 プロジェクトファイルのコンテキストメニューから[追加]→[新しい項目]を選択します。
+
#image(vs2008-02.png,60%,[追加]→[新しい項目]メニュー)
+

          
+
種類の一覧で[コード]を指定し、[クラス]を選択します。 ファイル名にはクラス名+拡張子を指定します。
+
#image(vs2008-03.png,60%,ファイルの新規作成)
+

          
+
上記の手順を繰り返してクラス用のファイルを作成し、[[先に挙げたソースコード>#samplecodes]]を記述します。
+
#image(vs2008-04.png,75%,作成されたファイル)
 

        

        
+
***テスト用のプロジェクトの追加
+
次に、テスト用のプロジェクトを追加します。 クラスライブラリにはエントリーポイントがなく実行することはできないので、作成したクラスライブラリを使用してテスト用のコードを実行するためのプロジェクトを作成します。 ソリューションファイルのコンテキストメニューから[追加]→[新しいプロジェクト]を選択します。
+
#image(vs2008-05.png,60%,[追加]→[新しいプロジェクト]メニュー)
 

        

        
+
クラスライブラリ用のプロジェクトの場合とは異なり実行可能ファイルを作成する必要があるので、ここでは[コンソール アプリケーション]を選択します。 [プロジェクト名]の欄には実行可能ファイルのプロジェクト名となる名前を入力します。 ここではプロジェクト名を"test"としました。
+
#image(vs2008-06.png,60%,プロジェクトの新規作成)
 

        

        
~
ここまでの手順で、テスト用のプロジェクトがソリューションに追加され、ソースファイルのテンプレートが自動生成されます。 テスト用のプロジェクトが追加出来たら、クラスライブラリのアセンブリを参照するようにします。 プロジェクトの参照設定のコンテキストメニューから[参照の追加]を選択します。
---------------------- 終了 ----------------------
+
#image(vs2008-07.png,60%,[参照の追加]メニュー)
 

        

        
~
[プロジェクト]タブを選択し、クラスライブラリのプロジェクトである"Smdn.Utils"を選択します。
    ビルド : 2 正常終了、0 失敗、0 スキップ
+
#image(vs2008-08.png,60%,参照するプロジェクトの選択)
+

          
+
これで、テスト用プロジェクトのビルド時にクラスライブラリのプロジェクトを参照するようになります。
+
#image(vs2008-09.png,75%,編集したあとの参照設定)
+

          
+
ここまでの手順で、通常の.NET Frameworkのクラスと同様にusing文(Importsステートメント)で名前空間をインポートしてクラス名を記述すると、次のように入力候補に表示されるようになります。
+
#image(vs2008-10.png,75%,入力候補に表示されるライブラリのクラスとメソッド)
+

          
+
***スタートアッププロジェクトの設定
+
次に、ビルド後にテスト用プロジェクトを実行するようにソリューションの設定を変更します。 ソリューションのコンテキストメニューから[プロパティ]を選択します。
+
#image(vs2008-11.png,60%,[プロパティ]メニュー)
+

          
+
[共通プロパティ]→[スタートアップ プロジェクト]の項目を選択し、[シングル スタートアップ プロジェクト]を選択してテスト用のプロジェクトである"test"を指定します。
+
#image(vs2008-12.png,60%,スタートアップ プロジェクトの設定)
+

          
+
あとは[[先に挙げたテスト用のソースコード>#samplecodes]]を記述し、ビルド・実行するとコンソールが表示され次のような結果が得られるはずです。
+
#image(vs2008-13.png,75%,実行結果)
+

          
+
***出力結果とフォルダの内容
+
ビルド時の出力を確認すると、次のようになっていると思います。 ビルド時のオプションは[[コマンドラインでのビルド>#build_commandline]]で指定したのと同じようになっていることが分かると思います。 また、ビルドの順番を見ると分かるとおり、先にクラスライブラリがビルドされ、その次にデバッグ用のプロジェクトがビルドされています。
+
#prompt{{
+
------ すべてのリビルド開始: プロジェクト: Smdn.Utils, 構成: Debug Any CPU ------
+
C:\Windows\Microsoft.NET\Framework\v3.5\Csc.exe /noconfig /nowarn:1701,1702 /errorreport:prompt /warn:4 /define:DEBUG;TRACE /reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll" /reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.Data.DataSetExtensions.dll" /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Data.dll /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Xml.dll /reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.Xml.Linq.dll" /debug+ /debug:full /filealign:512 /optimize- /out:obj\Debug\Smdn.Utils.dll /target:library OrderByLengthComparer.cs Properties\AssemblyInfo.cs StringUtils.cs
+

          
+
コンパイルの完了 -- エラー 0、警告 0
+
Smdn.Utils -> e:\classlibrary\Smdn.Utils\bin\Debug\Smdn.Utils.dll
+
------ すべてのリビルド開始: プロジェクト: test, 構成: Debug Any CPU ------
+
C:\Windows\Microsoft.NET\Framework\v3.5\Csc.exe /noconfig /nowarn:1701,1702 /errorreport:prompt /warn:4 /define:DEBUG;TRACE /reference:e:\classlibrary\Smdn.Utils\bin\Debug\Smdn.Utils.dll /reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll" /reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.Data.DataSetExtensions.dll" /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Data.dll /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Xml.dll /reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.Xml.Linq.dll" /debug+ /debug:full /filealign:512 /optimize- /out:obj\Debug\test.exe /target:exe Program.cs Properties\AssemblyInfo.cs
+

          
+
コンパイルの完了 -- エラー 0、警告 0
+
test -> e:\classlibrary\test\bin\Debug\test.exe
+
========== すべてリビルド: 2 正常終了、0 失敗、0 スキップ ==========
 
}}
}}
 

        

        
~
なお、ビルド後に生成されたファイルは次のようになっていると思います。 クラスライブラリプロジェクトのフォルダのbin/Debug/にはdllファイルが、テスト用プロジェクトのフォルダのbin/Debug/にはクラスライブラリのdllファイルのコピーと実行可能ファイルのexeが生成されています。 以下はVisual C#での例ですが、Visual Basicでも同様の出力となります。
ビルドの順番を見ればわかるように、先にクラスライブラリがビルドされ、その次にデバッグ用のプロジェクトがビルドされています。 また、デバッグ出力は次のようになっています。
 

        

        
 
#prompt{{
#prompt{{
~
E:\CLASSLIBRARY
'DefaultDomain': 'd:\windows\microsoft.net\framework\v1.0.3705\mscorlib.dll' が読み込まれました。シンボルは読み込まれませんでした。
~
│  classlibrary.sln
'SampleProject': 'E:\Visual C# .NET Program\BaseTechnologies\SampleProject\bin\SampleProject.exe' が読み込まれました。シンボルが読み込まれました。
~
'SampleProject.exe': 'e:\visual c# .net program\basetechnologies\sampleproject\bin\classlibrary.dll' が読み込まれました。シンボルが読み込まれました。
~
├─Smdn.Utils
'SampleProject.exe': 'd:\windows\assembly\gac\microsoft.visualbasic\7.0.3300.0__b03f5f7f11d50a3a\microsoft.visualbasic.dll' が読み込まれました。シンボルは読み込まれませんでした。
~
│  │  OrderByLengthComparer.cs
プログラム '[3712] SampleProject.exe' はコード 0 (0x0) で終了しました。
+
│  │  Smdn.Utils.csproj
+
│  │  StringUtils.cs
+
│  │
+
│  ├─bin
+
│  │  └─Debug
+
│  │          Smdn.Utils.dll
+
│  │          Smdn.Utils.pdb
+
│  │
+
│  ├─obj
+
│  │  └─Debug
+
│  │      │  Smdn.Utils.csproj.FileListAbsolute.txt
+
│  │      │  Smdn.Utils.dll
+
│  │      │  Smdn.Utils.pdb
+
│  │      │
+
│  │      └─TempPE
+
│  └─Properties
+
│          AssemblyInfo.cs
+
+
└─test
+
    │  Program.cs
+
    │  test.csproj
+
+
    ├─bin
+
    │  └─Debug
+
    │          Smdn.Utils.dll
+
    │          Smdn.Utils.pdb
+
    │          test.exe
+
    │          test.pdb
+
    │          test.vshost.exe
+
    │          test.vshost.exe.manifest
+
+
    ├─obj
+
    │  └─Debug
+
    │      │  ResolveAssemblyReference.cache
+
    │      │  test.csproj.FileListAbsolute.txt
+
    │      │  test.exe
+
    │      │  test.pdb
+
    │      │
+
    │      └─TempPE
+
    └─Properties
+
            AssemblyInfo.cs
 
}}
}}
 

        

        
~
**MonoDevelop 2.4での例
3行目の出力の通り、SampleProject.exeは確実にClassLibrary.dllを読み込んでいます。 続いて、それぞれのbinフォルダに生成されたファイルを見てみることにします。
~
***ソリューションとクラスライブラリ用のプロジェクトの作成
#ref(11.png,ClassLibraryのbinフォルダ)
~
まずは、ソリューションとクラスライブラリ用のプロジェクトを作成します。 [ファイル]メニューから[新規]→[ソリューション]を選択します。
#ref(12.png,SampleProjectのbinフォルダ)
+
#image(md2.4-00.png,60%,[ファイル]→[新規]→[ソリューション]メニュー)
+

          
+
次に、種類の一覧で[C#]や[VBNet]などクラスライブラリを作成する上で使用する言語をを指定し、[ライブラリ]を選択します。 [名前]の欄にはクラスライブラリのプロジェクト名となる"Smdn.Utils"を指定します。 ここではソリューション名を"classlibrary"としました。
+
#image(md2.4-01.png,60%,ソリューションとプロジェクトの新規作成)
+

          
+
続いて、このプロジェクトで有効にする機能を指定するオプションが提示されますが、ここでは特に指定せずに次に進みます。
+
#image(md2.4-02.png,60%,プロジェクトの機能)
+

          
+
ここまでの手順で、ソリューションとクラスライブラリ用のプロジェクト、ソースファイルのテンプレートが自動生成されます。 以降、このプロジェクトをビルドすると、ライブラリアセンブリが作成されます。 AssemblyInfoはライブラリアセンブリの情報を記述するファイルなので残しておいても良いですが、テンプレートファイルは不要なので削除しておくと良いでしょう。
+
#image(md2.4-03.png,75%,作成されたソリューションとプロジェクト)
+

          
+
続いて、クラスのコードを記述するためファイルを追加します。 プロジェクトファイルのコンテキストメニューから[追加]→[新しいファイル]を選択します。
+
#image(md2.4-04.png,60%,[追加]→[新しいファイル]メニュー)
+

          
+
種類の一覧で[General]を指定し、[空のファイル]を選択します。 名前にはクラス名を指定します。 ファイル名はこの名前を元に自動的に設定されます。
+
#image(md2.4-05.png,60%,ファイルの新規作成)
+

          
+
上記の手順を繰り返してクラス用のファイルを作成し、[[先に挙げたソースコード>#samplecodes]]を記述します。
+
#image(md2.4-06.png,75%,作成されたファイル)
+

          
+
***テスト用のプロジェクトの追加
+
次に、テスト用のプロジェクトを追加します。 クラスライブラリにはエントリーポイントがなく実行することはできないので、作成したクラスライブラリを使用してテスト用のコードを実行するためのプロジェクトを作成します。 ソリューションファイルのコンテキストメニューから[追加]→[新しいプロジェクトを追加]を選択します。
+
#image(md2.4-07.png,60%,[追加]→[新しいプロジェクトを追加]メニュー)
+

          
+
クラスライブラリ用のプロジェクトの場合とは異なり実行可能ファイルを作成する必要があるので、ここでは[コンソール プロジェクト]を選択します。 [名前]の欄には実行可能ファイルのプロジェクト名となる名前を入力します。 ここではプロジェクト名を"test"としました。
+
#image(md2.4-08.png,60%,プロジェクトの新規作成)
+

          
+
ここまでの手順で、テスト用のプロジェクトがソリューションに追加され、ソースファイルのテンプレートが自動生成されます。 テスト用のプロジェクトが追加出来たら、クラスライブラリのアセンブリを参照するようにします。 プロジェクトの参照アセンブリのコンテキストメニューから[参照アセンブリの編集]を選択します。
+
#image(md2.4-09.png,60%,[参照アセンブリの編集]メニュー)
+

          
+
[プロジェクト]タブを選択し、クラスライブラリのプロジェクトである"Smdn.Utils"にチェックを入れます。
+
#image(md2.4-10.png,60%,参照するプロジェクトの選択)
+

          
+
これで、テスト用プロジェクトのビルド時にクラスライブラリのプロジェクトを参照するようになります。
+
#image(md2.4-11.png,75%,編集したあとの参照)
+

          
+
ここまでの手順で、通常の.NET Frameworkのクラスと同様にusing文(Importsステートメント)で名前空間をインポートしてクラス名を記述すると、次のように入力候補に表示されるようになります。
+
#image(md2.4-12.png,75%,入力候補に表示されるライブラリのクラスとメソッド)
+

          
+
***スタートアッププロジェクトの設定
+
次に、ビルド後にテスト用プロジェクトを実行するようにソリューションの設定を変更します。 ソリューションのコンテキストメニューから[オプション]を選択します。
+
#image(md2.4-13.png,60%,[オプション]メニュー)
+

          
+
[実行]→[スタートアップ プロジェクト]の項目を選択し、[単一のスタートアップ プロジェクト]を選択してテスト用のプロジェクトである"test"を指定します。
+
#image(md2.4-14.png,60%,スタートアップ プロジェクトの設定)
+

          
+
あとは[[先に挙げたテスト用のソースコード>#samplecodes]]を記述し、ビルド・実行するとコンソールが表示され次のような結果が得られるはずです。
+
#image(md2.4-15.png,75%,実行結果)
+

          
+
***出力結果とフォルダの内容
+
ビルド時の出力を確認すると、次のようになっていると思います。 ビルド時のオプションは[[コマンドラインでのビルド>#build_commandline]]で指定したのと同じようになっていることが分かると思います。 また、ビルドの順番を見ると分かるとおり、先にクラスライブラリがビルドされ、その次にデバッグ用のプロジェクトがビルドされています。
+
#prompt{{
+
ビルド中: classlibrary (Debug)
+

          
+
ソリューション classlibrary をビルド中
+

          
+
ビルド中: Smdn.Utils (Debug)
+

          
+
メインのコンパイルを実行中...
+
/opt/mono/master/bin/gmcs /noconfig "/out:/home/smdn/classlibrary/cs/classlibrary/Smdn.Utils/bin/Debug/Smdn.Utils.dll" "/r:/opt/mono/master/lib/mono/2.0/System.dll" /nologo /warn:4 /debug:+ /debug:full /optimize- /codepage:utf8 "/define:DEBUG"  /t:library "/home/smdn/classlibrary/cs/classlibrary/Smdn.Utils/AssemblyInfo.cs" "/home/smdn/classlibrary/cs/classlibrary/Smdn.Utils/StringUtils.cs" "/home/smdn/classlibrary/cs/classlibrary/Smdn.Utils/OrderByLengthComparer.cs" 
+
ビルド完了 -- エラー 0件, 警告 0件
+

          
+
ビルド中: test (Debug)
+

          
+
メインのコンパイルを実行中...
+
/opt/mono/master/bin/gmcs /noconfig "/out:/home/smdn/classlibrary/cs/classlibrary/test/bin/Debug/test.exe" "/r:/opt/mono/master/lib/mono/2.0/System.dll" "/r:/home/smdn/classlibrary/cs/classlibrary/Smdn.Utils/bin/Debug/Smdn.Utils.dll" /nologo /warn:4 /debug:+ /debug:full /optimize- /codepage:utf8 "/define:DEBUG"  /t:exe "/home/smdn/classlibrary/cs/classlibrary/test/Main.cs" "/home/smdn/classlibrary/cs/classlibrary/test/AssemblyInfo.cs" 
+
ビルド完了 -- エラー 0件, 警告 0件
+

          
+
---------------------- 完了 ----------------------
+

          
+
ビルド成功
+
}}
+

          
+
なお、ビルド後に生成されたファイルは次のようになっていると思います。 クラスライブラリプロジェクトのフォルダのbin/Debug/にはdllファイルが、テスト用プロジェクトのフォルダのbin/Debug/にはクラスライブラリのdllファイルのコピーと実行可能ファイルのexeが生成されています。 以下はC#での例ですが、VBでも同様の出力となります。
+

          
+
#image(md2.4-16.png,75%,ビルド後のフォルダの内容)
 

        

        
~
*クラスライブラリと言語
このように、クラスライブラリはdllとその中間ファイルだけが生成されているのに対し、それを使う方は実行可能ファイルとクラスライブラリが生成されています。 ただ実際には生成ではなく、ビルド前にクラスライブラリのコピーが作られたのです。
+
ここまでの手順ではクラスライブラリと実行可能ファイルの両方に同じ言語を使って記述してきましたが、別々の言語で記述して使用することも出来ます。 クラスライブラリが異なる言語からも使用されることを想定する場合には、言語間の相互運用性について留意しておく必要があります。 この点については次のページで詳しく解説します。
 

        

        
 
#navi(..)
#navi(..)
 

        

        

programming/netfx/classlibrary/1_interoperability/index.wiki.txt

current previous
1,455 1,440
~
${smdncms:title,言語間の相互運用性と共通言語仕様 (CLS)}
${smdncms:title,共通言語仕様CLS}
+
${smdncms:keywords,共通言語仕様,相互運用,CLS}
 
${smdncms:meta,toc-amazonlivelink-keyword,books-jp,.net framework}
${smdncms:meta,toc-amazonlivelink-keyword,books-jp,.net framework}
 

        

        
 
#navi(..)
#navi(..)
 

        

        
~
アセンブリの実行コードは共通中間言語(CIL, Common Intermediate Language)の形式で格納されます。 C#やVB.NETなど共通言語基盤(CLI, Common Language Infrastructure)の仕様に準拠する言語であればこれらのアセンブリを使用することができ、例えばC#で記述して作成したライブラリアセンブリを参照してVB.NETで使用するといったことが可能になります。
----
 

        

        
~
しかし、各言語がサポートする機能の差異により、ある言語で提供される機能が他の言語では使用できないといった場合が生じます。 ここでは、言語を跨って使用されるようなクラスライブラリを作成する上で重要となる言語間の相互運用性と共通言語仕様(CLS, Common Language Specification)について解説します。
*共通言語仕様(CLS)
-
**C#ではコンパイルできるのに、VB.NETでは使えないコード
-
さて、クラスライブラリを設計している段階で、CalclationClassはすべてuint型で計算さるように変更し、さらに新しくint型の値を保持し、+と-演算子をオーバーロードしそれにより計算を行うクラスを作成しました。 次のようなコードです。
-
#code(cs,クラスライブラリ){{
-
using System;
 

        

        
~
-関連するページ
namespace ClassLibrary
~
--[[programming/netfx/attributes]]
{
~
--[[programming/netfx/conversion/1_userdefined]]
    // 計算クラス (全メソッドはstatic、引数はuint)
~
--Visual Basic
    public class CalculationClass
~
---[[programming/vb8/diff_from_7x/00_unsigned_types]]
    {
~
---[[programming/vb8/diff_from_7x/01_operator_overload]]
        public static uint Add( uint x, uint y )
~
---[[programming/vb8/diff_from_7x/02_ctype_operator_overload]]
        {
~
-関連するMSDNのドキュメント
            return x + y;
~
--&msdn(netfx,id,730f1wy3){言語間の相互運用性};
        }
+
---&msdn(netfx,id,a2c7tshk){言語の相互運用性の概要};
+
---&msdn(netfx,id,12a7a7h3){共通言語仕様};
+
---&msdn(netfx,id,bhc3fa7f){CLS 準拠コードの記述};
+
--&msdn(netfx,id,zcx1eb1e){共通型システム};
+
--&msdn(netfx,id,ms229042){クラス ライブラリ開発のデザイン ガイドライン};
+
--&msdn(netfx,id,ms973862){Microsoft .NET Framework による言語の相互運用性の処理};
 

        

        
~
----
        public static uint Subtract( uint x, uint y )
-
        {
-
            return x - y;
-
        }
-

          
-
        public static uint Multiply( uint x, uint y )
-
        {
-
            return x * y;
-
        }
-

          
-
        public static uint Divide( uint x, uint y )
-
        {
-
            if ( y == 0 ) throw new OriginalDivideByZeroException( "0で除算しました。" );
-
            return x / y;
-
        }
-
    }
-

          
-
    // 値クラス
-
    public class ValueClass
-
    {
-
        private int m_Value;
 

        

        
~
*言語間の相互運用性
        public ValueClass( int val )
~
C#とVBでは、文法・構文はもちろん言語がサポートする機能にも違いがあります。 例えばポインタのサポートや大文字小文字の扱いなどです。 このような言語の違いに起因して発生しうる問題があります。 例として、C#でクラスを作成し、VBでそれを継承する場合を考えてみます。 まず、C#で次のようなクラスを作成します。
        {
-
            m_Value = val;
-
        }
 

        

        
~
#code(cs,lib.cs){{
        public static ValueClass operator+ ( ValueClass x,  ValueClass y )
~
using System;
        {
-
            return new ValueClass( x.m_Value + y.m_Value );
-
        }
 

        

        
~
namespace SampleLibrary {
        public static ValueClass operator- ( ValueClass x,  ValueClass y )
~
  public class Account
        {
~
  {
            return new ValueClass( x.m_Value - y.m_Value );
~
    private int id;
        }
 

        

        
+
    public void SetID(int newID)
+
    {
+
      setID(newID);
 
    }
    }
 

        

        
~
    protected virtual void setID(int newID)
    // 以下略
~
    {
}}
~
      if (newID < 0) throw new ArgumentOutOfRangeException();

          
-
続いて、デバッグ側では先ほどのコードに、ValueClassのデバッグ用に次のコードを追加しました。
-
#code(vb,デバッグプログラム){{
-
' 途中略
-

          
-
' ValueClassのインスタンスを作成
-
Dim a As New ValueClass(3)
-
Dim b As New ValueClass(2)
 

        

        
~
      id = newID;
Dim result As ValueClass = a - b
+
    }
 

        

        
~
    public override string ToString()
' 以下略
+
    {
+
      return string.Format("ID={0}", id);
+
    }
+
  }
+
}
 
}}
}}
 

        

        
~
C#で書かれたAccountクラスは、IDを設定するためのパブリックなメソッドであるSetIDと、値をチェックしてフィールドにセットするメソッドsetIDを持っています。 setIDは派生クラスでチェック処理を追加したり動作をカスタマイズ出来るようにオーバーライド可能にしています。 C#ではメソッド名など識別子の大文字小文字の違いが区別されるため、このコードは問題なくコンパイル出来ます。
ここまで完了した時点で、コンパイルしてみました。 ところが次のようなエラーが出てしまいました。
-

          
-
#prompt{{
-
------ ビルド開始 : プロジェクト : ClassLibrary, 構成 : Debug .NET ------
-

          
-
リソースを準備しています...
-
参照を更新しています...
-
メイン コンパイルを実行しています...
-

          
-
ビルドの完了 -- エラー 0、警告 0
-
サテライト アセンブリをビルドしています...
-

          
 

        

        
+
次に、VBでこのクラスを継承したクラスを作成することにします。
 

        

        
~
#code(vb,exe.vb){{
------ ビルド開始 : プロジェクト : SampleProject, 構成 : Debug .NET ------
+
Imports System
+
Imports SampleLibrary
 

        

        
~
Class AccountEx
リソースを準備しています...
~
  Inherits Account
参照を更新しています...
-
メイン コンパイルを実行しています...
-
E:\Visual C# .NET Program\BaseTechnologies\SampleProject\SampleProject.vb(13) : error BC30311: 型 'Integer' の値を 'System.UInt32' に変換できません。
-
E:\Visual C# .NET Program\BaseTechnologies\SampleProject\SampleProject.vb(13) : error BC30311: 型 'Integer' の値を 'System.UInt32' に変換できません。
-
E:\Visual C# .NET Program\BaseTechnologies\SampleProject\SampleProject.vb(14) : error BC30311: 型 'Integer' の値を 'System.UInt32' に変換できません。
-
E:\Visual C# .NET Program\BaseTechnologies\SampleProject\SampleProject.vb(14) : error BC30311: 型 'Integer' の値を 'System.UInt32' に変換できません。
-
E:\Visual C# .NET Program\BaseTechnologies\SampleProject\SampleProject.vb(15) : error BC30311: 型 'Integer' の値を 'System.UInt32' に変換できません。
-
E:\Visual C# .NET Program\BaseTechnologies\SampleProject\SampleProject.vb(15) : error BC30311: 型 'Integer' の値を 'System.UInt32' に変換できません。
-
E:\Visual C# .NET Program\BaseTechnologies\SampleProject\SampleProject.vb(16) : error BC30311: 型 'Integer' の値を 'System.UInt32' に変換できません。
-
E:\Visual C# .NET Program\BaseTechnologies\SampleProject\SampleProject.vb(16) : error BC30311: 型 'Integer' の値を 'System.UInt32' に変換できません。
-
E:\Visual C# .NET Program\BaseTechnologies\SampleProject\SampleProject.vb(21) : error BC30311: 型 'Integer' の値を 'System.UInt32' に変換できません。
-
E:\Visual C# .NET Program\BaseTechnologies\SampleProject\SampleProject.vb(21) : error BC30311: 型 'Integer' の値を 'System.UInt32' に変換できません。
-
E:\Visual C# .NET Program\BaseTechnologies\SampleProject\SampleProject.vb(33) : error BC30452: 演算子 '-' は、型 'ClassLibrary.ValueClass' および 'ClassLibrary.ValueClass' に対して定義されていません。
-
サテライト アセンブリをビルドしています...
 

        

        
+
  Protected Overrides Sub setID(ByVal newID As Integer)
+
    If newID = 0 Then Throw New ArgumentException("reserved ID")
 

        

        
~
    MyBase.setID(newID)

          
~
  End Sub
---------------------- 終了 ----------------------
~
End Class

          
-
    ビルド : 1 正常終了、1 失敗、0 スキップ
 
}}
}}
 

        

        
~
AccountExクラスでは、setIDメソッドをオーバーライドし、0番のIDの場合は予約済みのIDとして例外をスローするようにしています。 しかし、このコードをコンパイルしようとすると次のようなエラーが発生します。
このようにC#で作成したクラスライブラリ自体にはエラーがなく、正常にビルド完了しているのですが、一方でそれを利用する方では大量のエラーが発生しています。 このエラーを一つ一つ見ていくことにします。 といっても、実質的には二つだけのエラーのようです。
-

          
-
まず、一つ目の「型 'Integer' の値を 'System.UInt32' に変換できません。」ですが、VB.NETの方で引数に指定している値はVB.NETでいうInteger型のリテラル数値です。 対して、メソッドの引数としているのはC#でいうuint型です。 これらはそれぞれSystem.Int32とSystem.UInt32のエイリアスです。 C#側で引数はUInt32と指定されているのにも関わらず、VB.NETにはInt32型であるIntegerしか存在せず、UInt32への型変換が行えません。 そのためにこのようなエラーになります。 この場合、何らかの手段でUInt32に型変換してからメソッドに渡さなければなりません。
 

        

        
~
#prompt{{
続いて、「演算子 '-' は、型 'ClassLibrary.ValueClass' および 'ClassLibrary.ValueClass' に対して定義されていません。」については、C#でちゃんと演算子をオーバーロードしていて、エラーも発生していないので何も問題ないように感じます。 しかし、VB.NETでは演算子のオーバーロードはサポートされていないのでVB.NETでこれをしようとすると、とたんにエラーになります。 これを回避する方法はありません(実はありますが・・・それは後ほど)。
~
D:\clscompliant>csc /nologo /t:library /out:lib.dll lib\lib.cs

          
-
**共通言語仕様(CLS)に準拠したコード
-
このように、ある言語ではサポートされている言語仕様は他の言語ではサポートされていなかったり、ある言語でプリミティブ型として用意されている型は他の言語では存在しないということになると、クラスライブラリは何らかの仕様に従ってコーディングする必要性が出てきます。 そこにあらわれるのが「共通言語仕様 (CLS: Common Language Specification)」です。 これを簡単に説明すると、どの言語にも共通するような言語仕様を装備した仕様のことです。 簡単な図で表すと次の様な感じです。
-

          
-
#ref(13.png,CLSの概念)
 

        

        
~
D:\clscompliant>vbc /nologo /out:sample.exe /r:lib.dll exe\exe.vb
さらに、型に対して共通性を持たせるためにCLSの一部として「共通型システム (CTS: Common Type System)」も存在します。 System.Int32がC#ではint、VB.NETではIntegerとなっているのはこれによるものです。
+
D:\clscompliant\exe\exe.vb(7) : error BC31086: 'Overridable' として宣言されていないため、'Protected Overrides Sub setID(newID As Integer)' で 'Public Sub SetID(newID As Integer)' をオーバーライドすることはできません。
 

        

        
~
  Protected Overrides Sub setID(ByVal newID As Integer)
つまり、共通言語仕様・CLSに準拠したコードでクラスライブラリを記述すれば、他のCLSに準拠した言語において使用できる汎用的なクラスライブラリを作ることができます。 それでは、CLSはどのような仕様なのか、MSDNに記述されているCLSの仕様から、その抜粋を次の表にまとめてみました。
+
                          ~~~~~
+
D:\clscompliant\exe\exe.vb(7) : error BC30266: 異なるアクセス レベルが指定されているため、'Protected Overrides Sub setID(newID As Integer)' で 'Public Sub SetID(newID As Integer)' をオーバーライドすることはできません。
 

        

        
~
  Protected Overrides Sub setID(ByVal newID As Integer)
|*CLSの仕様(抜粋)
~
                          ~~~~~
|>|~機能|~説明|
~
}}
|~一般|グローバル メンバ|グローバルで静的なフィールドとメソッドは CLS 準拠ではありません。|
-
|~名前付け|文字および大文字と小文字の区別|2 つの識別子の区別には、大文字と小文字の相違は使用できません。|
-
|~|シグネチャ|型やメンバのシグネチャに含まれる戻り値の型およびパラメータの型はすべて CLS 準拠であることが必要です。|
-
|~型|プリミティブ型|.NET Framework クラス ライブラリには、コンパイラが使用するプリミティブ データ型に対応する型が含まれています。これらの型のうち、次の型が CLS 準拠です。Byte、Int16、Int32、Int64、Single、Double、Boolean、Char、Decimal、IntPtr、および String。|
-
|~|コンストラクタの呼び出し|コンストラクタは、継承されたインスタンス データにアクセスする前に、基本クラスのコンストラクタを呼び出す必要があります。|
-
|~型のメンバ|オーバーロード|インデックス付きのプロパティ、メソッド、コンストラクタはオーバーロードできます。フィールドとイベントはオーバーロードしません。 プロパティについては、型 (プロパティの getter メソッドの戻り値の型) によるオーバーロードはできませんが、インデックスの数や型が異なる場合のオーバーロードは可能です。 メソッドの場合は、それぞれのパラメータの数および型に基づくオーバーロードだけは可能です。&br;演算子のオーバーロードは CLS 準拠にはなりません。ただし、CLS には、Add() などの有用な名前を付け、メタデータのビットを設定する方法についてのガイドラインが用意されています。演算子のオーバーロードをサポートするコンパイラはこのガイドラインに準拠することが推奨されますが、必須ではありません。|
-
|~|変換演算子|op_Implicit または op_Explicit のいずれかがその戻り値の型でオーバーロードされた場合は、その代わりとなる変換方法を提供する必要があります。|
-
|~ポインタ型|ポインタ|ポインタ型および関数ポインタ型は CLS 準拠ではありません。|
-
|~クラス型|継承|CLS 準拠のクラスは、CLS 準拠のクラスから継承する必要があります。System.Object は CLS 準拠です。|
-
|~配列|要素型|配列の要素は、CLS 準拠の型であることが必要です。|
-
|~|次元|配列の次元数は、0 より大きい固定数であることが必要です。|
-
|~|範囲|配列の次元の下限値は 0 にする必要があります。|
 

        

        
~
VBでは識別子の大文字小文字の違いは区別されないため、プロテクトメソッドであるAccount.setIDとパブリックメソッドであるAccount.SetIDが区別されず、AccountExではパブリックメソッドであるAccount.SetIDをオーバーライドしようとしてこのようなエラーとなっています。
これより詳しくはMSDNの「言語間の相互運用性」→「言語の相互運用性の概要」→「共通言語仕様の概要」のページに載っていますので、これを参考にして下さい。
 

        

        
~
上記のような大文字小文字の違いの場合は別名のメソッドを提供することで解決することは出来ます。 しかし、極端な例になりますが、先のsetIDメソッドの機能をポインタを使ってしか操作できないように提供した場合は、VBではポインタをサポートしていないためメソッドをオーバーライドすることも呼び出すことも出来なくなってしまいます。
それでは早速CLSに準拠して先ほどのソースコードを書き直してみます。
 

        

        
~
#code(cs,lib.cs){{
#code(cs,CLS準拠コードに書き換えたクラスライブラリ){{
 
using System;
using System;
 

        

        
~
namespace SampleLibrary {
namespace ClassLibrary
~
  public class Account
{
~
  {
    // 計算クラス (全メソッドはstatic、引数はlong)
~
    private int id;
    public class CalculationClass
-
    {
-
        public static long Add( long x, long y )
-
        {
-
            return x + y;
-
        }
-

          
-
        public static long Subtract( long x, long y )
-
        {
-
            return x - y;
-
        }
-

          
-
        public static long Multiply( long x, long y )
-
        {
-
            return x * y;
-
        }
 

        

        
~
    public void SetID(int newID)
        public static long Divide( long x, long y )
~
    {
        {
~
      unsafe
            if ( y == 0 ) throw new OriginalDivideByZeroException( "0で除算しました。" );
~
      {
            return x / y;
~
        InternalSetID(&newID);
        }
+
      }
 
    }
    }
 

        

        
~
    protected virtual unsafe void InternalSetID(int* ptrNewID)
    // 値クラス
-
    public class ValueClass
 
    {
    {
~
      if (*ptrNewID < 0) throw new ArgumentOutOfRangeException();
        private int m_Value;
-

          
-
        public ValueClass( int val )
-
        {
-
            m_Value = val;
-
        }
-

          
-
        // 加算演算子
-
        public static ValueClass operator+ ( ValueClass x,  ValueClass y )
-
        {
-
            return new ValueClass( x.m_Value + y.m_Value );
-
        }
-
        
-
        // メソッドによる代替
-
        public ValueClass Add( ValueClass val )
-
        {
-
            return new ValueClass( this.m_Value + val.m_Value );
-
        }
-

          
-
        // 減算演算子
-
        public static ValueClass operator- ( ValueClass x,  ValueClass y )
-
        {
-
            return new ValueClass( x.m_Value - y.m_Value );
-
        }
-

          
-
        // メソッドによる代替
-
        public ValueClass Subtract( ValueClass val )
-
        {
-
            return new ValueClass( this.m_Value - val.m_Value );
-
        }
-

          
-
        // ToString()をオーバーライド
-
        public override string ToString()
-
        {
-
            return m_Value.ToString();
-
        }
 

        

        
+
      id = *ptrNewID;
 
    }
    }
 

        

        
~
    public override string ToString()

          
~
    {
    // 以下略
+
      return string.Format("ID={0}", id);
+
    }
+
  }
+
}
 
}}
}}
-
まず、CalculationClassにおけるuintの部分は、uintの範囲を確実にカバーできるlong型で代用しました。 続いて、 ValueClassの演算子のオーバーロードはVB.NETなどでは使えないので、代替メソッドにより代用します。 同時に、C#同士や演算子のオーバーロードをサポートした他の言語での使用を想定して、演算子オーバーロードは残しておきます。 ちなみに、これらの処理はCLS準拠のガイドラインに従ったものです。
 

        

        
~
#prompt{{
続いて、デバッグ側も仕様の変更に伴い次のように変えました。
+
D:\clscompliant>csc /nologo /unsafe /t:library /out:lib.dll lib\lib.cs
 

        

        
~
D:\clscompliant>vbc /nologo /out:sample.exe /r:lib.dll exe\exe.vb
#code(vb,CLS準拠のクラスライブラリを使用するコード){{
~
D:\clscompliant\exe\exe.vb(7) : error BC30284: sub 'InternalSetID' はベース class の sub をオーバーライドしないため、'Overrides' として宣言することはできません。
Imports ClassLibrary
 

        

        
~
  Protected Overrides Sub InternalSetID(ByVal newID As Integer)
Module SampleProject
+
                          ~~~~~~~~~~~~~
+
D:\clscompliant\exe\exe.vb(10) : error BC30657: 'InternalSetID' には、サポートされていない戻り値の型か、またはサポートされていないパラメータ型が指定されています。
 

        

        
~
    MyBase.InternalSetID(newID)
    Sub Main()
+
    ~~~~~~~~~~~~~~~~~~~~
+
}}
 

        

        
~
このように、言語によって使用できる機能や仕様の違いが存在するため、ある言語でしか使えない機能を使って記述したライブラリは、他の言語では使えないという相互運用上の問題が発生します。
        ' MessageClassのインスタンスを作成
-
        Dim msg As New MessageClass("クラスライブラリからのメッセージ")
 

        

        
~
*共通言語仕様
        msg.ShowMessage()
+
このような問題を回避するためには、代替となる機能を提供するするか、どの言語でもサポートされているような機能のみを使うようにする必要があります。 .NET Frameworkでは、どの言語でも共通して用意されているべき基本的な機能を定めた共通言語仕様(CLS, Common Language Specification)というものが定められています。 共通言語仕様と個々の言語の仕様の関係を簡単な図で表すと次のようになります。
 

        

        
~
#ref(cls.png,CLSと個々の言語仕様との関係)
        ' CalculationClassクラスの共有メソッドを呼び出し
-
        Console.WriteLine(CalculationClass.Add(1L, 2L))
-
        Console.WriteLine(CalculationClass.Subtract(5L, 3L))
-
        Console.WriteLine(CalculationClass.Multiply(3L, 4L))
-
        Console.WriteLine(CalculationClass.Divide(6L, 2L))
 

        

        
~
この共通言語仕様にしたがってクラスやメソッドをデザインすることにより、共通言語仕様に準拠した他の言語からでも使用できるように保証することが出来ます。 また、共通言語仕様(CLI)ではその一部として共通型システム(CTS, Common Type System)というものも定義しています。 これは、各言語の型に対して共通性を持たせるもので、クラス・インターフェイス・構造体・列挙体などの機能や特性、C#のintやVBのIntegerなどの組み込み型の扱いなどについてを定めています。 一つの例として、ある言語で作成したクラスを他の言語でも継承することがきるように保証するためには、この規則に従っている必要があります。
        ' 自作例外OriginalDivideByZeroExceptionの捕獲
-
        Try
 

        

        
~
では、共通言語仕様では具体的にどのようなことが定められているのか見てみます。 以下の表は、MSDNの&msdn(netfx,id,12a7a7h3){共通言語仕様};より特に重要と思われるいくつかの規則を抜粋し補足を加えたものです。
            Console.WriteLine(CalculationClass.Divide(1L, 0L))
+

          
+
|*CLSで定められている規則(抜粋)
+
|~機能|~説明|~補足|h
+
|>|>|~一般|
+
|~参照可能範囲|CLS 規則は、型のうち、定義しているアセンブリの外部に公開される部分にだけ適用されます。|以下の規則は、パブリックなメソッドやクラスにのみ適用され、プライベートメソッドやinternalなクラスでは言語でサポートされるすべての機能を使うことが出来ます。|
+
|~グローバル メンバ|グローバルで静的な (static) フィールドとメソッドは CLS 準拠ではありません。|すべてのメソッド・変数は何らかの型に属している必要があります。|
+
|>|>|~名前付け|
+
|~文字および大文字と小文字の区別|2 つの識別子の区別には、大文字と小文字の相違は使用できません。|例えばIDListとIdListという二つのパブリックなクラスを作成することは出来ません。|
+
|~キーワード|CLS 準拠の言語コンパイラは、キーワードと一致してしまう識別子を参照できるようにする機構を提供する必要があります。また、CLS 準拠の言語コンパイラは、その言語のキーワードが名前になっている仮想メソッドを定義またはオーバーライドするための機構も提供する必要があります。|C#では@int、VBでは[Property]のように記述することでキーワードをエスケープすることが出来ます。|
+
|~シグネチャ|型やメンバのシグネチャに含まれる戻り値の型およびパラメータの型はすべて CLS 準拠であることが必要です。|パブリックなクラスやメソッドでは、引数・戻り値もすべてCLS準拠である必要があります。|
+
|>|>|~型|
+
|~プリミティブ型|.NET Framework クラス ライブラリには、コンパイラが使用するプリミティブ データ型に対応する型が含まれています。これらの型のうち、Byte、Int16、Int32、Int64、Single、Double、Boolean、Char、Decimal、IntPtr、および String の型が CLS 準拠です。|UInt32やSByteはC#、VBなどでプリミティブ型としてサポートされていますが、これらはCLS非準拠の型となります。|
+
|~コンストラクタの呼び出し|コンストラクタは、継承されたインスタンス データにアクセスする前に、基本クラスのコンストラクタを呼び出す必要があります。|VBでMyBase.Newを呼び出す場合に最初のステートメントとして呼び出す必要があるのはこの規定に従うためです。|
+
|>|>|~クラス型|
+
|~継承|CLS 準拠クラスは、CLS 準拠クラスを継承する必要があります (System.Object は CLS 準拠です)。|CLSに準拠していないクラスを継承した場合はCLS非準拠とみなされます。|
+
|>|>|~型のメンバ|
+
|~オーバーロード|演算子のオーバーロードは CLS 準拠にはなりません。ただし、CLS には、Add() などの有用な名前を付け、メタデータのビットを設定する方法についてのガイドラインが用意されています。演算子のオーバーロードをサポートするコンパイラはこのガイドラインに準拠することが推奨されますが、必須ではありません。|演算子をオーバーロードすると同時にAddやSubtractといった代替メソッドを用意することで、オーバーロードされた演算子をサポートしない言語にも同等の機能を提供することが出来ます。|
+
|~変換演算子|op_Implicit または op_Explicit のいずれかがその戻り値の型でオーバーロードされた場合は、その代わりとなる変換方法を提供する必要があります。|上記の演算子の場合と同様、ToXxx/FromXxxといった型変換用の代替メソッドやコンストラクタを用意します。|
+
|>|>|~ポインタ型|
+
|~ポインタ|ポインタ型および関数ポインタ型は CLS 準拠ではありません。|例えば、引数にポインタを取るメソッドを公開することは出来ませんが、メソッド内でポインタを使った演算を行うことやプライベートメソッドでポインタ型の引数を取ることは出来ます。&br;関数ポインタは&msdn(netfx,method,System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer){Marshal.GetDelegateForFunctionPointer};などのメソッドを使ってデリゲートに変換することが出来ます。|
+
|>|>|~配列|
+
|~境界|配列の次元の下限値は 0 にする必要があります。|これはすべての言語で配列の最初のインデックスが0であることを意味します。|
+
|>|>|~例外|
+
|~継承|スローされるオブジェクトは、System.Exception 型であるか、または System.Exception から継承する必要があります。|数値や文字列を例外として直接スローすることは出来ず、Exceptionを継承したクラス(&msdn(netfx,type,System.Runtime.CompilerServices.RuntimeWrappedException){RuntimeWrappedException};など)でラップする必要があります。|
+
|>|>|~メタデータ|
+
|~CLS 準拠|型とそれらを定義するアセンブリとで、CLS 準拠の方法が異なる場合、それらの型を System.CLSCompliantAttribute でマークする必要があります。同様に、メンバとそれらの型とで CLS 準拠の方法が異なる場合には、それらのメンバもマークする必要があります。メンバまたは型に非 CLS 準拠のマークを付けた場合は、CLS に準拠した代替のメンバまたは型を提供する必要があります。|CLSCompliantAttributeについては[[後ほど詳しく>#CLSCompliantAttribute]]解説します。|
 

        

        
~
*&aname(CLSCompliantAttribute){属性によるCLS準拠のチェック (CLSCompliantAttribute)};
        Catch ex As OriginalDivideByZeroException
+
CLSに準拠することにより言語間の相互運用性が保証されるようになりますが、仕様をすべて把握していても誤ってCLS非準拠なコードを書いてしまう可能性はあります。 &msdn(netfx,type,CLSCompliantAttribute);を使うと、コンパイルの時点でコードがCLSに準拠しているかどうかチェックすることが出来ます。 この属性は、アセンブリや外部に公開されるクラス・メソッドなどに適用することでその要素をCLS準拠とするかどうかを指定し、CLS準拠とする場合はコンパイラに準拠しているかどうかをチェックさせる事が出来ます。
 

        

        
~
次の例は、アセンブリにCLSCompliantAttributeを指定し、アセンブリ全体でCLSに準拠するように指定した例です。 次のコードをコンパイルすると、コンパイラはアセンブリ全体でCLSに準拠しているかどうかをチェックします。 しかし、引数にCLS非準拠の型uintを取るメソッドが存在するため、警告を表示するようになります。
            Console.WriteLine(ex.Message)
 

        

        
~
#tabpage(C#)
        End Try
+
#code(cs,lib.cs){{
+
using System;
 

        

        
~
[assembly: CLSCompliant(true)]
        ' ValueClassのインスタンスを作成
~

          
        Dim a As New ValueClass(3)
~
namespace SampleLibrary
        Dim b As New ValueClass(2)
+
{
+
  public class Account
+
  {
+
    private uint id;
 

        

        
~
    public void SetID(uint newID)
        Dim result As ValueClass = a.Subtract(b)
+
    {
+
      id = newID;
+
    }
 

        

        
~
    public override string ToString()
        Console.WriteLine(result)
+
    {
+
      return string.Format("ID={0}", id);
+
    }
+
  }
+
}
+
}}
 

        

        
~
#prompt{{
        result = a.Add(b)
+
D:\clscompliant>csc /nologo /target:library lib.cs
+
lib.cs(11,23): warning CS3001: 引数の型 'uint' は CLS に準拠していません。
 

        

        
~
}}
        Console.WriteLine(result)
+
#tabpage(VB)
+
#code(vb,lib.vb){{
+
Imports System
+

          
+
<Assembly: CLSCompliant(True)>
+

          
+
Namespace SampleLibrary
+
  Public Class Account
+
    Private id As UInteger
 

        

        
+
    Public Sub SetID(ByVal newID As UInteger)
+
      id = newID
 
    End Sub
    End Sub
 

        

        
~
    Public Overrides Function ToString() As String
End Module
+
      Return String.Format("ID={0}", id)
+
    End Function
+
  End Class
+
End Namespace
 
}}
}}
 

        

        
~
#prompt{{
まず、CalclationClassのメソッドを使用する際には、long(System.Int64,VB.NET-Long)で渡す必要があるので、Long型の数値リテラルを表すサフィックスLをつけます。 つけなくてもVB.NETでは自動的にLongに変換されますが、ここではわかりやすさのためにつけています。 続いてValueClassを用いた例では、演算子ではなく代替メソッドにより演算させます。 このコードをコンパイルすると無事成功するはずです。 実行結果は次のようになります。
+
D:\clscompliant>vbc /nologo /target:library lib.vb
+
D:\clscompliant\lib.vb(9) : warning BC40028: パラメータ 'newID' の型は CLS に準
+
拠していません。
+

          
+
    Public Sub SetID(ByVal newID As UInteger)
+
                           ~~~~~
 

        

        
-
#prompt(実行結果){{
-
クラスライブラリからのメッセージ
-
3
-
2
-
12
-
3
-
0で除算しました。
-
1
-
5
-
Press any key to continue
 
}}
}}
+
#tabpage-end
 

        

        
~
CLSCompliantAttributeのコンストラクタに指定している値から想像できるように、CLSCompliantAttributeを指定した属性をCLS準拠とするかどうかを指定できます。 これにより、例えばアセンブリ全体ではCLS準拠としたいが、利便性のためにCLS非準拠なメソッドも提供したいといった場合にはCLSCompliantAttributeにfalseを指定することが出来ます。
さらに、このときのクラスビューはこのようになっているはずです。
-
#ref(14.png,クラスビュー)
 

        

        
~
次の例では、uintを引数にとるCLS非準拠なメソッドと、その代替となるlongを引数にとるCLS準拠なメソッドの二つを用意しています。 このコードをコンパイルしても、先の例とは異なり警告は発生しません。
**属性によるCLS準拠のチェック
-
CLS準拠のクラスライブラリをコーディングするに当たり、便利な属性が存在します。 今回の例のように、C#でコーディングしていて、コンパイルは成功したが、いざVB.NETで使用するとエラーが生じ、調べてみるとCLSには準拠していなかったという事態が生じると思います。 そこで、C#でのコンパイルの時点で、コードがCLSに準拠しているか否かをチェックするための属性CLSCompliantAttributeが存在します。 ひとまず先ほどのコードにそれを適用した例を見てみます。
 

        

        
~
#tabpage(C#)
#code(cs,CLSCompliantAttributeの適用){{
+
#code(cs,lib.cs){{
 
using System;
using System;
 

        

        
~
[assembly: CLSCompliant(true)]
[assembly: CLSCompliant(true)] // このアセンブリをCLS準拠とする
 

        

        
~
namespace SampleLibrary
namespace ClassLibrary
 
{
{
~
  public class Account
    // 計算クラス
~
  {
    public class CalculationClass
~
    private uint id;
    {
 

        

        
~
    [CLSCompliant(false)]
        [CLSCompliant(true)] // このメソッドをCLS準拠とする
~
    public void SetID(uint newID)
        public static long Add( long x, long y )
~
    {
        {
~
      id = newID;
            return x + y;
~
    }
        }
-

          
-
        [CLSCompliant(true)] // このメソッドをCLS準拠とする
-
        public static uint Subtract( uint x, uint y )
-
        {
-
            return x - y;
-
        }
 

        

        
~
    public void SetID(long newID)
        [CLSCompliant(false)] // このメソッドをCLS非準拠とする
~
    {
        public static uint Multiply( uint x, uint y )
~
      if (newID < 0) throw new ArgumentOutOfRangeException("newID", "負の値を指定することは出来ません。");
        {
-
            return x * y;
-
        }
 

        

        
~
      id = (uint)newID;
        // 属性の指定無し
-
        public static uint Divide( uint x, uint y )
-
        {
-
            if ( y == 0 ) throw new OriginalDivideByZeroException( "0で除算しました。" );
-
            return x / y;
-
        }
 
    }
    }
 

        

        
~
    public override string ToString()
    // 以下略
+
    {
+
      return string.Format("ID={0}", id);
+
    }
+
  }
+
}
 
}}
}}
+
#tabpage(VB)
+
#code(vb,lib.vb){{
+
Imports System
+

          
+
<Assembly: CLSCompliant(True)>
+

          
+
Namespace SampleLibrary
+
  Public Class Account
+
    Private id As UInteger
+

          
+
    <CLSCompliant(False)> _
+
    Public Sub SetID(ByVal newID As UInteger)
+
      id = newID
+
    End Sub
 

        

        
~
    Public Sub SetID(ByVal newID As Long)
このコードをコンパイルするとデバッグ出力には次のように出力されます。
~
      If newID < 0 Then Throw New ArgumentOutOfRangeException("newID", "負の値を指定することは出来ません。")

          
-
#prompt{{
-
------ ビルド開始 : プロジェクト : ClassLibrary, 構成 : Debug .NET ------
 

        

        
~
      id = CUInt(newID)
リソースを準備しています...
~
    End Sub
参照を更新しています...
-
メイン コンパイルを実行しています...
-
e:\visual c# .net program\basetechnologies\classlibrary\classlibrary.cs(18,32): error CS3001: 引数の型 'uint' は CLS に準拠していません。
-
e:\visual c# .net program\basetechnologies\classlibrary\classlibrary.cs(18,40): error CS3001: 引数の型 'uint' は CLS に準拠していません。
-
e:\visual c# .net program\basetechnologies\classlibrary\classlibrary.cs(18,17): error CS3002: 'ClassLibrary.CalculationClass.Subtract(uint, uint)' の戻り値の型は CLS に準拠していません。
-
e:\visual c# .net program\basetechnologies\classlibrary\classlibrary.cs(30,30): error CS3001: 引数の型 'uint' は CLS に準拠していません。
-
e:\visual c# .net program\basetechnologies\classlibrary\classlibrary.cs(30,38): error CS3001: 引数の型 'uint' は CLS に準拠していません。
-
e:\visual c# .net program\basetechnologies\classlibrary\classlibrary.cs(30,17): error CS3002: 'ClassLibrary.CalculationClass.Divide(uint, uint)' の戻り値の型は CLS に準拠していません。
 

        

        
~
    Public Overrides Function ToString() As String
ビルドの完了 -- エラー 6、警告 0
~
      Return String.Format("ID={0}", id)
サテライト アセンブリをビルドしています...
+
    End Function
+
  End Class
+
End Namespace
 
}}
}}
+
#tabpage-end
 

        

        
~
*演算子のオーバーロードと代替メソッドについてのメモ
まず、ソースコードの3行目で、このアセンブリ全体をCLS準拠であるとしています。 こうすることにより、全ての要素がCLS準拠であるという前提でコンパイルさます。 また、メソッド毎にも指定できます。 17行目でSubtract()メソッドはCLS準拠であるとしているのに、次の行では非準拠型のuintを使用しています。 そのため、エラーが表示されます。 続いて、23行目では引数をfalseに設定しているので、このメソッドはCLS非準拠になります。 そのため、uintを用いてもエラーになりません。 しかしこのような場合、このメソッドを公開する必要性があるのであれば、必須ではありませんが代替メソッドを用意する必要があります。
+
C#およびVBでは演算子のオーバーロードをサポートしています。 また、CLSに準拠したクラスライブラリでは、演算子をオーバーロードする場合は演算子のオーバーロードをサポートしていない言語のために代替メソッドを用意することが推奨されます。
 

        

        
~
このこととは別に、演算子のオーバーロードを含む型をコンパイルすると、加算や型変換など演算子の実装を含む特別なメソッドが自動的に生成されます。 例えば加算演算子ではop_Addition、明示的な型変換演算子ではop_Explicitなどです(参考: &msdn(netfx,id,ms229032){演算子のオーバーロード};)。
続いて、29行目で何も属性を指定しなかった30行目のDivide()メソッドは、アセンブリ全体がCLS準拠であるとしているため、Multiply()メソッドのようにCLS非準拠を明記しないとCLS準拠であると解釈されます。 そのためuintを使用していることによりエラーが生じます。
 

        

        
~
そのため、自動生成された演算子のメソッドを呼び出すことが出来るならば、代替メソッドを作成することなくオーバーロードされた演算子の機能を使うことが出来ます。 以下は、演算子をオーバーロードした型を使い、オーバーロードされた演算子に該当するメソッドを直接呼び出す例です。
*本当にVB.NETには演算子オーバーロードは存在しないか
-
今まではCLS準拠のコードを用いてクラスライブラリを作成してきました。 そこで学んだのは、VB.NETでは演算子の多重定義(オーバーロード)を使用できないので、代替メソッドを用意する必要がある、というものでした。 しかし、実のところは代替メソッドを使用しなくてもVB.NETからでも使用できます。 次のコードがその例です。
 

        

        
~
#tabpage(C#)
#code(cs,C#で記述したクラスライブラリ){{
+
#code(cs,lib.cs){{
 
using System;
using System;
 

        

        
 
[assembly: CLSCompliant(true)]
[assembly: CLSCompliant(true)]
 

        

        
~
namespace SampleLibrary
namespace ClassLibrary
 
{
{
~
  // int型の値をラップする構造体
    public class ValueClass
+
  public struct IntValue
+
  {
+
    private int val;
+

          
+
    public IntValue(int val)
+
    {
+
      this.val = val;
+
    }
+

          
+
    // 加算演算子
+
    public static IntValue operator+(IntValue x, IntValue y)
 
    {
    {
~
      return new IntValue(x.val + y.val);
        private int m_Value;
+
    }
+

          
+
    // 明示的な型変換演算子
+
    public static explicit operator int(IntValue x)
+
    {
+
      return x.val;
+
    }
 

        

        
~
    public override string ToString()
        // コンストラクタ
~
    {
        public ValueClass( int val )
~
      return val.ToString();
        {
-
            m_Value = val;
-
        }
-

          
-
        // 加算演算子
-
        public static ValueClass operator+ ( ValueClass x,  ValueClass y )
-
        {
-
            return new ValueClass( x.m_Value + y.m_Value );
-
        }
-
        
-
        // 減算演算子
-
        public static ValueClass operator- ( ValueClass x,  ValueClass y )
-
        {
-
            return new ValueClass( x.m_Value - y.m_Value );
-
        }
-

          
-
        // 明示的な型変換演算子
-
        public static explicit operator int ( ValueClass x )
-
        {
-
            return x.m_Value;
-
        }
 
    }
    }
+
  }
 
}
}
 
}}
}}
 

        

        
~
#code(cs,exe.cs){{
#code(vb,VB.NETで演算子のオーバーロードを使用したクラスを利用する){{
~
using System;
Imports ClassLibrary
~
using SampleLibrary;

          
-
Module SampleProject
 

        

        
~
class Sample
    Sub Main()
+
{
+
  static void Main()
+
  {
+
    IntValue x = new IntValue(2);
+
    IntValue y = new IntValue(3);
 

        

        
~
    IntValue z = IntValue.op_Addition(x, y);
        Dim a As New ValueClass(3)
-
        Dim b As New ValueClass(2)
 

        

        
~
    Console.WriteLine("x + y: {0}", z);
        Dim result As ValueClass
-
        Dim value As Integer
 

        

        
~
    int i = IntValue.op_Explicit(x);
        result = ValueClass.op_Addition(a, b) ' 加算
-
        value = ValueClass.op_Explicit(result) ' 明示的型変換
 

        

        
~
    Console.WriteLine("(int)x: {0}", i);
        Console.WriteLine(value)
+
  }
+
}
+
}}
 

        

        
~
#prompt(ビルド・実行した結果){{
        result = ValueClass.op_Subtraction(a, b) ' 減算
~
D:\clscompliant>csc /nologo /target:library lib.cs
        value = ValueClass.op_Explicit(result) ' 明示的型変換
 

        

        
~
D:\clscompliant>csc /nologo /r:lib.dll exe.cs
        Console.WriteLine(value)
+
exe.cs(11,27): error CS0571: 'SampleLibrary.IntValue.operator +(SampleLibrary.IntValue, SampleLibrary.IntValue)':
+
        演算子またはアクセサを明示的に呼び出すことはできません。
+
exe.cs(15,22): error CS0571: 'SampleLibrary.IntValue.explicit operator int(SampleLibrary.IntValue)':
+
        演算子またはアクセサを明示的に呼び出すことはできません。
+
}}
+
#tabpage(VB)
+
#code(vb,lib.vb){{
+
Imports System
+

          
+
<Assembly: CLSCompliant(True)>
+

          
+
Namespace SampleLibrary
+
  ' Integer型の値をラップする構造体
+
  Public Structure IntValue
+
    Private val As Integer
 

        

        
+
    Public Sub New(ByVal val As Integer)
+
      MyClass.val = val
 
    End Sub
    End Sub
 

        

        
~
    ' 加算演算子
End Module
+
    Public Shared Operator +(ByVal x As IntValue, ByVal y As IntValue) As IntValue
+
      Return New IntValue(x.val + y.val)
+
    End Operator
+

          
+
    ' 明示的な型変換演算子
+
    Public Shared Narrowing Operator CType(ByVal x As IntValue) As Integer
+
      Return x.val
+
    End Operator
+

          
+
    Public Overrides Function ToString() As String
+
      Return val.ToString()
+
    End Function
+
  End Structure
+
End Namespace
 
}}
}}
 

        

        
~
#code(vb,exe.vb){{
ここで使用したクラスでは、加減算演算子と明示的型変換演算子をオーバーロードしています(型変換演算子についてはこの文章を参考にして下さい)。 コードの先頭でCLSCompliant属性を指定しているので、このコードはCLS準拠となります。
+
Imports System
+
Imports SampleLibrary
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim x As New IntValue(2)
+
    Dim y As New IntValue(3)
 

        

        
~
    Dim z As IntValue = IntValue.op_Addition(x, y)
作成されたクラスライブラリを使用する側では、代替メソッドによりこれらの演算子に変わる方法で演算する必要があるのですが、クラスライブラリ側では代替メソッドは一切用意していません。 しかしながらop_Additionやop_Explicitなどのメソッドにより適切にコードが実行されています。
 

        

        
~
    Console.WriteLine("x + y: {0}", z)
これらの謎のメソッドはいつ作成されたかというと、C#のコンパイラがIL(中間言語:Intermediate Language)を生成するときに、自動的に作成されます。 インテリセンスなどではあらわれないのですが、使用することができます。 引数の取り方や戻り値は演算子のシグネチャと同じになります。
 

        

        
~
    Dim i As Integer = IntValue.op_Explicit(x)
#ref(15.png,メンバ一覧にはあらわれない)
+

          
+
    Console.WriteLine("CInt(x): {0}", i)
+
  End Sub
+
End Class
+
}}
+

          
+
#prompt(ビルド・実行した結果){{
+
D:\clscompliant>vbc /nologo /target:library lib.vb
+

          
+
D:\clscompliant>vbc /nologo /r:lib.dll exe.vb
+

          
+
D:\clscompliant>exe.exe
+
x + y: 5
+
CInt(x): 2
+
}}
+
#tabpage-end
 

        

        
~
C#ではコンパイルエラーとなり演算子のメソッドを直接呼び出すことは出来ませんが、VBではエラーとなる事もなく演算子のメソッドを直接呼び出すことが出来ています。 言語でサポートしている必要があるためあまり推奨される方法ではありませんが、演算子のオーバーロードをサポートしていない言語からは、用意された代替メソッドではなく演算子のメソッドを直接呼び出させるようにすることも可能です。
このことから、VB.NETでは演算子の多重定義はできないが、利用することができるといえます。 ただ、op_Additionが代替メソッドであるといわれればそれまでですが・・・
 

        

        
 
#navi(..)
#navi(..)
 

        

        

programming/netfx/classlibrary/index.wiki.txt

current previous
1,13 1,9
 
${smdncms:title,クラスライブラリの作成}
${smdncms:title,クラスライブラリの作成}
~
${smdncms:keywords,ライブラリ,クラスライブラリ}
${smdncms:keywords,ライブラリ,アセンブリ,クラスライブラリ}
 
${smdncms:meta,toc-amazonlivelink-keyword,books-jp,.net framework}
${smdncms:meta,toc-amazonlivelink-keyword,books-jp,.net framework}
 

        

        
+
ここでは.NET Frameworkでのクラスライブラリ(ライブラリアセンブリ)の概要とVisual Studio、MonoDevelopでのクラスライブラリの作り方、および関連する事項として共通言語仕様(CLS)とXMLドキュメントコメントについて解説します。
+

          
+
[[programming/netfx/classlibrary/0_howtocreate]]ではクラスライブラリの作成と使用で同一の言語を使用して解説しますが、[[programming/netfx/classlibrary/1_interoperability]]では異なる言語間でクラスライブラリを使用する場合についての注意点などについて解説しています。 また、[[programming/netfx/classlibrary/2_xmldoccomments]]では作成したクラスライブラリのドキュメントを作成する方法について解説しています。 これらは順に読み進めていくことをおすすめしますが、途中を読み飛ばして必要な部分だけを読んでいくことも出来るようにしています。
+

          
+
なお、本文ではクラスライブラリのデザインに関するガイドラインに従うようにしていますが、ガイドラインの詳細については触れていません。 詳しくはMSDNの&msdn(netfx,id,ms229042){クラス ライブラリ開発のデザイン ガイドライン};を参照してください。
+

          
 
-ページ一覧
-ページ一覧
~
#ls2_1(noroot,pathsort,depth=1)
#ls2_1(noroot,pathsort)
~

          
-関連するページ
-
--[[programming/netfx/attributes]]
-
--[[programming/netfx2/overview/assemblyembedresource]]

programming/netfx/classlibrary/2_xmldoccomments/index.wiki.txt

current previous
1,422 1,283
~
${smdncms:title,XMLドキュメントコメントを用いたドキュメントの作成}
${smdncms:title,XMLコメントドキュメントを用いたIntelliSense機能の活用}
+
${smdncms:keywords,XMLドキュメントコメント,XMLコメント,IntelliSense,リファレンス,ドキュメント,仕様,作成}
 
${smdncms:meta,toc-amazonlivelink-keyword,books-jp,.net framework}
${smdncms:meta,toc-amazonlivelink-keyword,books-jp,.net framework}
 

        

        
 
#navi(..)
#navi(..)
 

        

        
~
クラスライブラリなどを提供する場合、同時に使い方や詳細な仕様をまとめたドキュメントも提供する場合があります。 これらを個別に作成・メンテナンスすることも出来ますが、バージョンアップを重ねる度に実装とドキュメントの内容の同期を取るのは手間のかかる作業です。
----
 

        

        
~
これを解決する一つの方法として、ソースコード中にコメントとして直接ドキュメントを記述することで、実装とドキュメントの同期をしやすくするというやり方があります。 この場合、コメントからドキュメントを作成する方法が問題となってきます。 Doxygenなどこの様な機能を提供する専用のツールは存在しますが、C#およびVBコンパイラではこの様な要求を満たすXMLドキュメントコメントという機能がコンパイラの機能として用意されています。
*XMLコメントドキュメントとは
-
クラスライブラリを作る際にXMLコメントドキュメントというものを記述すると、自作クラスライブラリでもIntelliSense機能を使えるようになります。 その前に、肝心のXMLコメントドキュメントとは何なんでしょうか。 C#プロジェクトを新規作成した初期状態では次のようなコードが記述されているのを見ると思います。
 

        

        
~
ここではXMLドキュメントコメントの記述方法と、XMLドキュメントコメントを使ったドキュメントの生成方法について解説します。
#code(cs,XMLコメントの例){{
+

          
+
*XMLドキュメントコメントとは
+
XMLドキュメントコメントとは、ソースコードにクラスやメソッドなどの動作・引数・戻り値などの説明をコメント文の形式で記述するためのものです。 しかし、単なるコメント文ではなく、XMLフラグメント(不完全なXMLドキュメント)の形式で記述します。 XMLドキュメントコメントを記述することで、クラスやメソッドの説明をIntelliSenseに表示したり、ツールを使ってクラスライブラリのリファレンスドキュメントを作成したりすることが出来ます。
+

          
+
XMLドキュメントコメントを使って記述した一つの例を見てみます。 次のコードでは、通常のコメントとXMLドキュメントコメントの二種類を使ってコメント文を記述しています。
+

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

        

        
~
/// <summary>
namespace XMLCommentDocumentation
+
/// XMLドキュメントコメントを使ったサンプルです。
+
/// </summary>
+
class Sample
 
{
{
~
  /// <summary>
    /// <summary>
~
  /// アプリケーションのメイン エントリ ポイントです。
    /// Class1 の概要の説明です。
~
  /// </summary>
    /// </summary>
~
  static void Main()
    class Class1
~
  {
    {
~
    // メッセージを表示します
        /// <summary>
~
    Console.WriteLine("Hello, world");
        /// アプリケーションのメイン エントリ ポイントです。
~
  }
        /// </summary>
-
        [STAThread]
-
        static void Main(string[] args)
-
        {
-
            //
-
            // TODO: アプリケーションを開始するコードをここに追加してください。
-
            //
-
        }
-
    }
 
}
}
 
}}
}}
 

        

        
~
2つの/(スラッシュ)で始まる行は通常のコメント、3つの///で始まる行がXMLドキュメントコメントです。
このコードで言う3本の/(スラッシュ)から始まる行が通常のコメントとは異なるXMLコメントとして扱われます。 また、このXMLコメントの行においてはXMLタグを入れることができ、これによっていくつかの情報を記述することができます。 コンパイル時にこれらの情報をまとめたXMLファイルを出力させることができますが、これがXMLコメントドキュメントです。 ただし、これはVisual C# .NETのみの機能で、Visual C++ .NETでは条件付き、Visual Basic .NETに至っては全く使えません。 しかし、C#で生成されたXMLコメントドキュメントを活用することはできます。 それがIntelliSense 機能のへ活用です。
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+

          
+
''' <summary>
+
''' XMLドキュメントコメントを使ったサンプルです。
+
''' </summary>
+
Class Sample
+
  ''' <summary>
+
  ''' アプリケーションのメイン エントリ ポイントです。
+
  ''' </summary>
+
  Shared Sub Main()
+
    ' メッセージを表示します
+
    Console.WriteLine("Hello, world")
+
  End Sub
+
End Class
+
}}
+
1つの'(アポストロフィ)で始まる行は通常のコメント、3つの'''で始まる行がXMLドキュメントコメントです。
+
#tabpage-end
 

        

        
~
XMLドキュメントコメントは、通常のコメント文と同様にコメントとして扱われるためコンパイル結果には一切影響を及ぼしません。 ですが、C#コンパイラでは1.0、VBコンパイラでは8.0からXMLドキュメントコメントに対応していて、通常のコメント文とは異なる扱いをします。 コンパイル結果に影響を及ぼさない点は変わりませんが、ソースコード上に記述されているXMLドキュメントコメントを読み取り、単一のXMLドキュメントを生成できるようになっています。
*XMLコメントドキュメントを出力させる
~

          
とは言っても、XMLコメントを記述しただけではただのコメントでしかありません。 XMLコメントをコンパイル時に出力させなければなりません。 そのために、クラスライブラリとなるプロジェクトのプロパティを設定します。 ソリューションエクスプローラなどからプロジェクトのプロパティのダイアログを表示させます。
~
例えば、上記のコードからは次のようなXMLドキュメントが生成されます([[XMLドキュメントの生成方法について>#generate_xmldocument]]は後述します)。
#ref(00.png)
+

          
+
#code(xml){{
+
<?xml version="1.0"?>
+
<doc>
+
    <assembly>
+
        <name>xmlcomment</name>
+
    </assembly>
+
    <members>
+
        <member name="T:Sample">
+
            <summary>
+
            XMLドキュメントコメントを使ったサンプルです。
+
            </summary>
+
        </member>
+
        <member name="M:Sample.Main">
+
            <summary>
+
            アプリケーションのメイン エントリ ポイントです。
+
            </summary>
+
        </member>
+
    </members>
+
</doc>
+
}}
 

        

        
~
XMLドキュメントコメントでは[[いくつかの要素(タグ)>#document_elements]]を使うことができ、これによって動作や引数・戻り値などの説明を記述することができます。 コンパイル時にこれらのXMLフラグメントが一つのXMLドキュメントとしてまとめられ、生成されるXMLドキュメントを加工することによってドキュメントの作成などを行うことが出来るようになっています。
ここから、「構成プロパティ」の「ビルド」で、「XML ドキュメントファイル」の項目に出力させるXMLドキュメントファイル名を指定してやります。 ここではクラスライブラリと同じ名前を付けました。 後はビルドするだけでコード中のXMLコメントがここで指定されたファイルに出力されるようになります。
 

        

        
~
*XMLドキュメントコメントの記述
また、IntelliSenseを機能させるためには、出力されたクラスライブラリのDLLファイルと同じフォルダにXMLファイルをおかなければなりませんが、DLLファイル同様コンパイル時にXMLファイルもコピーされるので特別な手順は必要ありません。 DLLを配布したりする場合には、XMLファイルも同梱する必要があります。
+
XMLドキュメントコメントはXMLフラグメントとして記述するため、XMLの規則にならって記述する必要があります。 XML形式であれば基本的にどのような要素(タグ)でも記述することは出来ますが、使用するXML要素として推奨されているものがいくつか存在します。 ここではXMLドキュメントコメントの記述方法について見ていきます。
 

        

        
~
**&aname(document_elements){XMLドキュメントコメントの要素};
*XMLコメントドキュメントを記述する
~
XMLドキュメントコメントを記述するにあたって、コメントに使用するXML要素(タグ)として推奨されるものがいくつか定められています。 例えば説明を記述するためのsummary要素や戻り値を説明するためのreturns要素などです。 これらの要素を使ってXMLドキュメントコメントを記述することにより、オブジェクトブラウザや入力候補に表示させたり、ツールを使ってリファレンスを作成したりすることが出来ます。 また、これらの要素は、言語によらずC#・VBなどで同じ要素を使うことが出来ます。
続いて、XMLコメントドキュメントを記述します。 これはXMLであるので、当然XMLの規則にならって記述する必要があります。 つまり、XMLコメントとして出力するテキストは数種のXMLタグでくくる必要があります。 XMLタグの種類と、その用途をまとめたものが次の表です。
 

        

        
~
以下は、使用が推奨される要素を使ってXMLドキュメントコメントを記述した例です。
|タグ|用途|
-
|<summary>|型や型のメンバについての概要を記述する。|
-
|<remarks>|その他の関係する型の情報を記述する。|
-
|<param>|メソッドの宣言で用い、引数の説明を記述する。&br;name属性で引数毎の説明が記述できる。|
-
|<returns>|メソッドの宣言で用い、メソッドの戻り値についての説明を記述する。|
 

        

        
~
#code(cs,ソースコード上に記述したXMLドキュメントコメント){{
このほかにもいくつかありますが、ここでは省略します。 説明だけではわかりにくいので、実際に使用した例を次に示します。
-
#code(cs,XMLコメントの記述){{
 
using System;
using System;
 

        

        
~
namespace Smdn.Utils
[assembly: CLSCompliant(true)]
-

          
-
namespace ClassLibrary
 
{
{
~
  /// <summary>文字列操作に関するユーティリティメソッドを提供するクラスです。</summary>
    /// <summary>Girl クラスは一般的な「女の子」のデータを扱うクラスです。</summary>
~
  /// <remarks>このクラスは静的クラスです。 インスタンスを作成することは出来ません。</remarks>
    /// <remarks>モーニング娘。のデータを扱う場合はMusumeクラスを使って下さい。</remarks>
~
  public static class StringUtils
    public class Girl
+
  {
+
    /// <summary>文字列の並びを逆転した結果を返します。</summary>
+
    /// <remarks><paramref name="str"/>の長さが0の場合、長さ0の<see cref="System.String"/>を返します。</remarks>
+
    /// <param name="str">並びを逆転する<see cref="System.String"/>を指定します。</param>
+
    /// <returns>並びを逆転した<see cref="System.String"/>を返します。</returns>
+
    /// <exception cref="ArgumentNullException"><paramref name="str"/>がnullの場合にスローされます。</exception>
+
    /// <example>
+
    /// 次のコードでは、変数<c>foo</c>に文字列<c>"oof"</c>が代入されます。
+
    /// <code>
+
    /// string foo = StringUtils.Reverse("foo");
+
    /// </code>
+
    /// </example>
+
    public static string Reverse(string str)
 
    {
    {
~
      if (str == null) throw new ArgumentNullException("str");
        /// <summary>名前を表します。</summary>
~

          
        protected string m_Name;
+
      char[] chars = str.ToCharArray();
+

          
+
      Array.Reverse(chars);
 

        

        
~
      return new string(chars);
        /// <summary>年齢を表します。</summary>
-
        protected int m_Age;
-
        
-
        /// <summary>生年月日を表します。</summary>
-
        protected DateTime m_BirthDate;
-
        
-
        /// <summary>血液型を表します。</summary>
-
        protected string m_BloodType;
-

          
-

          
-
        /// <param name="name">名前</param>
-
        /// <param name="birthDate">生年月日。 この値と現在の日付から Age プロパティの値が計算されます。</param>
-
        /// <param name="bloodType">血液型</param>
-
        public Girl( string name, DateTime birthDate, string bloodType )
-
        {
-
            m_Name = name;
-
            m_BloodType = bloodType;
-
            
-
            // プロパティを使って代入
-
            BirthDate = birthDate;
-
        }
-

          
-
        /// <summary>名前を設定または取得します。</summary>
-
        public string Name
-
        {
-
            get
-
            {
-
                return m_Name;
-
            }
-
            set
-
            {
-
                m_Name = value;
-
            }
-
        }
-

          
-
        /// <summary>生年月日を設定または取得します。 同時に Age プロパティの値も再計算されます。</summary>
-
        public DateTime BirthDate
-
        {
-
            get
-
            {
-
                return m_BirthDate;
-
            }
-
            set
-
            {
-
                m_BirthDate = value;
-

          
-
                m_Age = DateTime.Now.Year - m_BirthDate.Year;
-

          
-
                DateTime birthDateInThisYear = new DateTime( DateTime.Now.Year, m_BirthDate.Month, m_BirthDate.Day );
-

          
-
                if ( birthDateInThisYear > DateTime.Now ) m_Age -= 1;
-
            }
-
        }
-

          
-
        /// <summary>年齢を設定または取得します。</summary>
-
        public int Age
-
        {
-
            get
-
            {
-
                return m_Age;
-
            }
-
            set
-
            {
-
                m_Age = value;
-
            }
-
        }
-

          
-
        /// <summary>血液型を設定または取得します。</summary>
-
        public string BloodType
-
        {
-
            get
-
            {
-
                return m_BloodType;
-
            }
-
            set
-
            {
-
                m_BloodType = value;
-
            }
-
        }
-

          
-
        /// <summary>このインスタンスのデータを適切な文字列型で返します。</summary>
-
        /// <returns>このインスタンスのデータを表す適切な文字列</returns>
-
        public override string ToString()
-
        {
-
            return m_Name + " ( " + 
-
                    m_BirthDate.ToLongDateString() + "生まれ " + 
-
                    m_Age.ToString() + "歳、" + 
-
                    m_BloodType + "型 )";
-
        }
 
    }
    }
 

        

        
~
    /// <summary>文字列を全角および半角の空白で区切った結果を<see cref="System.String"/>の配列で返します。</summary>
    /// <summary>Musume クラスはモーニング娘。のデータを扱うクラスです。
~
    /// <remarks><paramref name="str"/>の長さが0の場合、長さ0の<see cref="System.String"/>配列を返します。</remarks>
    /// Girl クラスから派生し、第何期メンバーであるかを表す Generation プロパティを持ちます。</summary>
~
    /// <param name="str">分割する<see cref="System.String"/>を指定します。</param>
    /// <remarks>モーニング娘。以外の「女の子」のデータを扱う場合は Girl クラスを使って下さい。</remarks>
~
    /// <returns>文字列を全角および半角の空白で区切った<see cref="System.String"/>の配列を返します。</returns>
    public class Musume : Girl
+
    /// <exception cref="ArgumentNullException"><paramref name="str"/>がnullの場合にスローされます。</exception>
+
    public static string[] SplitBySpace(string str)
 
    {
    {
~
      if (str == null) throw new ArgumentNullException("str");
        /// <summary>第何期メンバーかを表します。</summary>
-
        protected int m_Generation;
 

        

        
~
      return str.Split(new char[] {' ', ' '}, StringSplitOptions.None);
        /// <param name="name">名前</param>
-
        /// <param name="birthDate">生年月日。 この値と現在の日付から Age プロパティの値が計算されます。</param>
-
        /// <param name="bloodType">血液型</param>
-
        /// <param name="generation">モーニング娘。加入時期</param>
-
        public Musume( string name, DateTime birthDate, string bloodType, int generation )
-
            : base( name, birthDate, bloodType )
-
        {
-
            m_Generation = generation;
-
        }
-

          
-
        /// <summary>第何期メンバーかを取得します。 このプロパティは読み取り専用です。</summary>
-
        public int Generation
-
        {
-
            get
-
            {
-
                return m_Generation;
-
            }
-
        }
-

          
-
        /// <summary>このインスタンスのデータを適切な文字列型で返します。</summary>
-
        /// <returns>このインスタンスのデータを表す適切な文字列</returns>
-
        public override string ToString()
-
        {
-
            return base.ToString() + " モーニング娘。第" + m_Generation.ToString() + "期メンバー";
-
        }
 
    }
    }
+
  }
 
}
}
 
}}
}}
 

        

        
~
ここで使用した要素、およびよく使われる要素とその用途・意味を以下の表にまとめておきます。
#code(xml,出力されたXMLファイル){{
+

          
+
|*XMLドキュメントコメントの要素
+
|~要素|~形式|~用途と意味|h
+
|>|>|~説明文を記述するための要素|
+
|~summary要素|<summary>...</summary>|型やメンバに関する説明・概要を記述します。&br;この要素は他の要素を含むことも出来、他の型やメンバへのリンクとなるsee要素や段落を表すpara要素などを使って記述することが出来ます。|
+
|~remarks要素|<remarks>...</remarks>|型やメンバに関する補足的な追加の情報を記述します。&br;この要素でsummary要素の記述を補足します。 summary要素と同様、see要素やpara要素を使って記述する事も出来ます。|
+
|~param要素|<param name="name">...</param>|指定された引数nameに関する説明を記述します。&br;この要素で引数に指定できる値や、値域についての説明を記述します。 ArgumentExceptionなどの例外について記述するにはexception要素を使います。|
+
|~returns要素|<returns>...</returns>|メソッドの戻り値に関する説明を記述します。|
+
|~exception要素|<exception cref="member">...</exception>|メンバがスローする例外に関する説明を記述します。&br;cref属性でスローする例外、要素のテキストでその例外がスローされる理由・状況についての説明を記述します。 cref属性の指定方法は[[メンバのID>#xmlelement_memberid]]で解説します。|
+
|~example要素|<example>...</example>|型やメンバの使用例について記述します。&br;see要素やpara要素の他に、インラインのコードはc要素、複数行にまたがるコードはcode要素を使って記述することが出来ます。|
+
|~value要素|<value>...</value>|プロパティが表す値について記述します。|
+
|>|>|~リンクを作成するための要素|
+
|~paramref要素|<paramref name="name"/>|指定された引数nameの説明へのリンクを指定します。|
+
|~see要素|<see cref="member"/>|指定されたメンバmemberへのリンクを指定します。 memberには任意の型やメソッドを指定することが出来ます。 cref属性の指定方法は[[メンバのID>#xmlelement_memberid]]で解説します。|
+
|>|>|~説明文の文書構造を定義するための要素|
+
|~para要素|<para>...</para>|説明文の段落を定義します。 これはXHTML/HTMLのp要素やdiv要素に相当する要素です。|
+
|~c要素|<c>...</c>|インラインで表示されるコードを記述します。 説明文中の変数名やメソッド名などを修飾する場合に使います。|
+
|~code要素|<code>...</code>|複数行のコードを記述します。|
+

          
+
上記以外にも、型パラメータについて記述するtypeparam要素やリスト構造を作成するlist要素、他のドキュメントを読み込むinclude要素などがあります。 詳細は以下のドキュメントを参照してください。
+
-&msdn(netfx,id,5ast78ax){ドキュメント コメントとして推奨される XML タグ (C# プログラミング ガイド)};
+
-&msdn(netfx,id,ms172653){ドキュメント コメントとして推奨される XML タグ (Visual Basic)};
+

          
+
このようにして記述したXMLドキュメントコメントは、前述したとおり[[IDEの入力候補等でも表示>#idecomplement]]されるようになります。
+

          
+
なお、上記のコードをコンパイルすると、以下のようなXMLドキュメントが生成されます。
+

          
+
#code(xml,コンパイラが出力するXMLドキュメント){{
 
<?xml version="1.0"?>
<?xml version="1.0"?>
 
<doc>
<doc>
 
    <assembly>
    <assembly>
~
        <name>Smdn.Utils</name>
        <name>ClassLibrary</name>
 
    </assembly>
    </assembly>
 
    <members>
    <members>
~
        <member name="T:Smdn.Utils.StringUtils">
        <member name="T:ClassLibrary.Girl">
~
            <summary>文字列操作に関するユーティリティメソッドを提供するクラスです。</summary>
            <summary>Girl クラスは一般的な「女の子」のデータを扱うクラスです。</summary>
~
            <remarks>このクラスは静的クラスです。 インスタンスを作成することは出来ません。</remarks>
            <remarks>モーニング娘。のデータを扱う場合はMusumeクラスを使って下さい。</remarks>
~
        </member>
        </member>
~
        <member name="M:Smdn.Utils.StringUtils.Reverse(System.String)">
        <member name="F:ClassLibrary.Girl.m_Name">
~
            <summary>文字列の並びを逆転した結果を返します。</summary>
            <summary>名前を表します。</summary>
~
            <remarks><paramref name="str"/>の長さが0の場合、長さ0の<see cref="T:System.String"/>を返します。</remarks>
        </member>
~
            <param name="str">並びを逆転する<see cref="T:System.String"/>を指定します。</param>
        <member name="F:ClassLibrary.Girl.m_Age">
~
            <returns>並びを逆転した<see cref="T:System.String"/>を返します。</returns>
            <summary>年齢を表します。</summary>
~
            <exception cref="T:System.ArgumentNullException"><paramref name="str"/>がnullの場合にスローされます。</exception>
        </member>
~
            <example>
        <member name="F:ClassLibrary.Girl.m_BirthDate">
~
            次のコードでは、変数<c>foo</c>に文字列<c>"oof"</c>が代入されます。
            <summary>生年月日を表します。</summary>
~
            <code>
        </member>
~
            string foo = StringUtils.Reverse("foo");
        <member name="F:ClassLibrary.Girl.m_BloodType">
~
            </code>
            <summary>血液型を表します。</summary>
~
            </example>
        </member>
~
        </member>
        <member name="M:ClassLibrary.Girl.#ctor(System.String,System.DateTime,System.String)">
~
        <member name="M:Smdn.Utils.StringUtils.SplitBySpace(System.String)">
            <param name="name">名前</param>
~
            <summary>文字列を全角および半角の空白で区切った結果を<see cref="T:System.String"/>の配列で返します。</summary>
            <param name="birthDate">生年月日。 この値と現在の日付から Age プロパティの値が計算されます。</param>
~
            <remarks><paramref name="str"/>の長さが0の場合、長さ0の<see cref="T:System.String"/>配列を返します。</remarks>
            <param name="bloodType">血液型</param>
~
            <param name="str">分割する<see cref="T:System.String"/>を指定します。</param>
        </member>
~
            <returns>文字列を全角および半角の空白で区切った<see cref="T:System.String"/>の配列を返します。</returns>
        <member name="M:ClassLibrary.Girl.ToString">
~
            <exception cref="T:System.ArgumentNullException"><paramref name="str"/>がnullの場合にスローされます。</exception>
            <summary>このインスタンスのデータを適切な文字列型で返します。</summary>
-
            <returns>このインスタンスのデータを表す適切な文字列</returns>
-
        </member>
-
        <member name="P:ClassLibrary.Girl.Name">
-
            <summary>名前を設定または取得します。</summary>
-
        </member>
-
        <member name="P:ClassLibrary.Girl.BirthDate">
-
            <summary>生年月日を設定または取得します。 同時に Age プロパティの値も再計算されます。</summary>
-
        </member>
-
        <member name="P:ClassLibrary.Girl.Age">
-
            <summary>年齢を設定または取得します。</summary>
 
        </member>
        </member>
~
    </members>
        <member name="P:ClassLibrary.Girl.BloodType">
~
</doc>
            <summary>血液型を設定または取得します。</summary>
~
}}
        </member>
~

          
        <member name="T:ClassLibrary.Musume">
~
**&aname(xmlelement_memberid){メンバのID};
            <summary>Musume クラスはモーニング娘。のデータを扱うクラスです。
~
see要素やexception要素ではcref属性で型やメンバのIDを指定することにより、リンクを作成することが出来ます。 このIDは、「メンバの種類:完全限定名」の形式で定義されています。 メンバの種類は一文字で表され、型やメソッドなどを指定します。 IDの規則といくつかの例を表にまとめると次のようになります。
            Girl クラスから派生し、第何期メンバーであるかを表す Generation プロパティを持ちます。</summary>
~

          
            <remarks>モーニング娘。以外の「女の子」のデータを扱う場合は Girl クラスを使って下さい。</remarks>
~
|*IDの規則
        </member>
~
|~IDの例|~IDが表すメンバ|h
        <member name="F:ClassLibrary.Musume.m_Generation">
~
|>|~N (名前空間)|
            <summary>第何期メンバーかを表します。</summary>
~
|N:System.Collections|System.Collections名前空間|
        </member>
~
|>|~T (型、クラス/インターフェイス/構造体/列挙体/デリゲート)|
        <member name="M:ClassLibrary.Musume.#ctor(System.String,System.DateTime,System.String,System.Int32)">
~
|T:System.String|Stringクラス|
            <param name="name">名前</param>
~
|T:System.ArgumentException|ArgumentException例外クラス|
            <param name="birthDate">生年月日。 この値と現在の日付から Age プロパティの値が計算されます。</param>
~
|T:System.StringComparison|StringComparison列挙体|
            <param name="bloodType">血液型</param>
~
|T:System.IDisposable|IDisposableインターフェイス|
            <param name="generation">モーニング娘。加入時期</param>
~
|T:System.EventHandler|EventHandlerデリゲート|
        </member>
~
|T:System.Collections.Generic.List`1|List<T>ジェネリッククラス|
        <member name="M:ClassLibrary.Musume.ToString">
~
|T:System.Action`1|Action<T>ジェネリックデリゲート|
            <summary>このインスタンスのデータを適切な文字列型で返します。</summary>
~
|T:System.Action`2|Action<T1, T2>ジェネリックデリゲート|
            <returns>このインスタンスのデータを表す適切な文字列</returns>
~
|>|~M (メソッド、メソッド/コンストラクタ/オーバーロードされた演算子)|
        </member>
~
|M:System.String.IndexOf(System.String)|String.IndexOf(string)メソッド|
        <member name="P:ClassLibrary.Musume.Generation">
~
|M:System.String.IndexOf(System.String,System.Int32)|String.IndexOf(string, int)メソッド|
            <summary>第何期メンバーかを取得します。 このプロパティは読み取り専用です。</summary>
+
|M:System.String.#ctor(System.Char[])|String(char[])コンストラクタ|
+
|M:System.String.#ctor(System.Char,System.Int32)|String(char, int)コンストラクタ|
+
|M:System.String.op_Equality(System.String,System.String)|Stringクラスの等価演算子==|
+
|>|~P (プロパティ、プロパティ/インデクサ)|
+
|P:System.String.Length|String.Lengthプロパティ|
+
|P:System.String.Chars(String.Int32)|String.Charsプロパティ (インデクサ)|
+
|P:System.Collections.Generic.List`1.Count|List<T>.Countプロパティ|
+
|>|~F (フィールド)|
+
|F:System.IO.Path.DirectorySeparatorChar|Path.DirectorySeparatorCharフィールド|
+
|>|~E (イベント)|
+
|E:System.Windows.Form.Control.Click|Control.Clickイベント|
+

          
+
メンバのIDの詳細については、以下のドキュメントにまとめられています。
+
-&msdn(netfx,id,fsbx0t7x){XML ファイルの処理 (C# プログラミング ガイド)};
+
-&msdn(netfx,id,ms172673){XML ファイルの処理 (Visual Basic)};
+

          
+
**独自の要素・属性とXMLドキュメントの拡張
+
XMLドキュメントコメントでは、妥当なXMLである限り上記以外の要素や属性を使って記述することが出来ます。 例えば、以下のようにXML名前空間を使って独自の属性を埋め込んだり、XHTMLの要素を使ってドキュメントを記述するといったことが出来ます。
+

          
+
#code(cs){{
+
/// <summary xml:lang="ja">1つめ<see cref="T:System.String"/></summary>
+
/// <summary>2つめ<see cref="T:System.String" x:version="netfx2.0" xmlns:x="http://example.com/ns/extensions"/></summary>
+
/// <summary xml:lang="en">3rd<see cref="T:System.String"/></summary>
+
/// <remarks>
+
/// XHTMLの要素を使って説明を記述する例です。
+
/// <div>
+
///   <h1>メソッドの動作</h1>
+
///   <p>メソッドの動作をフローチャートで表すと次のようになります。
+
///     <img src="flowchart.png" alt="フローチャート" />
+
///   </p>
+
/// </div>
+
/// </remarks>
+
class XmlDocumentExtension
+
{
+
}
+
}}
+

          
+
上記のコードからは次のようなXMLドキュメントが生成されます。
+

          
+
#code(xml){{
+
<?xml version="1.0"?>
+
<doc>
+
    <assembly>
+
        <name>XmlDocumentExtension</name>
+
    </assembly>
+
    <members>
+
        <member name="T:XmlDocumentExtension">
+
            <summary xml:lang="ja">1つめ<see cref="T:System.String"/></summary>
+
            <summary>2つめ<see cref="T:System.String" x:version="netfx2.0" xmlns:x="http://example.com/ns/extensions"/></summary>
+
            <summary xml:lang="en">3rd<see cref="T:System.String"/></summary>
+
            <remarks>
+
            XHTMLの要素を使って説明を記述する例です。
+
            <div>
+
              <h1>メソッドの動作</h1>
+
              <p>メソッドの動作をフローチャートで表すと次のようになります。
+
                <img src="flowchart.png" alt="フローチャート" />
+
              </p>
+
            </div>
+
            </remarks>
 
        </member>
        </member>
 
    </members>
    </members>
 
</doc>
</doc>
 
}}
}}
 

        

        
~
独自の要素を含むXMLドキュメントを生成することが出来ても、整形・表示する側がサポートしていない場合は当然無視されるかエラーとなります。 整形・表示には対応しているツールを使用したり専用のツールを作成する必要があります。
各識別子に付いている略号についてですが、「T:〜」は型(Type)、「P:〜」はプロパティ(Property)、「F:〜」はフィールド (Field)、「M:〜」はメソッド(Method)ないしメンバ(Member)の頭文字と思われます。 また、「#ctor」はコンストラクタ (Constructor)の略と思われます。
+

          
+
**&aname(idecomplement){XMLドキュメントと入力候補};
+
Visual Studio 2008では、XMLドキュメントコメントを記述するだけで入力候補に表示されるようになります。 必ずしも[[XMLドキュメントを生成するように設定>#generate_xmldocument]]する必要はありません。
+
#image(idecomplement-vs2008-00.png,75%,入力候補にXMLドキュメントが表示されている例1)
+
#image(idecomplement-vs2008-01.png,75%,入力候補にXMLドキュメントが表示されている例2)
+

          
+
また、オブジェクトブラウザで閲覧する場合にも、記述したXMLドキュメントコメントの内容が表示されるようになります。
+
#image(idecomplement-vs2008-02.png,75%,オブジェクトブラウザでXMLドキュメントが表示されている例)
+

          
+
MonoDevelop 2.4でも同様に、XMLドキュメントを生成していなくても入力候補に表示されるようになります。
+
#image(idecomplement-md2.4-00.png,75%,入力候補にXMLドキュメントが表示されている例1)
+
#image(idecomplement-md2.4-01.png,75%,入力候補にXMLドキュメントが表示されている例2)
+

          
+
*&aname(generate_xmldocument){XMLドキュメントの生成};
+
XMLドキュメントコメントを記述するだけでもIDEの入力候補に表示されたりするようになりますが、コンパイラにXMLドキュメントを生成させるようにすることで、記述したXMLドキュメントコメントを他のツールを使って整形したりより見やすい形式に変換したりすることが出来るようになります。 ここでは、コマンドラインとIDE上でXMLドキュメントを生成するように設定する手順について見ていきます。
+

          
+
**コマンドラインでの作成
+
コマンドラインでXMLドキュメントを生成するために最低限必要なオプションは以下のとおりです。 出力するターゲットが実行可能ファイルでもライブラリでもXMLドキュメントを生成できます。 いずれもcsc・vbcおよびMono C#コンパイラであるmcs(gmcs)に共通です。
+

          
+
:/doc:(ファイル名)|XMLドキュメントを生成し、指定したファイル名で保存する
+
:(入力ファイル)|アセンブリのソースファイルを指定する
+

          
+
以下は先のコードを使ってコンパイルとXMLドキュメントの生成を行う例です。
+

          
+
#tabpage(csc)
+
#prompt{{
+
csc /doc:sample.xml xmlcomment.cs
+
}}
+
#tabpage(vbc)
+
#prompt{{
+
vbc /doc:sample.xml xmlcomment.vb
+
}}
+
#tabpage(gmcs)
+
#prompt{{
+
gmcs /doc:sample.xml xmlcomment.cs
+
}}
+
#tabpage-end
+

          
+
なお、2010年11月現在のvbncではXMLドキュメントの生成はサポートされていません。
+

          
+
**IDEでの設定
+
ここではXMLドキュメントを生成するためのIDEでの設定方法について見ていきます。
+

          
+
***Visual Studio 2008での例
+
XMLドキュメントを生成させたいプロジェクトのコンテキストメニューから[プロパティ]を選択します。
+
#image(genxml-vs2008-00.png,60%,[プロパティ]メニュー)
+

          
+
[ビルド]タブを選択し、最下部にある[出力]の[XML ドキュメント ファイル]にチェックを入れます。 必要に応じて、出力するXMLドキュメントのフォルダとファイル名を指定します。
+
#image(genxml-vs2008-01.png,60%,ビルドオプション)
+

          
+
以上の手順でビルド時にXMLドキュメントが生成されるようになります。
+

          
+
***MonoDevelop 2.4での例
+
XMLドキュメントを生成させたいプロジェクトのコンテキストメニューから[オプション]を選択します。
+
#image(genxml-md2.4-00.png,60%,[オプション]メニュー)
+

          
+
[ビルド]→[コンパイラ]の項目を選択し、[一般オプション]の[xml ドキュメンテーションを生成する]にチェックを入れます。
+
#image(genxml-md2.4-01.png,60%,コンパイラのオプション)
+

          
+
以上の手順でビルド時にXMLドキュメントが生成されるようになります。 XMLドキュメントは、プロジェクトの出力フォルダに「アセンブリ名.xml」のファイル名で出力されるようになります。 なお、生成されるXMLドキュメントのファイル名を指定することは出来ません。
+

          
+

          
+

          
+
*XMLドキュメントの整形とHTMLへの変換
+
XMLドキュメントを整形したり他の形式に変換するツールにはいくつか種類がありますが、ここでは一つの例として[[Monodoc:http://www.mono-project.com/Monodoc]]を使ってHTMLに変換する例を紹介します。 Monodocはドキュメントの編集と閲覧を行うためのツールですが、コンパイラが生成するXMLドキュメントをインポートしてHTML形式に整形して出力させることも出来るようになっています。
 

        

        
~
ではMonodocを使ってXMLドキュメントからHTMLを作成する手順について見ていきます。 ここでは[[programming/netfx/classlibrary/0_howtocreate]]で作成したライブラリSmdn.Utils.dllを使ってHTMLを作成します。 ビルドが完了した時点でのディレクトリ構成は次のようになっています。
*実際にXMLコメントドキュメントを使用する
-
このようにしてできたXMLコメントドキュメントを使用するには、先程述べたようにできあがったアセンブリ(DLLファイル)と同じフォルダにXML ファイルを配置するだけです。 これで、IntelliSenseが機能するようになります。 実際に機能しているか、いくつかスクリーンショットを載せておきます。
 

        

        
~
#prompt{{
#ref(01.png)
~
$ tree 
#ref(02.png)
~
.
#ref(03.png)
+
|-- AssemblyInfo.cs
+
|-- OrderByLengthComparer.cs
+
|-- Smdn.Utils.csproj
+
|-- Smdn.Utils.pidb
+
|-- StringUtils.cs
+
`-- bin
+
    `-- Debug
+
        |-- Smdn.Utils.dll
+
        |-- Smdn.Utils.dll.mdb
+
        `-- Smdn.Utils.xml
 

        

        
~
2 directories, 8 files
さらに、オブジェクト ブラウザからでも参照することができます。
+
}}
+

          
+
HTMLを生成するにはまずXMLドキュメントをインポートしてMonodoc形式に変換する必要があります。 mdoc updateコマンドでXMLドキュメントと元になるアセンブリを指定してインポートします。 ここでは、変換したMonodoc形式ファイルの出力先ディレクトリ名をmdocとしました。
+

          
+
#prompt{{
+
$ mdoc update -i ./bin/Debug/Smdn.Utils.xml -o ./mdoc/ ./bin/Debug/Smdn.Utils.dll
+
New Type: Smdn.Utils.StringUtils
+
Member Added: public static string Reverse (string str);
+
Member Added: public static string[] SplitBySpace (string str);
+
Namespace Directory Created: Smdn.Utils
+
New Namespace File: Smdn.Utils
+
New Type: Smdn.Utils.OrderByLengthComparer
+
Member Added: public OrderByLengthComparer ();
+
Member Added: public override int Compare (string x, string y);
+
Members Added: 4, Members Deleted: 0
+

          
+
$ tree ./mdoc/
+
./mdoc/
+
|-- Smdn.Utils
+
|   |-- OrderByLengthComparer.xml
+
|   `-- StringUtils.xml
+
|-- index.xml
+
`-- ns-Smdn.Utils.xml
+

          
+
1 directory, 4 files
+
}}
+

          
+
これでMonodoc形式に変換されました。 Monodocでは専用のエディタを使ってこのファイルを編集することも出来ますが、ここでは編集を省略してHTMLへの変換に進みます。
+

          
+
mdoc export-htmlコマンドでMonodoc形式からHTML形式に変換します。 出力先のディレクトリ名はhtmlとしました。
+

          
+
#prompt{{
+
$ mdoc export-html -o ./html/ ./mdoc/
+
Smdn.Utils.OrderByLengthComparer
+
Smdn.Utils.StringUtils
+

          
+
$ tree ./html/
+
./html/
+
|-- Smdn.Utils
+
|   |-- OrderByLengthComparer.html
+
|   |-- StringUtils.html
+
|   `-- index.html
+
`-- index.html
+

          
+
1 directory, 4 files
+
}}
 

        

        
~
これで変換されたHTMLファイルが出力されます。 生成されたindex.htmlをブラウザで閲覧すると、ドキュメントを参照することが出来ます。 なお、生成したファイルは[[こちらのページ>programming/netfx/classlibrary/2_xmldoccomments/monodocgenerated]]でも閲覧できるようにしてありますのでご覧ください。
#ref(04.png)
-
#ref(05.png)
 

        

        
~
#image(monodoc-00.png,60%,Monodocによって生成されたHTML)
このように、XMLコメントドキュメントを活用すれば、インテリセンスでコーディングが楽になるほか、自作のクラスライブラリを配布する際などにも十分活用できると思います。
 

        

        
 
#navi(..)
#navi(..)