次の方法で共有


言語に依存しないコンポーネントと言語に依存しないコンポーネント

.NET は言語に依存しません。 つまり、開発者は、C#、F#、Visual Basic などの .NET 実装を対象とする多くの言語のいずれかで開発できます。 .NET 実装用に開発されたクラス ライブラリの型とメンバーにアクセスできます。最初に記述された言語を知らなくても、元の言語の規則に従う必要はありません。 コンポーネント開発者の場合は、その言語に関係なく、任意の .NET アプリからコンポーネントにアクセスできます。

この記事の最初の部分では、言語に依存しないコンポーネント、つまり、任意の言語で記述されたアプリで使用できるコンポーネントの作成について説明します。 複数の言語で記述されたソース コードから 1 つのコンポーネントまたはアプリを作成することもできます。この記事の第 2 部の「 言語間相互運用性 」を参照してください。

任意の言語で記述された他のオブジェクトと完全に対話するには、オブジェクトが呼び出し元にすべての言語に共通する機能のみを呼び出し元に公開する必要があります。 この一般的な機能セットは、共通 言語仕様 (CLS) によって定義されます。これは、生成されたアセンブリに適用される一連の規則です。 共通言語仕様は、 ECMA-335 Standard: Common Language Infrastructure のパーティション I、7 から 11 の句で定義されています。

コンポーネントが共通言語仕様に準拠している場合は、CLS に準拠することが保証され、CLS をサポートする任意のプログラミング言語で記述されたアセンブリ内のコードからアクセスできます。 CLSCompliantAttribute 属性をソース コードに適用することで、コンパイル時にコンポーネントが共通言語仕様に準拠しているかどうかを判断できます。 詳細については、「 CLSCompliantAttribute 属性」を参照してください。

CLS コンプライアンス規則

このセクションでは、CLS 準拠コンポーネントを作成するための規則について説明します。 規則の完全な一覧については、 ECMA-335 Standard: Common Language Infrastructure のパーティション I 第 11 項を参照してください。

共通言語仕様では、コンシューマー (CLS 準拠のコンポーネントにプログラムでアクセスする開発者)、フレームワーク (言語コンパイラを使用して CLS 準拠ライブラリを作成している開発者)、エクステンダー (言語コンパイラや CLS 準拠コンポーネントを作成するコード パーサーなどのツールを作成している開発者) に適用される CLS 準拠の各規則について説明します。 この記事では、フレームワークに適用される規則に焦点を当てます。 ただし、エクステンダーに適用される規則の一部は 、Reflection.Emit を使用して作成されたアセンブリにも適用される場合があることに注意してください。

言語に依存しないコンポーネントを設計するには、CLS 準拠の規則をコンポーネントのパブリック インターフェイスにのみ適用する必要があります。 プライベート実装は、仕様に準拠する必要はありません。

Von Bedeutung

CLS コンプライアンスの規則は、コンポーネントのパブリック インターフェイスにのみ適用され、プライベート実装には適用されません。

たとえば、 Byte 以外の符号なし整数は CLS に準拠していません。 次の例のPerson クラスはAge型のUInt16 プロパティを公開しているため、次のコードはコンパイラ警告を表示します。

using System;

[assembly: CLSCompliant(true)]

public class Person
{
   private UInt16 personAge = 0;

   public UInt16 Age
   { get { return personAge; } }
}
// The attempt to compile the example displays the following compiler warning:
//    Public1.cs(10,18): warning CS3003: Type of 'Person.Age' is not CLS-compliant
<Assembly: CLSCompliant(True)>

Public Class Person
    Private personAge As UInt16

    Public ReadOnly Property Age As UInt16
        Get
            Return personAge
        End Get
    End Property
End Class
' The attempt to compile the example displays the following compiler warning:
'    Public1.vb(9) : warning BC40027: Return type of function 'Age' is not CLS-compliant.
'    
'       Public ReadOnly Property Age As UInt16
'                                ~~~

Person クラスを CLS 準拠にするには、Age プロパティの型を UInt16 から Int16 (CLS 準拠の 16 ビット符号付き整数) に変更します。 プライベート personAge フィールドの型を変更する必要はありません。

using System;

[assembly: CLSCompliant(true)]

public class Person
{
   private Int16 personAge = 0;

   public Int16 Age
   { get { return personAge; } }
}
<Assembly: CLSCompliant(True)>

Public Class Person
    Private personAge As UInt16

    Public ReadOnly Property Age As Int16
        Get
            Return CType(personAge, Int16)
        End Get
    End Property
End Class

ライブラリのパブリック インターフェイスは、次の要素で構成されます。

  • パブリック クラスの定義。

  • パブリック クラスのパブリック メンバーの定義と、派生クラスにアクセスできるメンバーの定義 (つまり、保護されたメンバー)。

  • パブリック クラスのパブリック メソッドのパラメーターと戻り値の型、および派生クラスからアクセスできるメソッドのパラメーターと戻り値の型。

CLS 準拠の規則を次の表に示します。 ルールのテキストは、 Ecma-335 Standard: Common Language Infrastructure (Ecma International の Copyright 2012) から逐語的に取られます。 これらの規則の詳細については、次のセクションを参照してください。

カテゴリ 参照先 規則 ルール番号
アクセシビリティ メンバーのアクセシビリティ 継承されたメソッドをオーバーライドする場合、アクセシビリティは変更してはいけない。ただし、別のアセンブリから継承されたメソッドをアクセシビリティ family-or-assembly でオーバーライドする場合は除く。 この場合、オーバーライドにはアクセシビリティ familyがあります。 10
アクセシビリティ メンバーのアクセシビリティ 型とメンバーの可視性とアクセシビリティは、メンバー自体が可視でアクセス可能であるときに、メンバーのシグネチャにある型も可視でアクセス可能であるようにすべきです。 たとえば、アセンブリの外部で参照できるパブリック メソッドには、その型がアセンブリ内でのみ表示される引数は含まれません。 任意のメンバーのシグネチャで使用されるインスタンス化されたジェネリック型を構成する型の可視性とアクセシビリティは、メンバー自体が可視でアクセス可能な際には、常に可視でアクセス可能でなければなりません。 たとえば、アセンブリの外部に表示されるメンバーのシグネチャに存在するインスタンス化されたジェネリック型には、アセンブリ内でのみ参照可能な型のジェネリック引数は含まれません。 12
配列 配列 配列には CLS 準拠型の要素があり、配列のすべての次元の下限は 0 である必要があります。 オーバーロードを区別するために必要なのは、項目が配列であり、配列の要素型であるという事実だけです。 オーバーロードが 2 つ以上の配列型に基づいている場合、要素型は名前付き型である必要があります。 16
属性 属性 属性は、System.Attribute型、またはその型から継承された型である必要があります。 41
属性 属性 CLS では、カスタム属性のエンコードのサブセットのみが許可されます。 これらのエンコードに表示される唯一の型は (パーティション IV を参照) です。 System.TypeSystem.StringSystem.CharSystem.BooleanSystem.ByteSystem.Int16System.Int32System.Int64System.SingleSystem.Double、および CLS 準拠の基本整数型に基づく列挙型。 34
属性 属性 CLS では、パブリックに表示される必要な修飾子 (modreq、パーティション II を参照) は許可されませんが、認識できない省略可能な修飾子 (modopt、パーティション II を参照) は許可されます。 35
コンストラクター コンストラクター オブジェクト コンストラクターは、継承されたインスタンス データへのアクセスが発生する前に、その基底クラスのインスタンス コンストラクターを呼び出す必要があります。 (これは、コンストラクターを持つ必要のない値型には適用されません)。 21 (二十一)
コンストラクター コンストラクター オブジェクト コンストラクターは、オブジェクトの作成の一部として以外は呼び出されず、オブジェクトは 2 回初期化されません。 22
列挙 列挙 列挙型の基になる型は組み込みの CLS 整数型で、フィールドの名前は "value__" で、そのフィールドは RTSpecialNameマークされます。 7
列挙 列挙 System.FlagsAttributeの有無 (パーティション IV ライブラリを参照) カスタム属性によって示される、2 種類の列挙型があります。 1 つは名前付き整数値を表します。もう 1 つは、名前のない値を生成するために組み合わせることができる名前付きビット フラグを表します。 enumの値は、指定した値に限定されません。 8
列挙 列挙 列挙型のリテラル静的フィールドは、列挙型自体の型を持つ必要があります。 9
イベント イベント イベントを実装するメソッドは、メタデータ内で SpecialName マークされます。 二十九
イベント イベント イベントとそのアクセサーのアクセシビリティは同じである必要があります。 30
イベント イベント イベントの add メソッドと remove メソッドの両方が存在するか、存在しないかのどちらかである必要があります。 31
イベント イベント イベントの add メソッドと remove メソッドは、それぞれ、イベントの型を定義し、 System.Delegate から派生する型を持つ 1 つのパラメーターを受け取る必要があります。 32
イベント イベント イベントは、特定の名前付けパターンに従う必要があります。 CLS 規則 29 で参照される SpecialName 属性は、適切な名前の比較では無視され、識別子規則に従う必要があります。 33
例外 例外 スローされるオブジェクトは、 System.Exception 型またはそれを継承する型である必要があります。 ただし、CLS に準拠したメソッドは、他の種類の例外の伝達をブロックする必要はありません。 40
全般 CLS コンプライアンス規則 CLS 規則は、定義アセンブリの外部でアクセス可能または参照可能な型の部分にのみ適用されます。 1
全般 CLS コンプライアンス規則 CLS 非準拠型のメンバーを CLS 準拠と指定しない。 2
ジェネリック ジェネリック型とメンバー 入れ子になった型には、外側の型と同数以上のジェネリック パラメーターが含まれている必要があります。 入れ子にされた型のジェネリック パラメーターは、それを囲む型のジェネリック パラメーターと、位置によって対応します。 42
ジェネリック ジェネリック型とメンバー ジェネリック型の名前は、上記で定義した規則に従って、入れ子になっていない型で宣言された型パラメーターの数、または入れ子になった場合に新しく型に導入された型パラメーターの数をエンコードする必要があります。 43
ジェネリック ジェネリック型とメンバー ジェネリック型は、基本型の制約、またはインターフェイスがジェネリック型の制約によって満たされることを保証するために十分な制約を再宣言する必要があります。 44
ジェネリック ジェネリック型とメンバー ジェネリック パラメーターの制約として使用される型は、それ自体が CLS に準拠している必要があります。 45
ジェネリック ジェネリック型とメンバー インスタンス化されたジェネリック型のメンバー (入れ子になった型を含む) の可視性とアクセシビリティは、ジェネリック型宣言全体ではなく、特定のインスタンス化のスコープと見なされます。 これを想定すると、CLS 規則 12 の可視性とアクセシビリティの規則が引き続き適用されます。 46
ジェネリック ジェネリック型とメンバー 抽象メソッドまたは仮想ジェネリック メソッドごとに、既定の具象 (非abstract) 実装が存在する必要があります。 47
インターフェイス インターフェイス CLS 準拠インターフェイスを実装するために、非 CLS 準拠メソッドの定義は必要ありません。 18
インターフェイス インターフェイス CLS 準拠インターフェイスは静的メソッドを定義せず、フィールドも定義しません。 19
メンバー 一般的な型メンバー グローバル静的フィールドとメソッドは CLS に準拠していません。 36
メンバー -- リテラル静的の値は、フィールド初期化メタデータを使用して指定されます。 CLS 準拠リテラルは、フィールド初期化メタデータで、リテラルとまったく同じ型 (または、そのリテラルが enumの場合は基になる型) の値を指定する必要があります。 13
メンバー 一般的な型メンバー vararg 制約は CLS の一部ではなく、CLS でサポートされている唯一の呼び出し規則は、標準のマネージド呼び出し規則です。 15
名前付け規則 名前付け規則 アセンブリは、Unicode 正規化フォームでオンラインで使用できる識別子に開始および含まれることが許可されている文字のセットを管理する Unicode Standard3.0 のテクニカル レポート 15 の付属書 7 に従う必要があります。 識別子は、Unicode 正規化形式 C で定義された正規形式である必要があります。CLS の目的上、小文字のマッピング (Unicode ロケールを区別しない 1 対 1 の小文字マッピングで指定) が同じ場合、2 つの識別子は同じです。 つまり、2 つの識別子が CLS の下で異なると見なされるためには、単に異なる場合よりも異なる必要があります。 ただし、継承された定義をオーバーライドするには、CLI で元の宣言の正確なエンコードを使用する必要があります。 4
オーバーロード 名前付け規則 CLS 準拠のスコープに導入されるすべての名前は、完全に独立した種類である必要があります。ただし、名前が同じでオーバーロードによって解決できる場合を除きます。 つまり、CTS では、1 つの型でメソッドとフィールドに同じ名前を使用することができますが、CLS では使用されません。 5
オーバーロード 名前付け規則 CTS では個別の署名を区別できる場合でも、フィールドと入れ子になった型は識別子の比較だけで区別されます。 同じ名前を持つメソッド、プロパティ、およびイベント (識別子の比較) は、CLS 規則 39 で指定されている場合を除き、戻り値の型以上の違いがあります 6
オーバーロード オーバーロード オーバーロードできるのはプロパティとメソッドだけです。 37
オーバーロード オーバーロード プロパティとメソッドは、パラメーターの数と型にのみ基づいてオーバーロードできます。ただし、 op_Implicit および op_Explicit という名前の変換演算子は、戻り値の型に基づいてオーバーロードすることもできます。 三十八
オーバーロード -- 型で宣言されている 2 つ以上の CLS 準拠メソッドの名前が同じで、特定の型インスタンス化のセットに対して、パラメーターと戻り値の型が同じである場合、これらのメソッドはすべて、それらの型のインスタンス化で意味的に同等である必要があります。 48
特性 プロパティ プロパティの getter メソッドと setter メソッドを実装するメソッドは、メタデータ内で SpecialName マークする必要があります。 二十四
特性 プロパティ プロパティのアクセサーはすべて静的であるか、すべて仮想であるか、すべてインスタンスである必要があります。 26
特性 プロパティ プロパティの型は、getter の戻り値の型と、セッターの最後の引数の型である必要があります。 プロパティのパラメーターの型は、getter のパラメーターの型と、セッターの最後のパラメーターを除くすべての型である必要があります。 これらの型はすべて CLS に準拠し、マネージド ポインターではない (つまり、参照渡しされない)。 二十七
特性 プロパティ プロパティは、特定の名前付けパターンに従う必要があります。 CLS 規則 24 で参照される SpecialName 属性は、適切な名前比較では無視され、識別子規則に従う必要があります。 プロパティには、getter メソッド、セッター メソッド、またはその両方が含まれます。 28
型変換 型変換 op_Implicitまたはop_Explicitが提供される場合は、強制を提供する別の手段を提供する必要があります。 39
種類 型および型メンバーのシグネチャ ボックス化された値型は CLS に準拠していません。 3
種類 型および型メンバーのシグネチャ シグネチャに表示されるすべての型は、CLS に準拠している必要があります。 インスタンス化されたジェネリック型を構成するすべての型は、CLS に準拠している必要があります。 11
種類 型および型メンバーのシグネチャ 型指定された参照は CLS 準拠ではありません。 14
種類 型および型メンバーのシグネチャ 管理されていないポインター型は CLS に準拠していません。 十七
種類 型および型メンバーのシグネチャ CLS 準拠のクラス、値の型、およびインターフェイスでは、CLS に準拠していないメンバーの実装は必要ありません 20
種類 型および型メンバーのシグネチャ System.Object は CLS に準拠しています。 その他の CLS 準拠クラスは、CLS 準拠クラスから継承する必要があります。 23

サブセクションへのインデックス:

型および型メンバーのシグネチャ

System.Object 型は CLS に準拠しており、.NET 型システムのすべてのオブジェクト型の基本型です。 .NET での継承は、暗黙的 (たとえば、String クラスがObject クラスから暗黙的に継承) または明示的 (たとえば、CultureNotFoundException クラスは、Exception クラスから明示的に継承される ArgumentException クラスから明示的に継承されます)。 派生型を CLS に準拠させるには、その基本型も CLS に準拠している必要があります。

次の例は、基本型が CLS に準拠していない派生型を示しています。 これは、符号なし 32 ビット整数をカウンターとして使用する基本 Counter クラスを定義します。 このクラスは符号なし整数をラップすることによってカウンター機能を提供するため、クラスは非 CLS 準拠としてマークされます。 その結果、派生クラス ( NonZeroCounter) も CLS に準拠していません。

using System;

[assembly: CLSCompliant(true)]

[CLSCompliant(false)]
public class Counter
{
   UInt32 ctr;

   public Counter()
   {
      ctr = 0;
   }

   protected Counter(UInt32 ctr)
   {
      this.ctr = ctr;
   }

   public override string ToString()
   {
      return String.Format("{0}). ", ctr);
   }

   public UInt32 Value
   {
      get { return ctr; }
   }

   public void Increment()
   {
      ctr += (uint) 1;
   }
}

public class NonZeroCounter : Counter
{
   public NonZeroCounter(int startIndex) : this((uint) startIndex)
   {
   }

   private NonZeroCounter(UInt32 startIndex) : base(startIndex)
   {
   }
}
// Compilation produces a compiler warning like the following:
//    Type3.cs(37,14): warning CS3009: 'NonZeroCounter': base type 'Counter' is not
//            CLS-compliant
//    Type3.cs(7,14): (Location of symbol related to previous warning)
<Assembly: CLSCompliant(True)>

<CLSCompliant(False)> _
Public Class Counter
    Dim ctr As UInt32

    Public Sub New
        ctr = 0
    End Sub

    Protected Sub New(ctr As UInt32)
        ctr = ctr
    End Sub

    Public Overrides Function ToString() As String
        Return String.Format("{0}). ", ctr)
    End Function

    Public ReadOnly Property Value As UInt32
        Get
            Return ctr
        End Get
    End Property

    Public Sub Increment()
        ctr += CType(1, UInt32)
    End Sub
End Class

Public Class NonZeroCounter : Inherits Counter
    Public Sub New(startIndex As Integer)
        MyClass.New(CType(startIndex, UInt32))
    End Sub

    Private Sub New(startIndex As UInt32)
        MyBase.New(CType(startIndex, UInt32))
    End Sub
End Class
' Compilation produces a compiler warning like the following:
'    Type3.vb(34) : warning BC40026: 'NonZeroCounter' is not CLS-compliant 
'    because it derives from 'Counter', which is not CLS-compliant.
'    
'    Public Class NonZeroCounter : Inherits Counter
'                 ~~~~~~~~~~~~~~

メソッドの戻り値の型やプロパティ型など、メンバー シグネチャに表示されるすべての型は CLS に準拠している必要があります。 さらに、ジェネリック型の場合:

  • インスタンス化されたジェネリック型を構成するすべての型は、CLS に準拠している必要があります。

  • ジェネリック パラメーターの制約として使用されるすべての型は、CLS に準拠している必要があります。

.NET 共通型システム には、共通言語ランタイムによって直接サポートされ、アセンブリのメタデータで特別にエンコードされる多くの組み込み型が含まれています。 これらの組み込み型のうち、次の表に示す型は CLS に準拠しています。

CLS 準拠型 説明
バイト 8 ビット符号なし整数
Int16 16 ビット符号付き整数
Int32 32 ビット符号付き整数
Int64 64 ビット符号付き整数
半分 半精度浮動小数点数値
シングル 単精度浮動小数点数値
ダブル 倍精度浮動小数点数値
ブーリアン true または false 値型
焦がす UTF-16 でエンコードされたコードユニット
小数 浮動小数点以外の 10 進数
IntPtr プラットフォーム定義サイズのポインターまたはハンドル
ストリング 0 個、1 つ以上の Char オブジェクトのコレクション

次の表に示す組み込み型は CLS 準拠ではありません。

非準拠型 説明 CLS 準拠の代替
SByte 8 ビット符号付き整数データ型 Int16
UInt16 する 16 ビット符号なし整数 Int32
UInt32 する 32 ビット符号なし整数 Int64
UInt64 する 64 ビット符号なし整数 Int64 (オーバーフローする可能性があります)、 BigInteger、または Double
UIntPtr 符号なしポインターまたはハンドル IntPtr

.NET クラス ライブラリまたはその他のクラス ライブラリには、CLS に準拠していない他の型が含まれる場合があります。次に例を示します。

  • ボックス化された値型。 次の C# の例では、int* という名前Value型のパブリック プロパティを持つクラスを作成します。 int*はボックス化された値型であるため、コンパイラは CLS 非準拠としてフラグを設定します。

    using System;
    
    [assembly:CLSCompliant(true)]
    
    public unsafe class TestClass
    {
       private int* val;
    
       public TestClass(int number)
       {
          val = (int*) number;
       }
    
       public int* Value {
          get { return val; }
       }
    }
    // The compiler generates the following output when compiling this example:
    //        warning CS3003: Type of 'TestClass.Value' is not CLS-compliant
    
  • 型指定された参照。オブジェクトへの参照と型への参照を含む特殊なコンストラクトです。 型指定された参照は、 TypedReference クラスによって .NET で表されます。

型が CLS に準拠していない場合は、CLSCompliantAttribute属性をisCompliantfalse値とともに適用する必要があります。 詳細については、「 CLSCompliantAttribute 属性 」セクションを参照してください。

次の例は、メソッド シグネチャとジェネリック型のインスタンス化における CLS 準拠の問題を示しています。 InvoiceItem型のプロパティ、UInt32型のプロパティ、およびNullable<UInt32>型とUInt32のパラメーターを持つコンストラクターを持つNullable<UInt32> クラスを定義します。 この例をコンパイルしようとすると、4 つのコンパイラ警告が表示されます。

using System;

[assembly: CLSCompliant(true)]

public class InvoiceItem
{
   private uint invId = 0;
   private uint itemId = 0;
   private Nullable<uint> qty;

   public InvoiceItem(uint sku, Nullable<uint> quantity)
   {
      itemId = sku;
      qty = quantity;
   }

   public Nullable<uint> Quantity
   {
      get { return qty; }
      set { qty = value; }
   }

   public uint InvoiceId
   {
      get { return invId; }
      set { invId = value; }
   }
}
// The attempt to compile the example displays the following output:
//    Type1.cs(13,23): warning CS3001: Argument type 'uint' is not CLS-compliant
//    Type1.cs(13,33): warning CS3001: Argument type 'uint?' is not CLS-compliant
//    Type1.cs(19,26): warning CS3003: Type of 'InvoiceItem.Quantity' is not CLS-compliant
//    Type1.cs(25,16): warning CS3003: Type of 'InvoiceItem.InvoiceId' is not CLS-compliant
<Assembly: CLSCompliant(True)>

Public Class InvoiceItem

    Private invId As UInteger = 0
    Private itemId As UInteger = 0
    Private qty AS Nullable(Of UInteger)

    Public Sub New(sku As UInteger, quantity As Nullable(Of UInteger))
        itemId = sku
        qty = quantity
    End Sub

    Public Property Quantity As Nullable(Of UInteger)
        Get
            Return qty
        End Get
        Set
            qty = value
        End Set
    End Property

    Public Property InvoiceId As UInteger
        Get
            Return invId
        End Get
        Set
            invId = value
        End Set
    End Property
End Class
' The attempt to compile the example displays output similar to the following:
'    Type1.vb(13) : warning BC40028: Type of parameter 'sku' is not CLS-compliant.
'    
'       Public Sub New(sku As UInteger, quantity As Nullable(Of UInteger))
'                      ~~~
'    Type1.vb(13) : warning BC40041: Type 'UInteger' is not CLS-compliant.
'    
'       Public Sub New(sku As UInteger, quantity As Nullable(Of UInteger))
'                                                               ~~~~~~~~
'    Type1.vb(18) : warning BC40041: Type 'UInteger' is not CLS-compliant.
'    
'       Public Property Quantity As Nullable(Of UInteger)
'                                               ~~~~~~~~
'    Type1.vb(27) : warning BC40027: Return type of function 'InvoiceId' is not CLS-compliant.
'    
'       Public Property InvoiceId As UInteger
'                       ~~~~~~~~~

コンパイラの警告を排除するには、 InvoiceItem パブリック インターフェイスの CLS 準拠以外の型を準拠型に置き換えます。

using System;

[assembly: CLSCompliant(true)]

public class InvoiceItem
{
   private uint invId = 0;
   private uint itemId = 0;
   private Nullable<int> qty;

   public InvoiceItem(int sku, Nullable<int> quantity)
   {
      if (sku <= 0)
         throw new ArgumentOutOfRangeException("The item number is zero or negative.");
      itemId = (uint) sku;

      qty = quantity;
   }

   public Nullable<int> Quantity
   {
      get { return qty; }
      set { qty = value; }
   }

   public int InvoiceId
   {
      get { return (int) invId; }
      set {
         if (value <= 0)
            throw new ArgumentOutOfRangeException("The invoice number is zero or negative.");
         invId = (uint) value; }
   }
}
<Assembly: CLSCompliant(True)>

Public Class InvoiceItem

    Private invId As UInteger = 0
    Private itemId As UInteger = 0
    Private qty AS Nullable(Of Integer)

    Public Sub New(sku As Integer, quantity As Nullable(Of Integer))
        If sku <= 0 Then
            Throw New ArgumentOutOfRangeException("The item number is zero or negative.")
        End If
        itemId = CUInt(sku)
        qty = quantity
    End Sub

    Public Property Quantity As Nullable(Of Integer)
        Get
            Return qty
        End Get
        Set
            qty = value
        End Set
    End Property

    Public Property InvoiceId As Integer
        Get
            Return CInt(invId)
        End Get
        Set
            invId = CUInt(value)
        End Set
    End Property
End Class

リストされている特定の型に加えて、一部のカテゴリの型は CLS に準拠していません。 これらには、アンマネージ ポインター型と関数ポインター型が含まれます。 次の例では、整数へのポインターを使用して整数の配列を作成するため、コンパイラ警告が生成されます。

using System;

[assembly: CLSCompliant(true)]

public class ArrayHelper
{
   unsafe public static Array CreateInstance(Type type, int* ptr, int items)
   {
      Array arr = Array.CreateInstance(type, items);
      int* addr = ptr;
      for (int ctr = 0; ctr < items; ctr++) {
          int value = *addr;
          arr.SetValue(value, ctr);
          addr++;
      }
      return arr;
   }
}
// The attempt to compile this example displays the following output:
//    UnmanagedPtr1.cs(8,57): warning CS3001: Argument type 'int*' is not CLS-compliant

CLS 準拠の抽象クラス (つまり、C# で abstract としてマークされているクラス、または Visual Basic で MustInherit としてマークされているクラス) の場合、クラスのすべてのメンバーも CLS に準拠している必要があります。

名前付け規則

一部のプログラミング言語は大文字と小文字が区別されるので、識別子 (名前空間、型、メンバーの名前など) は、大文字と小文字が違うだけで相違します。 小文字のマッピングが同じ場合、2 つの識別子は同等と見なされます。 次の C# の例では、 Personpersonの 2 つのパブリック クラスを定義します。 このクラスは大文字と小文字のみが異なるので、コンパイラは CLS 非準拠としてフラグを設定します。

using System;

[assembly: CLSCompliant(true)]

public class Person : person
{
}

public class person
{
}
// Compilation produces a compiler warning like the following:
//    Naming1.cs(11,14): warning CS3005: Identifier 'person' differing
//                       only in case is not CLS-compliant
//    Naming1.cs(6,14): (Location of symbol related to previous warning)

名前空間、型、メンバーの名前などのプログラミング言語識別子は、 Unicode 標準に準拠している必要があります。 これは、次のことを意味します。

  • 識別子の最初の文字には、Unicode 大文字、小文字、タイトル ケース文字、修飾子文字、その他の文字、または文字番号を指定できます。 Unicode 文字カテゴリの詳細については、 System.Globalization.UnicodeCategory 列挙型を参照してください。

  • 後続の文字は、最初の文字として任意のカテゴリから取得できます。また、非スペース マーク、スペース結合マーク、10 進数、コネクタ区切り記号、および書式設定コードを含めることもできます。

識別子を比較する前に、書式設定コードをフィルター処理し、識別子を Unicode 正規化フォーム C に変換する必要があります。これは、1 つの文字を複数の UTF-16 でエンコードされたコード単位で表すことができるためです。 Unicode 正規化形式 C で同じコード単位を生成する文字シーケンスは、CLS に準拠していません。 次の例では、文字 ÅNGSTROM SIGN (U+212B) で構成される という名前のプロパティと、文字 LATIN CAPITAL LETTER A WITH RING ABOVE (U+00C5) で構成される Å という名前の2つ目のプロパティを定義します。 C# コンパイラと Visual Basic コンパイラの両方で、ソース コードに CLS 準拠以外のフラグが設定されます。

public class Size
{
   private double a1;
   private double a2;

   public double Å
   {
       get { return a1; }
       set { a1 = value; }
   }

   public double Å
   {
       get { return a2; }
       set { a2 = value; }
   }
}
// Compilation produces a compiler warning like the following:
//    Naming2a.cs(16,18): warning CS3005: Identifier 'Size.Å' differing only in case is not
//            CLS-compliant
//    Naming2a.cs(10,18): (Location of symbol related to previous warning)
//    Naming2a.cs(18,8): warning CS3005: Identifier 'Size.Å.get' differing only in case is not
//            CLS-compliant
//    Naming2a.cs(12,8): (Location of symbol related to previous warning)
//    Naming2a.cs(19,8): warning CS3005: Identifier 'Size.Å.set' differing only in case is not
//            CLS-compliant
//    Naming2a.cs(13,8): (Location of symbol related to previous warning)
<Assembly: CLSCompliant(True)>
Public Class Size
    Private a1 As Double
    Private a2 As Double

    Public Property Å As Double
        Get
            Return a1
        End Get
        Set
            a1 = value
        End Set
    End Property

    Public Property Å As Double
        Get
            Return a2
        End Get
        Set
            a2 = value
        End Set
    End Property
End Class
' Compilation produces a compiler warning like the following:
'    Naming1.vb(9) : error BC30269: 'Public Property Å As Double' has multiple definitions
'     with identical signatures.
'    
'       Public Property Å As Double
'                       ~

特定のスコープ内のメンバー名 (アセンブリ内の名前空間、名前空間内の型、型内のメンバーなど) は、オーバーロードによって解決される名前を除き、一意である必要があります。 この要件は共通型システムの要件よりも厳しく、スコープ内の複数のメンバーが異なる種類のメンバーである限り、同じ名前を持つことができます (たとえば、1 つはメソッドで、1 つはフィールドです)。 特に、型メンバーの場合:

  • フィールドと入れ子になった型は、名前だけで識別されます。

  • 同じ名前を持つメソッド、プロパティ、およびイベントは、単なる戻り値の型以上の違いを持つ必要があります。

次の例は、メンバー名がスコープ内で一意である必要がある要件を示しています。 Converterという名前の 4 つのメンバーを含む Conversion という名前のクラスを定義します。 3 つはメソッドであり、1 つはプロパティです。 Int64 パラメーターを含むメソッドには一意の名前が付けられますが、戻り値がメンバーのシグネチャの一部と見なされないため、Int32 パラメーターを持つ 2 つのメソッドは指定されません。 Conversion プロパティは、オーバーロードされたメソッドと同じ名前を持つことができないため、この要件にも違反します。

using System;

[assembly: CLSCompliant(true)]

public class Converter
{
   public double Conversion(int number)
   {
      return (double) number;
   }

   public float Conversion(int number)
   {
      return (float) number;
   }

   public double Conversion(long number)
   {
      return (double) number;
   }

   public bool Conversion
   {
      get { return true; }
   }
}
// Compilation produces a compiler error like the following:
//    Naming3.cs(13,17): error CS0111: Type 'Converter' already defines a member called
//            'Conversion' with the same parameter types
//    Naming3.cs(8,18): (Location of symbol related to previous error)
//    Naming3.cs(23,16): error CS0102: The type 'Converter' already contains a definition for
//            'Conversion'
//    Naming3.cs(8,18): (Location of symbol related to previous error)
<Assembly: CLSCompliant(True)>

Public Class Converter
    Public Function Conversion(number As Integer) As Double
        Return CDbl(number)
    End Function

    Public Function Conversion(number As Integer) As Single
        Return CSng(number)
    End Function

    Public Function Conversion(number As Long) As Double
        Return CDbl(number)
    End Function

    Public ReadOnly Property Conversion As Boolean
        Get
            Return True
        End Get
    End Property
End Class
' Compilation produces a compiler error like the following:
'    Naming3.vb(8) : error BC30301: 'Public Function Conversion(number As Integer) As Double' 
'                    and 'Public Function Conversion(number As Integer) As Single' cannot 
'                    overload each other because they differ only by return types.
'    
'       Public Function Conversion(number As Integer) As Double
'                       ~~~~~~~~~~
'    Naming3.vb(20) : error BC30260: 'Conversion' is already declared as 'Public Function 
'                     Conversion(number As Integer) As Single' in this class.
'    
'       Public ReadOnly Property Conversion As Boolean
'                                ~~~~~~~~~~

個々の言語には一意のキーワードが含まれているため、共通言語ランタイムを対象とする言語は、キーワードと一致する識別子 (型名など) を参照するためのメカニズムも提供する必要があります。 たとえば、 case は C# と Visual Basic の両方のキーワードです。 ただし、次の Visual Basic コード例では、左右の中かっこを使用して、case キーワードと case という名前のクラスを明確に区別できます。 それ以外の場合、この例では"キーワードは識別子として有効ではありません" というエラー メッセージが生成され、コンパイルに失敗します。

Public Class [case]
    Private _id As Guid
    Private name As String

    Public Sub New(name As String)
        _id = Guid.NewGuid()
        Me.name = name
    End Sub

    Public ReadOnly Property ClientName As String
        Get
            Return name
        End Get
    End Property
End Class

次の C# の例では、case シンボルを使用して言語キーワードから識別子を区別することで、@ クラスをインスタンス化できます。 これを使用しない場合、C# コンパイラでは、"Type expected" と "Invalid expression term 'case' という 2 つのエラー メッセージが表示されます。

using System;

public class Example
{
   public static void Main()
   {
      @case c = new @case("John");
      Console.WriteLine(c.ClientName);
   }
}

型変換

共通言語仕様では、次の 2 つの変換演算子が定義されています。

  • op_Implicitは、データや精度を失わない拡大変換に使用されます。 たとえば、Decimal構造体には、整数型の値とop_Implicit値をChar値に変換するオーバーロードされたDecimal演算子が含まれています。

  • op_Explicitは、範囲や精度が小さい値への変換を行う「縮小変換」に使用され、これにより大きさや精度が失われる可能性があります。 たとえば、Decimal構造体には、op_Explicit値とDouble値をSingleに変換し、Decimal値を整数値、DecimalDouble、およびSingleに変換するオーバーロードされたChar演算子が含まれています。

ただし、すべての言語で演算子のオーバーロードやカスタム演算子の定義がサポートされているわけではありません。 これらの変換演算子を実装する場合は、変換を実行する別の方法も提供する必要があります。 From Xxx メソッドと ToXxx メソッドを指定することをお勧めします。

次の例では、CLS 準拠の暗黙的および明示的な変換を定義します。 符号なし倍精度浮動小数点数を表す UDouble クラスが作成されます。 UDoubleからDoubleへの暗黙的な変換、およびUDoubleからSingleへの明示的な変換、DoubleからUDoubleへのSingle、およびUDoubleへの明示的な変換が提供されます。 また、暗黙的な変換演算子の代わりに ToDouble メソッドを定義し、明示的な変換演算子の代わりに ToSingleFromDouble、および FromSingle メソッドを定義します。

using System;

public struct UDouble
{
   private double number;

   public UDouble(double value)
   {
      if (value < 0)
         throw new InvalidCastException("A negative value cannot be converted to a UDouble.");

      number = value;
   }

   public UDouble(float value)
   {
      if (value < 0)
         throw new InvalidCastException("A negative value cannot be converted to a UDouble.");

      number = value;
   }

   public static readonly UDouble MinValue = (UDouble) 0.0;
   public static readonly UDouble MaxValue = (UDouble) Double.MaxValue;

   public static explicit operator Double(UDouble value)
   {
      return value.number;
   }

   public static implicit operator Single(UDouble value)
   {
      if (value.number > (double) Single.MaxValue)
         throw new InvalidCastException("A UDouble value is out of range of the Single type.");

      return (float) value.number;
   }

   public static explicit operator UDouble(double value)
   {
      if (value < 0)
         throw new InvalidCastException("A negative value cannot be converted to a UDouble.");

      return new UDouble(value);
   }

   public static implicit operator UDouble(float value)
   {
      if (value < 0)
         throw new InvalidCastException("A negative value cannot be converted to a UDouble.");

      return new UDouble(value);
   }

   public static Double ToDouble(UDouble value)
   {
      return (Double) value;
   }

   public static float ToSingle(UDouble value)
   {
      return (float) value;
   }

   public static UDouble FromDouble(double value)
   {
      return new UDouble(value);
   }

   public static UDouble FromSingle(float value)
   {
      return new UDouble(value);
   }
}
Public Structure UDouble
    Private number As Double

    Public Sub New(value As Double)
        If value < 0 Then
            Throw New InvalidCastException("A negative value cannot be converted to a UDouble.")
        End If
        number = value
    End Sub

    Public Sub New(value As Single)
        If value < 0 Then
            Throw New InvalidCastException("A negative value cannot be converted to a UDouble.")
        End If
        number = value
    End Sub

    Public Shared ReadOnly MinValue As UDouble = CType(0.0, UDouble)
    Public Shared ReadOnly MaxValue As UDouble = Double.MaxValue

    Public Shared Widening Operator CType(value As UDouble) As Double
        Return value.number
    End Operator

    Public Shared Narrowing Operator CType(value As UDouble) As Single
        If value.number > CDbl(Single.MaxValue) Then
            Throw New InvalidCastException("A UDouble value is out of range of the Single type.")
        End If
        Return CSng(value.number)
    End Operator

    Public Shared Narrowing Operator CType(value As Double) As UDouble
        If value < 0 Then
            Throw New InvalidCastException("A negative value cannot be converted to a UDouble.")
        End If
        Return New UDouble(value)
    End Operator

    Public Shared Narrowing Operator CType(value As Single) As UDouble
        If value < 0 Then
            Throw New InvalidCastException("A negative value cannot be converted to a UDouble.")
        End If
        Return New UDouble(value)
    End Operator

    Public Shared Function ToDouble(value As UDouble) As Double
        Return CType(value, Double)
    End Function

    Public Shared Function ToSingle(value As UDouble) As Single
        Return CType(value, Single)
    End Function

    Public Shared Function FromDouble(value As Double) As UDouble
        Return New UDouble(value)
    End Function

    Public Shared Function FromSingle(value As Single) As UDouble
        Return New UDouble(value)
    End Function
End Structure

配列

CLS 準拠の配列は、次の規則に準拠しています。

  • 配列のすべての次元の下限は 0 である必要があります。 次の例では、下限が 1 の CLS 準拠でない配列を作成します。 CLSCompliantAttribute属性が存在するにもかかわらず、コンパイラは、Numbers.GetTenPrimes メソッドによって返される配列が CLS に準拠していないことを検出しません。

    [assembly: CLSCompliant(true)]
    
    public class Numbers
    {
       public static Array GetTenPrimes()
       {
          Array arr = Array.CreateInstance(typeof(Int32), new int[] {10}, new int[] {1});
          arr.SetValue(1, 1);
          arr.SetValue(2, 2);
          arr.SetValue(3, 3);
          arr.SetValue(5, 4);
          arr.SetValue(7, 5);
          arr.SetValue(11, 6);
          arr.SetValue(13, 7);
          arr.SetValue(17, 8);
          arr.SetValue(19, 9);
          arr.SetValue(23, 10);
    
          return arr;
       }
    }
    
    <Assembly: CLSCompliant(True)>
    
    Public Class Numbers
        Public Shared Function GetTenPrimes() As Array
            Dim arr As Array = Array.CreateInstance(GetType(Int32), {10}, {1})
            arr.SetValue(1, 1)
            arr.SetValue(2, 2)
            arr.SetValue(3, 3)
            arr.SetValue(5, 4)
            arr.SetValue(7, 5)
            arr.SetValue(11, 6)
            arr.SetValue(13, 7)
            arr.SetValue(17, 8)
            arr.SetValue(19, 9)
            arr.SetValue(23, 10)
    
            Return arr
        End Function
    End Class
    
  • すべての配列要素は、CLS に準拠した型で構成されている必要があります。 次の例では、CLS に準拠していない配列を返す 2 つのメソッドを定義します。 1 つ目は、 UInt32 値の配列を返します。 2 つ目は、Object値とInt32値を含むUInt32配列を返します。 コンパイラは、 UInt32 型のために最初の配列を非準拠として識別しますが、2 番目の配列に CLS 準拠以外の要素が含まれていることを認識できません。

    using System;
    
    [assembly: CLSCompliant(true)]
    
    public class Numbers
    {
       public static UInt32[] GetTenPrimes()
       {
          uint[] arr = { 1u, 2u, 3u, 5u, 7u, 11u, 13u, 17u, 19u };
          return arr;
       }
    
       public static Object[] GetFivePrimes()
       {
          Object[] arr = { 1, 2, 3, 5u, 7u };
          return arr;
       }
    }
    // Compilation produces a compiler warning like the following:
    //    Array2.cs(8,27): warning CS3002: Return type of 'Numbers.GetTenPrimes()' is not
    //            CLS-compliant
    
    <Assembly: CLSCompliant(True)>
    
    Public Class Numbers
        Public Shared Function GetTenPrimes() As UInt32()
            Return {1ui, 2ui, 3ui, 5ui, 7ui, 11ui, 13ui, 17ui, 19ui}
        End Function
    
        Public Shared Function GetFivePrimes() As Object()
            Dim arr() As Object = {1, 2, 3, 5ui, 7ui}
            Return arr
        End Function
    End Class
    ' Compilation produces a compiler warning like the following:
    '    warning BC40027: Return type of function 'GetTenPrimes' is not CLS-compliant.
    '    
    '       Public Shared Function GetTenPrimes() As UInt32()
    '                              ~~~~~~~~~~~~
    
  • 配列パラメーターを持つメソッドのオーバーロード解決は、それらが配列であるという事実とその要素型に基づいています。 このため、オーバーロードされた GetSquares メソッドの次の定義は CLS に準拠しています。

    using System;
    using System.Numerics;
    
    [assembly: CLSCompliant(true)]
    
    public class Numbers
    {
       public static byte[] GetSquares(byte[] numbers)
       {
          byte[] numbersOut = new byte[numbers.Length];
          for (int ctr = 0; ctr < numbers.Length; ctr++) {
             int square = ((int) numbers[ctr]) * ((int) numbers[ctr]);
             if (square <= Byte.MaxValue)
                numbersOut[ctr] = (byte) square;
             // If there's an overflow, assign MaxValue to the corresponding
             // element.
             else
                numbersOut[ctr] = Byte.MaxValue;
          }
          return numbersOut;
       }
    
       public static BigInteger[] GetSquares(BigInteger[] numbers)
       {
          BigInteger[] numbersOut = new BigInteger[numbers.Length];
          for (int ctr = 0; ctr < numbers.Length; ctr++)
             numbersOut[ctr] = numbers[ctr] * numbers[ctr];
    
          return numbersOut;
       }
    }
    
    Imports System.Numerics
    
    <Assembly: CLSCompliant(True)>
    
    Public Module Numbers
        Public Function GetSquares(numbers As Byte()) As Byte()
            Dim numbersOut(numbers.Length - 1) As Byte
            For ctr As Integer = 0 To numbers.Length - 1
                Dim square As Integer = (CInt(numbers(ctr)) * CInt(numbers(ctr)))
                If square <= Byte.MaxValue Then
                    numbersOut(ctr) = CByte(square)
                    ' If there's an overflow, assign MaxValue to the corresponding 
                    ' element.
                Else
                    numbersOut(ctr) = Byte.MaxValue
                End If
            Next
            Return numbersOut
        End Function
    
        Public Function GetSquares(numbers As BigInteger()) As BigInteger()
            Dim numbersOut(numbers.Length - 1) As BigInteger
            For ctr As Integer = 0 To numbers.Length - 1
                numbersOut(ctr) = numbers(ctr) * numbers(ctr)
            Next
            Return numbersOut
        End Function
    End Module
    

インターフェイス

CLS 準拠インターフェイスでは、プロパティ、イベント、および仮想メソッド (実装のないメソッド) を定義できます。 CLS 準拠インターフェイスには、次のいずれかを指定することはできません。

  • 静的メソッドまたは静的フィールド。 インターフェイスで静的メンバーを定義すると、C# コンパイラと Visual Basic コンパイラの両方でコンパイラ エラーが生成されます。

  • フィールド。 インターフェイスでフィールドを定義すると、C# コンパイラと Visual Basic コンパイラの両方でコンパイラ エラーが生成されます。

  • CLS に準拠していないメソッド。 たとえば、次のインターフェイス定義には、CLS 非準拠としてマークされているメソッド ( INumber.GetUnsigned) が含まれています。 この例では、コンパイラ警告が生成されます。

    using System;
    
    [assembly:CLSCompliant(true)]
    
    public interface INumber
    {
       int Length();
       [CLSCompliant(false)] ulong GetUnsigned();
    }
    // Attempting to compile the example displays output like the following:
    //    Interface2.cs(8,32): warning CS3010: 'INumber.GetUnsigned()': CLS-compliant interfaces
    //            must have only CLS-compliant members
    
    <Assembly: CLSCompliant(True)>
    
    Public Interface INumber
        Function Length As Integer
    
        <CLSCompliant(False)> Function GetUnsigned As ULong
    End Interface
    ' Attempting to compile the example displays output like the following:
    '    Interface2.vb(9) : warning BC40033: Non CLS-compliant 'function' is not allowed in a 
    '    CLS-compliant interface.
    '    
    '       <CLSCompliant(False)> Function GetUnsigned As ULong
    '                                      ~~~~~~~~~~~
    

    この規則により、CLS 準拠でないメンバーを実装するために CLS 準拠型は必要ありません。 CLS 準拠フレームワークが、CLS 準拠でないインターフェイスを実装するクラスを公開する場合は、CLS に準拠していないすべてのメンバーの具象実装も提供する必要があります。

CLS 準拠言語コンパイラでは、クラスが複数のインターフェイスで同じ名前とシグネチャを持つメンバーの個別の実装を提供できるようにする必要もあります。 C# と Visual Basic の両方で、同じ名前のメソッドのさまざまな実装を提供する 明示的なインターフェイス 実装がサポートされています。 Visual Basic では、 Implements キーワードもサポートされています。これにより、特定のメンバーが実装するインターフェイスとメンバーを明示的に指定できます。 次の例では、TemperatureインターフェイスとICelsius インターフェイスを明示的なインターフェイス実装として実装するIFahrenheit クラスを定義することで、このシナリオを示します。

using System;

[assembly: CLSCompliant(true)]

public interface IFahrenheit
{
   decimal GetTemperature();
}

public interface ICelsius
{
   decimal GetTemperature();
}

public class Temperature : ICelsius, IFahrenheit
{
   private decimal _value;

   public Temperature(decimal value)
   {
      // We assume that this is the Celsius value.
      _value = value;
   }

   decimal IFahrenheit.GetTemperature()
   {
      return _value * 9 / 5 + 32;
   }

   decimal ICelsius.GetTemperature()
   {
      return _value;
   }
}
public class Example
{
   public static void Main()
   {
      Temperature temp = new Temperature(100.0m);
      ICelsius cTemp = temp;
      IFahrenheit fTemp = temp;
      Console.WriteLine($"Temperature in Celsius: {cTemp.GetTemperature()} degrees");
      Console.WriteLine($"Temperature in Fahrenheit: {fTemp.GetTemperature()} degrees");
   }
}
// The example displays the following output:
//       Temperature in Celsius: 100.0 degrees
//       Temperature in Fahrenheit: 212.0 degrees
<Assembly: CLSCompliant(True)>

Public Interface IFahrenheit
    Function GetTemperature() As Decimal
End Interface

Public Interface ICelsius
    Function GetTemperature() As Decimal
End Interface

Public Class Temperature : Implements ICelsius, IFahrenheit
    Private _value As Decimal

    Public Sub New(value As Decimal)
        ' We assume that this is the Celsius value.
        _value = value
    End Sub

    Public Function GetFahrenheit() As Decimal _
           Implements IFahrenheit.GetTemperature
        Return _value * 9 / 5 + 32
    End Function

    Public Function GetCelsius() As Decimal _
           Implements ICelsius.GetTemperature
        Return _value
    End Function
End Class

Module Example
    Public Sub Main()
        Dim temp As New Temperature(100.0d)
        Console.WriteLine("Temperature in Celsius: {0} degrees",
                          temp.GetCelsius())
        Console.WriteLine("Temperature in Fahrenheit: {0} degrees",
                          temp.GetFahrenheit())
    End Sub
End Module
' The example displays the following output:
'       Temperature in Celsius: 100.0 degrees
'       Temperature in Fahrenheit: 212.0 degrees

列挙

CLS 準拠の列挙型は、次の規則に従う必要があります。

  • 列挙型の基になる型は、CLS 準拠の組み込み整数 (ByteInt16Int32、または Int64) である必要があります。 たとえば、次のコードは、基になる型が UInt32 列挙体を定義しようと試み、コンパイラ警告を生成します。

    using System;
    
    [assembly: CLSCompliant(true)]
    
    public enum Size : uint {
       Unspecified = 0,
       XSmall = 1,
       Small = 2,
       Medium = 3,
       Large = 4,
       XLarge = 5
    };
    
    public class Clothing
    {
       public string Name;
       public string Type;
       public string Size;
    }
    // The attempt to compile the example displays a compiler warning like the following:
    //    Enum3.cs(6,13): warning CS3009: 'Size': base type 'uint' is not CLS-compliant
    
    <Assembly: CLSCompliant(True)>
    
    Public Enum Size As UInt32
        Unspecified = 0
        XSmall = 1
        Small = 2
        Medium = 3
        Large = 4
        XLarge = 5
    End Enum
    
    Public Class Clothing
        Public Name As String
        Public Type As String
        Public Size As Size
    End Class
    ' The attempt to compile the example displays a compiler warning like the following:
    '    Enum3.vb(6) : warning BC40032: Underlying type 'UInt32' of Enum is not CLS-compliant.
    '    
    '    Public Enum Size As UInt32
    '                ~~~~
    
  • 列挙型には、Value__属性でマークされた FieldAttributes.RTSpecialName という名前の単一のインスタンス フィールドが必要です。 これにより、フィールド値を暗黙的に参照できます。

  • 列挙型には、列挙型自体の型と一致する型を持つリテラル静的フィールドが含まれます。 たとえば、StateState.Onの値を持つState.Off列挙体を定義する場合、State.OnState.Offはどちらも、型がStateリテラルの静的フィールドです。

  • 列挙型には次の 2 種類があります。

    • 相互に排他的な名前付き整数値のセットを表す列挙体。 この種類の列挙型は、カスタム属性が System.FlagsAttribute されていないことで示されます。

    • 名前のない値を生成するために組み合わせることができるビット フラグのセットを表す列挙体。 この種類の列挙型は、 System.FlagsAttribute カスタム属性の存在によって示されます。

    詳細については、 Enum 構造のドキュメントを参照してください。

  • 列挙体の値は、指定した値の範囲に限定されません。 つまり、列挙型の値の範囲は、基になる値の範囲です。 Enum.IsDefined メソッドを使用して、指定した値が列挙体のメンバーであるかどうかを判断できます。

一般的な型メンバー

共通言語仕様では、すべてのフィールドとメソッドに特定のクラスのメンバーとしてアクセスする必要があります。 したがって、グローバル静的フィールドとメソッド (つまり、型とは別に定義された静的フィールドまたはメソッド) は CLS に準拠していません。 ソース コードにグローバル フィールドまたはメソッドを含める場合、C# コンパイラと Visual Basic コンパイラの両方でコンパイラ エラーが生成されます。

共通言語仕様では、標準のマネージド呼び出し規則のみがサポートされています。 varargs キーワードでマークされた可変引数リストを持つアンマネージ呼び出し規則とメソッドはサポートされていません。 標準のマネージド呼び出し規則と互換性のある可変引数リストの場合は、 ParamArrayAttribute 属性または個々の言語の実装 (C# の params キーワードや Visual Basic の ParamArray キーワードなど) を使用します。

メンバーのアクセシビリティ

継承されたメンバーをオーバーライドしても、そのメンバーのアクセシビリティを変更することはできません。 たとえば、基底クラスのパブリック メソッドは、派生クラスのプライベート メソッドではオーバーライドできません。 1つの例外があります。それは、別のアセンブリの型によってオーバーライドされる、あるアセンブリのprotected internal (C#の場合) またはProtected Friend (Visual Basicの場合) メンバーです。 その場合、オーバーライドのアクセシビリティは Protected

次の例は、CLSCompliantAttribute 属性が true に設定され、Humanから派生したクラスである Animal が、Species プロパティのアクセシビリティをパブリックからプライベートに変更しようとしたときに生成されるエラーを示しています。 この例では、アクセシビリティがパブリックに変更されると、正常にコンパイルされます。

using System;

[assembly: CLSCompliant(true)]

public class Animal
{
   private string _species;

   public Animal(string species)
   {
      _species = species;
   }

   public virtual string Species
   {
      get { return _species; }
   }

   public override string ToString()
   {
      return _species;
   }
}

public class Human : Animal
{
   private string _name;

   public Human(string name) : base("Homo Sapiens")
   {
      _name = name;
   }

   public string Name
   {
      get { return _name; }
   }

   private override string Species
   {
      get { return base.Species; }
   }

   public override string ToString()
   {
      return _name;
   }
}

public class Example
{
   public static void Main()
   {
      Human p = new Human("John");
      Console.WriteLine(p.Species);
      Console.WriteLine(p.ToString());
   }
}
// The example displays the following output:
//    error CS0621: 'Human.Species': virtual or abstract members cannot be private
<Assembly: CLSCompliant(True)>

Public Class Animal
    Private _species As String

    Public Sub New(species As String)
        _species = species
    End Sub

    Public Overridable ReadOnly Property Species As String
        Get
            Return _species
        End Get
    End Property

    Public Overrides Function ToString() As String
        Return _species
    End Function
End Class

Public Class Human : Inherits Animal
    Private _name As String

    Public Sub New(name As String)
        MyBase.New("Homo Sapiens")
        _name = name
    End Sub

    Public ReadOnly Property Name As String
        Get
            Return _name
        End Get
    End Property

    Private Overrides ReadOnly Property Species As String
        Get
            Return MyBase.Species
        End Get
    End Property

    Public Overrides Function ToString() As String
        Return _name
    End Function
End Class

Public Module Example
    Public Sub Main()
        Dim p As New Human("John")
        Console.WriteLine(p.Species)
        Console.WriteLine(p.ToString())
    End Sub
End Module
' The example displays the following output:
'     'Private Overrides ReadOnly Property Species As String' cannot override 
'     'Public Overridable ReadOnly Property Species As String' because
'      they have different access levels.
' 
'         Private Overrides ReadOnly Property Species As String

メンバーのシグネチャ内の型は、そのメンバーがアクセス可能な場合は常にアクセス可能である必要があります。 たとえば、パブリック メンバーは、型がプライベート、保護、または内部であるパラメーターを含めることはできません。 次の例は、 StringWrapper クラス コンストラクターが、文字列値のラップ方法を決定する内部 StringOperationType 列挙値を公開したときに発生するコンパイラ エラーを示しています。

using System;
using System.Text;

public class StringWrapper
{
   string internalString;
   StringBuilder internalSB = null;
   bool useSB = false;

   public StringWrapper(StringOperationType type)
   {
      if (type == StringOperationType.Normal) {
         useSB = false;
      }
      else {
         useSB = true;
         internalSB = new StringBuilder();
      }
   }

   // The remaining source code...
}

internal enum StringOperationType { Normal, Dynamic }
// The attempt to compile the example displays the following output:
//    error CS0051: Inconsistent accessibility: parameter type
//            'StringOperationType' is less accessible than method
//            'StringWrapper.StringWrapper(StringOperationType)'
Imports System.Text

<Assembly: CLSCompliant(True)>

Public Class StringWrapper

    Dim internalString As String
    Dim internalSB As StringBuilder = Nothing
    Dim useSB As Boolean = False

    Public Sub New(type As StringOperationType)
        If type = StringOperationType.Normal Then
            useSB = False
        Else
            internalSB = New StringBuilder()
            useSB = True
        End If
    End Sub

    ' The remaining source code...
End Class

Friend Enum StringOperationType As Integer
    Normal = 0
    Dynamic = 1
End Enum
' The attempt to compile the example displays the following output:
'    error BC30909: 'type' cannot expose type 'StringOperationType'
'     outside the project through class 'StringWrapper'.
'    
'       Public Sub New(type As StringOperationType)
'                              ~~~~~~~~~~~~~~~~~~~

ジェネリック型とメンバー

入れ子になった型には、外側の型と同数以上のジェネリック パラメーターが常に含まれます。 これらは囲み型のジェネリックパラメーターと位置的に対応しています。 ジェネリック型には、新しいジェネリック パラメーターを含めることもできます。

含む型のジェネリック型パラメーターとその入れ子になった型の関係は、個々の言語の構文によって非表示になる場合があります。 次の例では、ジェネリック型 Outer<T> には、 Inner1AInner1B<U>の 2 つの入れ子になったクラスが含まれています。 各クラスがToStringから継承するObject.ToString() メソッドの呼び出しは、入れ子になった各クラスに、その包含クラスの型パラメーターが含まれていることを示しています。

using System;

[assembly:CLSCompliant(true)]

public class Outer<T>
{
   T value;

   public Outer(T value)
   {
      this.value = value;
   }

   public class Inner1A : Outer<T>
   {
      public Inner1A(T value) : base(value)
      {  }
   }

   public class Inner1B<U> : Outer<T>
   {
      U value2;

      public Inner1B(T value1, U value2) : base(value1)
      {
         this.value2 = value2;
      }
   }
}

public class Example
{
   public static void Main()
   {
      var inst1 = new Outer<String>("This");
      Console.WriteLine(inst1);

      var inst2 = new Outer<String>.Inner1A("Another");
      Console.WriteLine(inst2);

      var inst3 = new Outer<String>.Inner1B<int>("That", 2);
      Console.WriteLine(inst3);
   }
}
// The example displays the following output:
//       Outer`1[System.String]
//       Outer`1+Inner1A[System.String]
//       Outer`1+Inner1B`1[System.String,System.Int32]
<Assembly: CLSCompliant(True)>

Public Class Outer(Of T)
    Dim value As T

    Public Sub New(value As T)
        Me.value = value
    End Sub

    Public Class Inner1A : Inherits Outer(Of T)
        Public Sub New(value As T)
            MyBase.New(value)
        End Sub
    End Class

    Public Class Inner1B(Of U) : Inherits Outer(Of T)
        Dim value2 As U

        Public Sub New(value1 As T, value2 As U)
            MyBase.New(value1)
            Me.value2 = value2
        End Sub
    End Class
End Class

Public Module Example
    Public Sub Main()
        Dim inst1 As New Outer(Of String)("This")
        Console.WriteLine(inst1)

        Dim inst2 As New Outer(Of String).Inner1A("Another")
        Console.WriteLine(inst2)

        Dim inst3 As New Outer(Of String).Inner1B(Of Integer)("That", 2)
        Console.WriteLine(inst3)
    End Sub
End Module
' The example displays the following output:
'       Outer`1[System.String]
'       Outer`1+Inner1A[System.String]
'       Outer`1+Inner1B`1[System.String,System.Int32]

ジェネリック型名はフォーム 名'n でエンコードされます。 name は型名、' は文字リテラル、 n は型で宣言されたパラメーターの数、または入れ子になったジェネリック型の場合は、新しく導入された型パラメーターの数です。 ジェネリック型名のこのエンコードは、主にリフレクションを使用してライブラリ内の CLS に準拠するジェネリック型にアクセスする開発者にとって重要です。

制約がジェネリック型に適用される場合、制約として使用される型も CLS に準拠している必要があります。 次の例では、CLS に準拠していない BaseClass という名前のクラスと、型パラメーターが BaseCollection から派生する必要がある BaseClass という名前のジェネリック クラスを定義します。 ただし、 BaseClass は CLS に準拠していないため、コンパイラは警告を出力します。

using System;

[assembly:CLSCompliant(true)]

[CLSCompliant(false)] public class BaseClass
{}

public class BaseCollection<T> where T : BaseClass
{}
// Attempting to compile the example displays the following output:
//    warning CS3024: Constraint type 'BaseClass' is not CLS-compliant
<Assembly: CLSCompliant(True)>

<CLSCompliant(False)> Public Class BaseClass
End Class


Public Class BaseCollection(Of T As BaseClass)
End Class
' Attempting to compile the example displays the following output:
'    warning BC40040: Generic parameter constraint type 'BaseClass' is not 
'    CLS-compliant.
'    
'    Public Class BaseCollection(Of T As BaseClass)
'                                        ~~~~~~~~~

ジェネリック型がジェネリック基本型から派生している場合は、基本型の制約も満たされるように、制約を再宣言する必要があります。 次の例では、任意の数値型を表すことができる Number<T> を定義します。 また、浮動小数点値を表す FloatingPoint<T> クラスも定義します。 ただし、ソース コードは、Number<T>FloatingPoint<T> (T は値型である必要があります) に制約を適用しないため、コンパイルに失敗します。

using System;

[assembly:CLSCompliant(true)]

public class Number<T> where T : struct
{
   // use Double as the underlying type, since its range is a superset of
   // the ranges of all numeric types except BigInteger.
   protected double number;

   public Number(T value)
   {
      try {
         this.number = Convert.ToDouble(value);
      }
      catch (OverflowException e) {
         throw new ArgumentException("value is too large.", e);
      }
      catch (InvalidCastException e) {
         throw new ArgumentException("The value parameter is not numeric.", e);
      }
   }

   public T Add(T value)
   {
      return (T) Convert.ChangeType(number + Convert.ToDouble(value), typeof(T));
   }

   public T Subtract(T value)
   {
      return (T) Convert.ChangeType(number - Convert.ToDouble(value), typeof(T));
   }
}

public class FloatingPoint<T> : Number<T>
{
   public FloatingPoint(T number) : base(number)
   {
      if (typeof(float) == number.GetType() ||
          typeof(double) == number.GetType() ||
          typeof(decimal) == number.GetType())
         this.number = Convert.ToDouble(number);
      else
         throw new ArgumentException("The number parameter is not a floating-point number.");
   }
}
// The attempt to compile the example displays the following output:
//       error CS0453: The type 'T' must be a non-nullable value type in
//               order to use it as parameter 'T' in the generic type or method 'Number<T>'
<Assembly: CLSCompliant(True)>

Public Class Number(Of T As Structure)
    ' Use Double as the underlying type, since its range is a superset of
    ' the ranges of all numeric types except BigInteger.
    Protected number As Double

    Public Sub New(value As T)
        Try
            Me.number = Convert.ToDouble(value)
        Catch e As OverflowException
            Throw New ArgumentException("value is too large.", e)
        Catch e As InvalidCastException
            Throw New ArgumentException("The value parameter is not numeric.", e)
        End Try
    End Sub

    Public Function Add(value As T) As T
        Return CType(Convert.ChangeType(number + Convert.ToDouble(value), GetType(T)), T)
    End Function

    Public Function Subtract(value As T) As T
        Return CType(Convert.ChangeType(number - Convert.ToDouble(value), GetType(T)), T)
    End Function
End Class

Public Class FloatingPoint(Of T) : Inherits Number(Of T)
    Public Sub New(number As T)
        MyBase.New(number)
        If TypeOf number Is Single Or
                 TypeOf number Is Double Or
                 TypeOf number Is Decimal Then
            Me.number = Convert.ToDouble(number)
        Else
            throw new ArgumentException("The number parameter is not a floating-point number.")
        End If
    End Sub
End Class
' The attempt to compile the example displays the following output:
'    error BC32105: Type argument 'T' does not satisfy the 'Structure'
'    constraint for type parameter 'T'.
'    
'    Public Class FloatingPoint(Of T) : Inherits Number(Of T)
'                                                          ~

この例では、制約が FloatingPoint<T> クラスに追加されると、正常にコンパイルされます。

using System;

[assembly:CLSCompliant(true)]

public class Number<T> where T : struct
{
   // use Double as the underlying type, since its range is a superset of
   // the ranges of all numeric types except BigInteger.
   protected double number;

   public Number(T value)
   {
      try {
         this.number = Convert.ToDouble(value);
      }
      catch (OverflowException e) {
         throw new ArgumentException("value is too large.", e);
      }
      catch (InvalidCastException e) {
         throw new ArgumentException("The value parameter is not numeric.", e);
      }
   }

   public T Add(T value)
   {
      return (T) Convert.ChangeType(number + Convert.ToDouble(value), typeof(T));
   }

   public T Subtract(T value)
   {
      return (T) Convert.ChangeType(number - Convert.ToDouble(value), typeof(T));
   }
}

public class FloatingPoint<T> : Number<T> where T : struct
{
   public FloatingPoint(T number) : base(number)
   {
      if (typeof(float) == number.GetType() ||
          typeof(double) == number.GetType() ||
          typeof(decimal) == number.GetType())
         this.number = Convert.ToDouble(number);
      else
         throw new ArgumentException("The number parameter is not a floating-point number.");
   }
}
<Assembly: CLSCompliant(True)>

Public Class Number(Of T As Structure)
    ' Use Double as the underlying type, since its range is a superset of
    ' the ranges of all numeric types except BigInteger.
    Protected number As Double

    Public Sub New(value As T)
        Try
            Me.number = Convert.ToDouble(value)
        Catch e As OverflowException
            Throw New ArgumentException("value is too large.", e)
        Catch e As InvalidCastException
            Throw New ArgumentException("The value parameter is not numeric.", e)
        End Try
    End Sub

    Public Function Add(value As T) As T
        Return CType(Convert.ChangeType(number + Convert.ToDouble(value), GetType(T)), T)
    End Function

    Public Function Subtract(value As T) As T
        Return CType(Convert.ChangeType(number - Convert.ToDouble(value), GetType(T)), T)
    End Function
End Class

Public Class FloatingPoint(Of T As Structure) : Inherits Number(Of T)
    Public Sub New(number As T)
        MyBase.New(number)
        If TypeOf number Is Single Or
                 TypeOf number Is Double Or
                 TypeOf number Is Decimal Then
            Me.number = Convert.ToDouble(number)
        Else
            throw new ArgumentException("The number parameter is not a floating-point number.")
        End If
    End Sub
End Class

共通言語仕様では、入れ子になった型とプロテクト メンバーに対して従来のインスタンス化ごとのモデルが適用されます。 オープン ジェネリック型は、入れ子になった保護されたジェネリック型の特定のインスタンス化を含むシグネチャを持つフィールドまたはメンバーを公開できません。 ジェネリック基底クラスまたはインターフェイスの特定のインスタンス化を拡張する非ジェネリック型は、入れ子になった保護されたジェネリック型の別のインスタンス化を含むシグネチャを持つフィールドまたはメンバーを公開できません。

次の例では、ジェネリック型、 C1<T> (Visual Basic では C1(Of T) )、保護されたクラス 、 C1<T>.N (または Visual Basic では C1(Of T).N ) を定義します。 C1<T> には、 M1M2の 2 つのメソッドがあります。 ただし、M1は C1C1<int>.NTC1(Of Integer).N (または <) から> (またはC1(Of T)) オブジェクトを返そうとするため、CLS に準拠していません。 2 番目のクラス ( C2) は、 C1<long> (または C1(Of Long)) から派生します。 M3M4の 2 つのメソッドがあります。 M3は CLS に準拠していません。これは、C1<int>.NのサブクラスからC1(Of Integer).N (またはC1<long>) オブジェクトを返そうとするためです。 言語コンパイラは、さらに制限が厳しい場合があります。 この例では、visual Basic は、 M4をコンパイルしようとしたときにエラーを表示します。

using System;

[assembly:CLSCompliant(true)]

public class C1<T>
{
   protected class N { }

   protected void M1(C1<int>.N n) { } // Not CLS-compliant - C1<int>.N not
                                      // accessible from within C1<T> in all
                                      // languages
   protected void M2(C1<T>.N n) { }   // CLS-compliant – C1<T>.N accessible
                                      // inside C1<T>
}

public class C2 : C1<long>
{
   protected void M3(C1<int>.N n) { }  // Not CLS-compliant – C1<int>.N is not
                                       // accessible in C2 (extends C1<long>)

   protected void M4(C1<long>.N n) { } // CLS-compliant, C1<long>.N is
                                       // accessible in C2 (extends C1<long>)
}
// Attempting to compile the example displays output like the following:
//       Generics4.cs(9,22): warning CS3001: Argument type 'C1<int>.N' is not CLS-compliant
//       Generics4.cs(18,22): warning CS3001: Argument type 'C1<int>.N' is not CLS-compliant
<Assembly: CLSCompliant(True)>

Public Class C1(Of T)
    Protected Class N
    End Class

    Protected Sub M1(n As C1(Of Integer).N)   ' Not CLS-compliant - C1<int>.N not
        ' accessible from within C1(Of T) in all
    End Sub                                   ' languages


    Protected Sub M2(n As C1(Of T).N)     ' CLS-compliant – C1(Of T).N accessible
    End Sub                               ' inside C1(Of T)
End Class

Public Class C2 : Inherits C1(Of Long)
    Protected Sub M3(n As C1(Of Integer).N)   ' Not CLS-compliant – C1(Of Integer).N is not
    End Sub                                   ' accessible in C2 (extends C1(Of Long))

    Protected Sub M4(n As C1(Of Long).N)
    End Sub
End Class
' Attempting to compile the example displays output like the following:
'    error BC30508: 'n' cannot expose type 'C1(Of Integer).N' in namespace 
'    '<Default>' through class 'C1'.
'    
'       Protected Sub M1(n As C1(Of Integer).N)   ' Not CLS-compliant - C1<int>.N not
'                             ~~~~~~~~~~~~~~~~
'    error BC30389: 'C1(Of T).N' is not accessible in this context because 
'    it is 'Protected'.
'    
'       Protected Sub M3(n As C1(Of Integer).N)   ' Not CLS-compliant - C1(Of Integer).N is not
'    
'                             ~~~~~~~~~~~~~~~~
'    
'    error BC30389: 'C1(Of T).N' is not accessible in this context because it is 'Protected'.
'    
'       Protected Sub M4(n As C1(Of Long).N)  
'                             ~~~~~~~~~~~~~

コンストラクター

CLS 準拠のクラスと構造体のコンストラクターは、次の規則に従う必要があります。

  • 派生クラスのコンストラクターは、継承されたインスタンス データにアクセスする前に、その基底クラスのインスタンス コンストラクターを呼び出す必要があります。 この要件は、基底クラスのコンストラクターが派生クラスによって継承されないためです。 この規則は、直接継承をサポートしない構造体には適用されません。

    通常、次の例に示すように、コンパイラは CLS 準拠とは無関係にこの規則を適用します。 Doctor クラスから派生したPerson クラスが作成されますが、Doctor クラスは、継承されたインスタンス フィールドを初期化するためにPerson クラス コンストラクターを呼び出しません。

    using System;
    
    [assembly: CLSCompliant(true)]
    
    public class Person
    {
       private string fName, lName, _id;
    
       public Person(string firstName, string lastName, string id)
       {
          if (String.IsNullOrEmpty(firstName + lastName))
             throw new ArgumentNullException("Either a first name or a last name must be provided.");
    
          fName = firstName;
          lName = lastName;
          _id = id;
       }
    
       public string FirstName
       {
          get { return fName; }
       }
    
       public string LastName
       {
          get { return lName; }
       }
    
       public string Id
       {
          get { return _id; }
       }
    
       public override string ToString()
       {
          return String.Format("{0}{1}{2}", fName,
                               String.IsNullOrEmpty(fName) ?  "" : " ",
                               lName);
       }
    }
    
    public class Doctor : Person
    {
       public Doctor(string firstName, string lastName, string id)
       {
       }
    
       public override string ToString()
       {
          return "Dr. " + base.ToString();
       }
    }
    // Attempting to compile the example displays output like the following:
    //    ctor1.cs(45,11): error CS1729: 'Person' does not contain a constructor that takes 0
    //            arguments
    //    ctor1.cs(10,11): (Location of symbol related to previous error)
    
    <Assembly: CLSCompliant(True)>
    
    Public Class Person
        Private fName, lName, _id As String
    
        Public Sub New(firstName As String, lastName As String, id As String)
            If String.IsNullOrEmpty(firstName + lastName) Then
                Throw New ArgumentNullException("Either a first name or a last name must be provided.")
            End If
    
            fName = firstName
            lName = lastName
            _id = id
        End Sub
    
        Public ReadOnly Property FirstName As String
            Get
                Return fName
            End Get
        End Property
    
        Public ReadOnly Property LastName As String
            Get
                Return lName
            End Get
        End Property
    
        Public ReadOnly Property Id As String
            Get
                Return _id
            End Get
        End Property
    
        Public Overrides Function ToString() As String
            Return String.Format("{0}{1}{2}", fName,
                                 If(String.IsNullOrEmpty(fName), "", " "),
                                 lName)
        End Function
    End Class
    
    Public Class Doctor : Inherits Person
        Public Sub New(firstName As String, lastName As String, id As String)
        End Sub
    
        Public Overrides Function ToString() As String
            Return "Dr. " + MyBase.ToString()
        End Function
    End Class
    ' Attempting to compile the example displays output like the following:
    '    Ctor1.vb(46) : error BC30148: First statement of this 'Sub New' must be a call 
    '    to 'MyBase.New' or 'MyClass.New' because base class 'Person' of 'Doctor' does 
    '    not have an accessible 'Sub New' that can be called with no arguments.
    '    
    '       Public Sub New()
    '                  ~~~
    
  • オブジェクト コンストラクターは、オブジェクトを作成する以外は呼び出せません。 さらに、オブジェクトを 2 回初期化することはできません。 たとえば、これは、 Object.MemberwiseClone メソッドと逆シリアル化メソッドがコンストラクターを呼び出してはならないことを意味します。

特性

CLS 準拠型のプロパティは、次の規則に従う必要があります。

  • プロパティには setter、getter、またはこの両方が必ず必要です。 アセンブリでは、これらは特殊なメソッドとして実装されます。これは、アセンブリのメタデータでget_としてマークされた個別のメソッド (getter は set_、setter は SpecialName) として表示されることを意味します。 C# および Visual Basic コンパイラでは、 CLSCompliantAttribute 属性を適用する必要なく、この規則が自動的に適用されます。

  • プロパティの型は、プロパティ getter の戻り値の型であり、セッターの最後の引数です。 これらの型は CLS に準拠している必要があり、引数を参照によってプロパティに割り当てることはできません (つまり、マネージド ポインターにすることはできません)。

  • プロパティにゲッターとセッターの両方がある場合は、両方とも仮想インスタンス、静的インスタンス、または両方のインスタンスである必要があります。 C# コンパイラと Visual Basic コンパイラは、プロパティ定義構文を使用してこの規則を自動的に適用します。

イベント

イベントは、その名前とその型によって定義されます。 イベントタイプは、イベントを示すために使用されるデリゲートです。 たとえば、 AppDomain.AssemblyResolve イベントの種類は ResolveEventHandler です。 イベント自体に加えて、イベント名に基づく名前を持つ 3 つのメソッドによってイベントの実装が提供され、アセンブリのメタデータに SpecialName としてマークされます。

  • add_ EventName という名前のイベント ハンドラーを追加するためのメソッド。 たとえば、 AppDomain.AssemblyResolve イベントのイベント サブスクリプション メソッドには、 add_AssemblyResolveという名前が付けられます。

  • remove_ EventName という名前のイベント ハンドラーを削除するメソッド。 たとえば、 AppDomain.AssemblyResolve イベントの削除メソッドの名前は remove_AssemblyResolve です。

  • イベントが発生したことを示すメソッド 。名前は raise_EventName です

共通言語仕様のイベントに関する規則のほとんどは、言語コンパイラによって実装され、コンポーネント開発者には透過的です。

イベントを追加、削除、および発生させるメソッドには、同じアクセシビリティが必要です。 これらはすべて静的、インスタンス、または仮想である必要もあります。 イベントを追加および削除するためのメソッドには、イベント デリゲート型の型を持つ 1 つのパラメーターがあります。 add メソッドと remove メソッドの両方が存在するか、両方が存在しない必要があります。

次の例では、 Temperature という名前の CLS 準拠クラスを定義します。このクラスでは、2 つの読み取り値間の温度の変化がしきい値と等しいか、しきい値を超えた場合に TemperatureChanged イベントが発生します。 Temperature クラスは、イベント ハンドラーを選択的に実行できるように、raise_TemperatureChanged メソッドを明示的に定義します。

using System;
using System.Collections;
using System.Collections.Generic;

[assembly: CLSCompliant(true)]

public class TemperatureChangedEventArgs : EventArgs
{
   private Decimal originalTemp;
   private Decimal newTemp;
   private DateTimeOffset when;

   public TemperatureChangedEventArgs(Decimal original, Decimal @new, DateTimeOffset time)
   {
      originalTemp = original;
      newTemp = @new;
      when = time;
   }

   public Decimal OldTemperature
   {
      get { return originalTemp; }
   }

   public Decimal CurrentTemperature
   {
      get { return newTemp; }
   }

   public DateTimeOffset Time
   {
      get { return when; }
   }
}

public delegate void TemperatureChanged(Object sender, TemperatureChangedEventArgs e);

public class Temperature
{
   private struct TemperatureInfo
   {
      public Decimal Temperature;
      public DateTimeOffset Recorded;
   }

   public event TemperatureChanged TemperatureChanged;

   private Decimal previous;
   private Decimal current;
   private Decimal tolerance;
   private List<TemperatureInfo> tis = new List<TemperatureInfo>();

   public Temperature(Decimal temperature, Decimal tolerance)
   {
      current = temperature;
      TemperatureInfo ti = new TemperatureInfo();
      ti.Temperature = temperature;
      tis.Add(ti);
      ti.Recorded = DateTimeOffset.UtcNow;
      this.tolerance = tolerance;
   }

   public Decimal CurrentTemperature
   {
      get { return current; }
      set {
         TemperatureInfo ti = new TemperatureInfo();
         ti.Temperature = value;
         ti.Recorded = DateTimeOffset.UtcNow;
         previous = current;
         current = value;
         if (Math.Abs(current - previous) >= tolerance)
            raise_TemperatureChanged(new TemperatureChangedEventArgs(previous, current, ti.Recorded));
      }
   }

   public void raise_TemperatureChanged(TemperatureChangedEventArgs eventArgs)
   {
      if (TemperatureChanged == null)
         return;

      foreach (TemperatureChanged d in TemperatureChanged.GetInvocationList()) {
         if (d.Method.Name.Contains("Duplicate"))
            Console.WriteLine("Duplicate event handler; event handler not executed.");
         else
            d.Invoke(this, eventArgs);
      }
   }
}

public class Example
{
   public Temperature temp;

   public static void Main()
   {
      Example ex = new Example();
   }

   public Example()
   {
      temp = new Temperature(65, 3);
      temp.TemperatureChanged += this.TemperatureNotification;
      RecordTemperatures();
      Example ex = new Example(temp);
      ex.RecordTemperatures();
   }

   public Example(Temperature t)
   {
      temp = t;
      RecordTemperatures();
   }

   public void RecordTemperatures()
   {
      temp.TemperatureChanged += this.DuplicateTemperatureNotification;
      temp.CurrentTemperature = 66;
      temp.CurrentTemperature = 63;
   }

   internal void TemperatureNotification(Object sender, TemperatureChangedEventArgs e)
   {
      Console.WriteLine($"Notification 1: The temperature changed from {e.OldTemperature} to {e.CurrentTemperature}");
   }

   public void DuplicateTemperatureNotification(Object sender, TemperatureChangedEventArgs e)
   {
      Console.WriteLine($"Notification 2: The temperature changed from {e.OldTemperature} to {e.CurrentTemperature}");
   }
}
Imports System.Collections
Imports System.Collections.Generic

<Assembly: CLSCompliant(True)>

Public Class TemperatureChangedEventArgs : Inherits EventArgs
    Private originalTemp As Decimal
    Private newTemp As Decimal
    Private [when] As DateTimeOffset

    Public Sub New(original As Decimal, [new] As Decimal, [time] As DateTimeOffset)
        originalTemp = original
        newTemp = [new]
        [when] = [time]
    End Sub

    Public ReadOnly Property OldTemperature As Decimal
        Get
            Return originalTemp
        End Get
    End Property

    Public ReadOnly Property CurrentTemperature As Decimal
        Get
            Return newTemp
        End Get
    End Property

    Public ReadOnly Property [Time] As DateTimeOffset
        Get
            Return [when]
        End Get
    End Property
End Class

Public Delegate Sub TemperatureChanged(sender As Object, e As TemperatureChangedEventArgs)

Public Class Temperature
    Private Structure TemperatureInfo
        Dim Temperature As Decimal
        Dim Recorded As DateTimeOffset
    End Structure

    Public Event TemperatureChanged As TemperatureChanged

    Private previous As Decimal
    Private current As Decimal
    Private tolerance As Decimal
    Private tis As New List(Of TemperatureInfo)

    Public Sub New(temperature As Decimal, tolerance As Decimal)
        current = temperature
        Dim ti As New TemperatureInfo()
        ti.Temperature = temperature
        ti.Recorded = DateTimeOffset.UtcNow
        tis.Add(ti)
        Me.tolerance = tolerance
    End Sub

    Public Property CurrentTemperature As Decimal
        Get
            Return current
        End Get
        Set
            Dim ti As New TemperatureInfo
            ti.Temperature = value
            ti.Recorded = DateTimeOffset.UtcNow
            previous = current
            current = value
            If Math.Abs(current - previous) >= tolerance Then
                raise_TemperatureChanged(New TemperatureChangedEventArgs(previous, current, ti.Recorded))
            End If
        End Set
    End Property

    Public Sub raise_TemperatureChanged(eventArgs As TemperatureChangedEventArgs)
        If TemperatureChangedEvent Is Nothing Then Exit Sub

        Dim ListenerList() As System.Delegate = TemperatureChangedEvent.GetInvocationList()
        For Each d As TemperatureChanged In TemperatureChangedEvent.GetInvocationList()
            If d.Method.Name.Contains("Duplicate") Then
                Console.WriteLine("Duplicate event handler; event handler not executed.")
            Else
                d.Invoke(Me, eventArgs)
            End If
        Next
    End Sub
End Class

Public Class Example
    Public WithEvents temp As Temperature

    Public Shared Sub Main()
        Dim ex As New Example()
    End Sub

    Public Sub New()
        temp = New Temperature(65, 3)
        RecordTemperatures()
        Dim ex As New Example(temp)
        ex.RecordTemperatures()
    End Sub

    Public Sub New(t As Temperature)
        temp = t
        RecordTemperatures()
    End Sub

    Public Sub RecordTemperatures()
        temp.CurrentTemperature = 66
        temp.CurrentTemperature = 63

    End Sub

    Friend Shared Sub TemperatureNotification(sender As Object, e As TemperatureChangedEventArgs) _
           Handles temp.TemperatureChanged
        Console.WriteLine("Notification 1: The temperature changed from {0} to {1}", e.OldTemperature, e.CurrentTemperature)
    End Sub

    Friend Shared Sub DuplicateTemperatureNotification(sender As Object, e As TemperatureChangedEventArgs) _
           Handles temp.TemperatureChanged
        Console.WriteLine("Notification 2: The temperature changed from {0} to {1}", e.OldTemperature, e.CurrentTemperature)
    End Sub
End Class

オーバーロード

共通言語仕様では、オーバーロードされたメンバーに次の要件が課されます。

  • メンバーは、パラメーターの数と任意のパラメーターの型に基づいてオーバーロードできます。 呼び出し規則、戻り値の型、メソッドまたはそのパラメーターに適用されるカスタム修飾子、およびパラメーターが値または参照によって渡されるかどうかは、オーバーロードを区別する際には考慮されません。 例については、「 名前付け規則 」セクションのスコープ内で名前を一意にする必要がある要件のコードを参照してください。

  • オーバーロードできるのはプロパティとメソッドだけです。 フィールドとイベントをオーバーロードすることはできません。

  • ジェネリック メソッドは、ジェネリック パラメーターの数に基づいてオーバーロードできます。

op_Explicit演算子とop_Implicit演算子は、戻り値がオーバーロード解決のメソッド シグネチャの一部と見なされないルールの例外です。 これら 2 つの演算子は、パラメーターと戻り値の両方に基づいてオーバーロードできます。

例外

例外オブジェクトは、 System.Exception から派生するか、 System.Exceptionから派生した別の型から派生する必要があります。 次の例は、例外処理に ErrorClass という名前のカスタム クラスが使用されたときに発生するコンパイラ エラーを示しています。

using System;

[assembly: CLSCompliant(true)]

public class ErrorClass
{
   string msg;

   public ErrorClass(string errorMessage)
   {
      msg = errorMessage;
   }

   public string Message
   {
      get { return msg; }
   }
}

public static class StringUtilities
{
   public static string[] SplitString(this string value, int index)
   {
      if (index < 0 | index > value.Length) {
         ErrorClass badIndex = new ErrorClass("The index is not within the string.");
         throw badIndex;
      }
      string[] retVal = { value.Substring(0, index - 1),
                          value.Substring(index) };
      return retVal;
   }
}
// Compilation produces a compiler error like the following:
//    Exceptions1.cs(26,16): error CS0155: The type caught or thrown must be derived from
//            System.Exception
Imports System.Runtime.CompilerServices

<Assembly: CLSCompliant(True)>

Public Class ErrorClass
    Dim msg As String

    Public Sub New(errorMessage As String)
        msg = errorMessage
    End Sub

    Public ReadOnly Property Message As String
        Get
            Return msg
        End Get
    End Property
End Class

Public Module StringUtilities
    <Extension()> Public Function SplitString(value As String, index As Integer) As String()
        If index < 0 Or index > value.Length Then
            Dim BadIndex As New ErrorClass("The index is not within the string.")
            Throw BadIndex
        End If
        Dim retVal() As String = {value.Substring(0, index - 1),
                                   value.Substring(index)}
        Return retVal
    End Function
End Module
' Compilation produces a compiler error like the following:
'    Exceptions1.vb(27) : error BC30665: 'Throw' operand must derive from 'System.Exception'.
'    
'             Throw BadIndex
'             ~~~~~~~~~~~~~~

このエラーを修正するには、 ErrorClass クラスが System.Exceptionから継承する必要があります。 また、Message プロパティはオーバーライドする必要があります。 次の例では、これらのエラーを修正して、CLS に準拠する ErrorClass クラスを定義します。

using System;

[assembly: CLSCompliant(true)]

public class ErrorClass : Exception
{
   string msg;

   public ErrorClass(string errorMessage)
   {
      msg = errorMessage;
   }

   public override string Message
   {
      get { return msg; }
   }
}

public static class StringUtilities
{
   public static string[] SplitString(this string value, int index)
   {
      if (index < 0 | index > value.Length) {
         ErrorClass badIndex = new ErrorClass("The index is not within the string.");
         throw badIndex;
      }
      string[] retVal = { value.Substring(0, index - 1),
                          value.Substring(index) };
      return retVal;
   }
}
Imports System.Runtime.CompilerServices

<Assembly: CLSCompliant(True)>

Public Class ErrorClass : Inherits Exception
    Dim msg As String

    Public Sub New(errorMessage As String)
        msg = errorMessage
    End Sub

    Public Overrides ReadOnly Property Message As String
        Get
            Return msg
        End Get
    End Property
End Class

Public Module StringUtilities
    <Extension()> Public Function SplitString(value As String, index As Integer) As String()
        If index < 0 Or index > value.Length Then
            Dim BadIndex As New ErrorClass("The index is not within the string.")
            Throw BadIndex
        End If
        Dim retVal() As String = {value.Substring(0, index - 1),
                                   value.Substring(index)}
        Return retVal
    End Function
End Module

属性

.NET アセンブリでは、カスタム属性は、カスタム属性を格納し、アセンブリ、型、メンバー、メソッド パラメーターなどのプログラミング オブジェクトに関するメタデータを取得するための拡張可能なメカニズムを提供します。 カスタム属性は 、System.Attribute から派生するか、 System.Attributeから派生した型から派生する必要があります。

次の例は、この規則に違反しています。 NumericAttributeから派生しないSystem.Attribute クラスを定義します。 コンパイラ エラーは、クラスが定義されている場合ではなく、CLS に準拠していない属性が適用されている場合にのみ発生します。

using System;

[assembly: CLSCompliant(true)]

[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Struct)]
public class NumericAttribute
{
   private bool _isNumeric;

   public NumericAttribute(bool isNumeric)
   {
      _isNumeric = isNumeric;
   }

   public bool IsNumeric
   {
      get { return _isNumeric; }
   }
}

[Numeric(true)] public struct UDouble
{
   double Value;
}
// Compilation produces a compiler error like the following:
//    Attribute1.cs(22,2): error CS0616: 'NumericAttribute' is not an attribute class
//    Attribute1.cs(7,14): (Location of symbol related to previous error)
<Assembly: CLSCompliant(True)>

<AttributeUsageAttribute(AttributeTargets.Class Or AttributeTargets.Struct)> _
Public Class NumericAttribute
    Private _isNumeric As Boolean

    Public Sub New(isNumeric As Boolean)
        _isNumeric = isNumeric
    End Sub

    Public ReadOnly Property IsNumeric As Boolean
        Get
            Return _isNumeric
        End Get
    End Property
End Class

<Numeric(True)> Public Structure UDouble
    Dim Value As Double
End Structure
' Compilation produces a compiler error like the following:
'    error BC31504: 'NumericAttribute' cannot be used as an attribute because it 
'    does not inherit from 'System.Attribute'.
'    
'    <Numeric(True)> Public Structure UDouble
'     ~~~~~~~~~~~~~

CLS 準拠属性のコンストラクターまたはプロパティは、次の型のみを公開できます。

次の例では、DescriptionAttribute から派生する クラスを定義します。 クラス コンストラクターには Descriptor 型のパラメーターがあるため、クラスは CLS に準拠していません。 C# コンパイラは警告を出力しますが、正常にコンパイルされますが、Visual Basic コンパイラは警告またはエラーを出力しません。

using System;

[assembly:CLSCompliantAttribute(true)]

public enum DescriptorType { type, member };

public class Descriptor
{
   public DescriptorType Type;
   public String Description;
}

[AttributeUsage(AttributeTargets.All)]
public class DescriptionAttribute : Attribute
{
   private Descriptor desc;

   public DescriptionAttribute(Descriptor d)
   {
      desc = d;
   }

   public Descriptor Descriptor
   { get { return desc; } }
}
// Attempting to compile the example displays output like the following:
//       warning CS3015: 'DescriptionAttribute' has no accessible
//               constructors which use only CLS-compliant types
<Assembly: CLSCompliantAttribute(True)>

Public Enum DescriptorType As Integer
    Type = 0
    Member = 1
End Enum

Public Class Descriptor
    Public Type As DescriptorType
    Public Description As String
End Class

<AttributeUsage(AttributeTargets.All)> _
Public Class DescriptionAttribute : Inherits Attribute
    Private desc As Descriptor

    Public Sub New(d As Descriptor)
        desc = d
    End Sub

    Public ReadOnly Property Descriptor As Descriptor
        Get
            Return desc
        End Get
    End Property
End Class

CLSCompliantAttribute 属性

CLSCompliantAttribute 属性は、プログラム要素が共通言語仕様に準拠しているかどうかを示すために使用されます。 CLSCompliantAttribute(Boolean) コンストラクターには、プログラム要素が CLS に準拠しているかどうかを示す 1 つの必須パラメーター isCompliant が含まれています。

コンパイル時に、コンパイラは CLS に準拠していると推定される非準拠の要素を検出し、警告を出力します。 コンパイラは、非準拠として明示的に宣言されている型またはメンバーに対して警告を出力しません。

コンポーネント開発者は、 CLSCompliantAttribute 属性を 2 つの方法で使用できます。

  • CLS に準拠しているコンポーネントによって公開されるパブリック インターフェイスの部分と、CLS に準拠していない部分を定義します。 この属性を使用して特定のプログラム要素を CLS 準拠としてマークする場合、その使用により、.NET を対象とするすべての言語とツールからそれらの要素にアクセスできるようになります。

  • コンポーネント ライブラリのパブリック インターフェイスが CLS に準拠しているプログラム要素のみを公開するようにします。 要素が CLS に準拠していない場合、コンパイラは通常警告を発行します。

Warnung

言語コンパイラでは、 CLSCompliantAttribute 属性が使用されているかどうかに関係なく、CLS 準拠の規則が適用される場合があります。 たとえば、インターフェイスで静的メンバーを定義すると、CLS 規則に違反します。 この点で、インターフェイスで static (C#) または Shared (Visual Basic) メンバーを定義すると、C# コンパイラと Visual Basic コンパイラの両方でエラー メッセージが表示され、アプリのコンパイルに失敗します。

CLSCompliantAttribute属性は、AttributeUsageAttributeの値を持つAttributeTargets.All属性でマークされます。 この値を使用すると、アセンブリ、モジュール、型 (クラス、構造体、列挙型、インターフェイス、デリゲート)、型メンバー (コンストラクター、メソッド、プロパティ、フィールド、イベント)、パラメーター、ジェネリック パラメーター、戻り値など、任意のプログラム要素に CLSCompliantAttribute 属性を適用できます。 ただし、実際には、アセンブリ、型、および型のメンバーにのみ属性を適用する必要があります。 それ以外の場合、コンパイラは属性を無視し、ライブラリのパブリック インターフェイスで非準拠のパラメーター、ジェネリック パラメーター、または戻り値が検出されるたびにコンパイラ警告を生成し続けます。

CLSCompliantAttribute属性の値は、包含プログラム要素によって継承されます。 たとえば、アセンブリが CLS 準拠としてマークされている場合、その型も CLS に準拠しています。 型が CLS 準拠としてマークされている場合、入れ子になった型とメンバーも CLS に準拠します。

CLSCompliantAttribute属性を包含プログラム要素に適用することで、継承されたコンプライアンスを明示的にオーバーライドできます。 たとえば、CLSCompliantAttributeisCompliant値を持つfalse属性を使用して準拠アセンブリで非準拠型を定義し、isComplianttrue値を持つ属性を使用して非準拠アセンブリで準拠型を定義できます。 準拠型で非準拠メンバーを定義することもできます。 ただし、非準拠型は準拠しているメンバーを持つことはできません。そのため、isComplianttrue値を持つ属性を使用して、非準拠型からの継承をオーバーライドすることはできません。

コンポーネントを開発するときは、アセンブリ、その型、およびそのメンバーが CLS に準拠しているかどうかを示すために、常に CLSCompliantAttribute 属性を使用する必要があります。

CLS 準拠コンポーネントを作成するには:

  1. CLSCompliantAttributeを使用して、アセンブリを CLS 準拠としてマークします。

  2. CLS に準拠していないアセンブリ内のパブリックに公開されている型を非準拠としてマークします。

  3. CLS 準拠型のパブリックに公開されているメンバーを非準拠としてマークします。

  4. CLS に準拠していないメンバーの CLS 準拠の代替手段を提供します。

準拠していない型とメンバーをすべて正常にマークした場合、コンパイラはコンプライアンス違反の警告を出力しないでください。 ただし、CLS に準拠していないメンバーを指定し、CLS 準拠の代替手段を製品ドキュメントに記載する必要があります。

次の例では、 CLSCompliantAttribute 属性を使用して、CLS 準拠のアセンブリと、2 つの非 CLS 準拠メンバーを持つ型 ( CharacterUtilities) を定義します。 両方のメンバーが CLSCompliant(false) 属性でタグ付けされているため、コンパイラは警告を生成しません。 このクラスは、両方のメソッドに CLS 準拠の代替手段も提供します。 通常は、CLS に準拠した代替手段を提供するために、 ToUTF16 メソッドに 2 つのオーバーロードを追加するだけです。 ただし、メソッドは戻り値に基づいてオーバーロードできないため、CLS 準拠メソッドの名前は非準拠メソッドの名前とは異なります。

using System;
using System.Text;

[assembly:CLSCompliant(true)]

public class CharacterUtilities
{
   [CLSCompliant(false)] public static ushort ToUTF16(String s)
   {
      s = s.Normalize(NormalizationForm.FormC);
      return Convert.ToUInt16(s[0]);
   }

   [CLSCompliant(false)] public static ushort ToUTF16(Char ch)
   {
      return Convert.ToUInt16(ch);
   }

   // CLS-compliant alternative for ToUTF16(String).
   public static int ToUTF16CodeUnit(String s)
   {
      s = s.Normalize(NormalizationForm.FormC);
      return (int) Convert.ToUInt16(s[0]);
   }

   // CLS-compliant alternative for ToUTF16(Char).
   public static int ToUTF16CodeUnit(Char ch)
   {
      return Convert.ToInt32(ch);
   }

   public bool HasMultipleRepresentations(String s)
   {
      String s1 = s.Normalize(NormalizationForm.FormC);
      return s.Equals(s1);
   }

   public int GetUnicodeCodePoint(Char ch)
   {
      if (Char.IsSurrogate(ch))
         throw new ArgumentException("ch cannot be a high or low surrogate.");

      return Char.ConvertToUtf32(ch.ToString(), 0);
   }

   public int GetUnicodeCodePoint(Char[] chars)
   {
      if (chars.Length > 2)
         throw new ArgumentException("The array has too many characters.");

      if (chars.Length == 2) {
         if (! Char.IsSurrogatePair(chars[0], chars[1]))
            throw new ArgumentException("The array must contain a low and a high surrogate.");
         else
            return Char.ConvertToUtf32(chars[0], chars[1]);
      }
      else {
         return Char.ConvertToUtf32(chars.ToString(), 0);
      }
   }
}
Imports System.Text

<Assembly: CLSCompliant(True)>

Public Class CharacterUtilities
    <CLSCompliant(False)> Public Shared Function ToUTF16(s As String) As UShort
        s = s.Normalize(NormalizationForm.FormC)
        Return Convert.ToUInt16(s(0))
    End Function

    <CLSCompliant(False)> Public Shared Function ToUTF16(ch As Char) As UShort
        Return Convert.ToUInt16(ch)
    End Function

    ' CLS-compliant alternative for ToUTF16(String).
    Public Shared Function ToUTF16CodeUnit(s As String) As Integer
        s = s.Normalize(NormalizationForm.FormC)
        Return CInt(Convert.ToInt16(s(0)))
    End Function

    ' CLS-compliant alternative for ToUTF16(Char).
    Public Shared Function ToUTF16CodeUnit(ch As Char) As Integer
        Return Convert.ToInt32(ch)
    End Function

    Public Function HasMultipleRepresentations(s As String) As Boolean
        Dim s1 As String = s.Normalize(NormalizationForm.FormC)
        Return s.Equals(s1)
    End Function

    Public Function GetUnicodeCodePoint(ch As Char) As Integer
        If Char.IsSurrogate(ch) Then
            Throw New ArgumentException("ch cannot be a high or low surrogate.")
        End If
        Return Char.ConvertToUtf32(ch.ToString(), 0)
    End Function

    Public Function GetUnicodeCodePoint(chars() As Char) As Integer
        If chars.Length > 2 Then
            Throw New ArgumentException("The array has too many characters.")
        End If
        If chars.Length = 2 Then
            If Not Char.IsSurrogatePair(chars(0), chars(1)) Then
                Throw New ArgumentException("The array must contain a low and a high surrogate.")
            Else
                Return Char.ConvertToUtf32(chars(0), chars(1))
            End If
        Else
            Return Char.ConvertToUtf32(chars.ToString(), 0)
        End If
    End Function
End Class

ライブラリではなくアプリを開発している場合 (つまり、他のアプリ開発者が使用できる型やメンバーを公開していない場合)、アプリが使用するプログラム要素の CLS コンプライアンスは、言語でサポートされていない場合にのみ重要です。 その場合、CLS に準拠していない要素を使用しようとすると、言語コンパイラでエラーが生成されます。

言語間の相互運用性

言語の独立には、いくつかの意味があります。 1 つの意味は、ある言語で記述された型を、別の言語で記述されたアプリからシームレスに使用することです。 この記事の焦点である 2 つ目の意味は、複数の言語で記述されたコードを 1 つの .NET アセンブリに結合することです。

次の例は、 NumericLibStringLibの 2 つのクラスを含む Utilities.dll という名前のクラス ライブラリを作成することで、言語間の相互運用性を示しています。 NumericLib クラスは C# で記述され、StringLib クラスは Visual Basic で記述されます。 StringUtil.vbのソース コードを次に示します。このコードには、ToTitleCase クラスに 1 つのメンバー (StringLib) が含まれています。

Imports System.Collections.Generic
Imports System.Runtime.CompilerServices

Public Module StringLib
    Private exclusions As List(Of String)

    Sub New()
        Dim words() As String = {"a", "an", "and", "of", "the"}
        exclusions = New List(Of String)
        exclusions.AddRange(words)
    End Sub

    <Extension()> _
    Public Function ToTitleCase(title As String) As String
        Dim words() As String = title.Split()
        Dim result As String = String.Empty

        For ctr As Integer = 0 To words.Length - 1
            Dim word As String = words(ctr)
            If ctr = 0 OrElse Not exclusions.Contains(word.ToLower()) Then
                result += word.Substring(0, 1).ToUpper() + _
                          word.Substring(1).ToLower()
            Else
                result += word.ToLower()
            End If
            If ctr <= words.Length - 1 Then
                result += " "
            End If
        Next
        Return result
    End Function
End Module

NumericLibIsEvenの 2 つのメンバーを持つNearZero クラスを定義する、NumberUtil.csのソース コードを次に示します。

using System;

public static class NumericLib
{
   public static bool IsEven(this IConvertible number)
   {
      if (number is Byte ||
          number is SByte ||
          number is Int16 ||
          number is UInt16 ||
          number is Int32 ||
          number is UInt32 ||
          number is Int64)
         return Convert.ToInt64(number) % 2 == 0;
      else if (number is UInt64)
         return ((ulong) number) % 2 == 0;
      else
         throw new NotSupportedException("IsEven called for a non-integer value.");
   }

   public static bool NearZero(double number)
   {
      return Math.Abs(number) < .00001;
   }
}

2 つのクラスを 1 つのアセンブリにパッケージ化するには、それらをモジュールにコンパイルする必要があります。 Visual Basic ソース コード ファイルをモジュールにコンパイルするには、次のコマンドを使用します。

vbc /t:module StringUtil.vb

Visual Basic コンパイラのコマンド ライン構文の詳細については、「 コマンド ラインからのビルド」を参照してください。

C# ソース コード ファイルをモジュールにコンパイルするには、次のコマンドを使用します。

csc /t:module NumberUtil.cs

次に、 リンカー オプション を使用して、2 つのモジュールをアセンブリにコンパイルします。

link numberutil.netmodule stringutil.netmodule /out:UtilityLib.dll /dll

次の例では、 NumericLib.NearZero メソッドと StringLib.ToTitleCase メソッドを呼び出します。 Visual Basic コードと C# コードの両方で、両方のクラスのメソッドにアクセスできます。

using System;

public class Example
{
   public static void Main()
   {
      Double dbl = 0.0 - Double.Epsilon;
      Console.WriteLine(NumericLib.NearZero(dbl));

      string s = "war and peace";
      Console.WriteLine(s.ToTitleCase());
   }
}
// The example displays the following output:
//       True
//       War and Peace
Module Example
    Public Sub Main()
        Dim dbl As Double = 0.0 - Double.Epsilon
        Console.WriteLine(NumericLib.NearZero(dbl))

        Dim s As String = "war and peace"
        Console.WriteLine(s.ToTitleCase())
    End Sub
End Module
' The example displays the following output:
'       True
'       War and Peace

Visual Basic コードをコンパイルするには、次のコマンドを使用します。

vbc example.vb /r:UtilityLib.dll

C# でコンパイルするには、コンパイラの名前を vbc から csc に変更し、ファイル拡張子を .vb から .cs に変更します。

csc example.cs /r:UtilityLib.dll