Kemandirian bahasa dan komponen yang tidak bergantung pada bahasa

.NET bersifat mandiri dari bahasa komputer. Ini berarti bahwa, sebagai pengembang, Anda dapat mengembangkan dalam salah satu dari banyak bahasa yang menargetkan implementasi .NET, seperti C #, F #, dan Visual Basic. Anda dapat mengakses jenis dan anggota perpustakaan kelas yang dikembangkan untuk implementasi .NET tanpa harus mengetahui bahasa komputer awal penulisan mereka dan tanpa harus mengikuti salah satu konvensi bahasa komputer aslinya. Jika Anda adalah pengembang komponen, komponen Anda dapat diakses oleh aplikasi .NET mana pun, terlepas dari bahasanya.

Catatan

Bagian pertama dari artikel ini membahas pembuatan komponen yang independen bahasa, yaitu komponen yang dapat dikonsumsi oleh aplikasi yang ditulis dalam bahasa apa pun. Anda juga dapat membuat satu komponen atau aplikasi dari kode sumber yang ditulis dalam beberapa bahasa; lihat Interoperabilitas Lintas Bahasa di bagian kedua artikel ini.

Untuk sepenuhnya berinteraksi dengan objek lain yang ditulis dalam bahasa apa pun, hanya fitur-fitur yang umum untuk semua bahasa yang boleh diekspos oleh objek ke pemanggil. Serangkaian fitur umum ini didefinisikan oleh Common Language Specification (CLS), yang merupakan sekumpulan aturan yang berlaku untuk rakitan yang dihasilkan. Spesifikasi Bahasa Umum didefinisikan dalam Partisi I, Klausul 7 hingga 11 dari ECMA-335 Standard: Common Language Infrastructure.

Jika komponen Anda sesuai dengan Spesifikasi Bahasa Umum, dijamin sesuai dengan CLS dan dapat diakses dari kode dalam rakitan yang ditulis dalam bahasa pemrograman apa pun yang mendukung CLS. Anda dapat menentukan apakah komponen Anda sesuai dengan Spesifikasi Bahasa Umum pada waktu kompilasi dengan menerapkan atribut CLSCompliantAttribute ke kode sumber Anda. Untuk informasi selengkapnya, lihat atribut CLSCompliantAttribute.

Aturan kepatuhan CLS

Bagian ini membahas aturan untuk membuat komponen yang sesuai dengan CLS. Untuk daftar lengkap aturan, lihat Partisi I, Klausul 11dari ECMA-335 Standard: Common Language Infrastructure.

Catatan

Spesifikasi Bahasa Umum membahas setiap aturan untuk kepatuhan CLS karena berlaku untuk konsumen (pengembang yang secara terprogram mengakses komponen yang sesuai dengan CLS), kerangka kerja (pengembang yang menggunakan pengompilasi bahasa untuk membuat pustaka yang sesuai dengan CLS), dan extender (pengembang yang membuat alat seperti pengompilasi bahasa atau pengurai kode yang membuat komponen yang sesuai dengan CLS). Artikel ini berfokus pada aturan yang berlaku untuk kerangka kerja. Namun, perhatikan bahwa beberapa aturan yang berlaku untuk extender juga dapat berlaku untuk rakitan yang dibuat menggunakan Reflection.Emit.

Untuk merancang komponen yang independen bahasa, Anda hanya perlu menerapkan aturan kepatuhan CLS ke antarmuka publik komponen Anda. Implementasi privat Anda tidak harus sesuai dengan spesifikasi.

Penting

Aturan untuk kepatuhan CLS hanya berlaku untuk antarmuka publik komponen, bukan untuk implementasi privatnya.

Misalnya, bilangan bulat tidak bertanda selain Byte tidak mematuhi CLS. Karena kelas Person dalam contoh berikut mengekspos properti Age jenis UInt16, kode berikut menampilkan peringatan pengompilasi.

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
'                                ~~~

Anda dapat membuat kelas Person yang sesuai dengan CLS dengan mengubah jenis properti Age dari UInt16 ke Int16, yang merupakan bilangan bulat bertanda 16-bit yang mematuhi CLS. Anda tidak perlu mengubah jenis bidang privat 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

Antarmuka publik perpustakaan terdiri dari berikut:

  • Definisi kelas publik.

  • Definisi anggota publik kelas publik, dan definisi anggota yang dapat diakses oleh kelas turunan (yaitu, anggota yang dilindungi).

  • Parameter dan jenis pengembalian metode publik kelas publik, dan parameter dan jenis pengembalian metode yang dapat diakses oleh kelas turunan.

Aturan untuk kepatuhan CLS tercantum dalam tabel berikut. Teks aturan diambil secara verbatim dari ECMA-335 Standard: Common Language Infrastructure, yang merupakan Hak Cipta 2012 oleh Ecma International. Informasi lebih rinci tentang aturan ini ditemukan di bagian berikut.

Kategori Lihat Aturan Nomor Aturan
Aksesibilitas Aksesibilitas anggota Aksesibilitas tidak akan diubah saat menimpa metode yang diwariskan, kecuali ketika menimpa metode yang diwarisi dari perakitan yang berbeda dengan aksesibilitas family-or-assembly. Dalam hal ini, penimpaan akan memiliki aksesibilitas family. 10
Aksesibilitas Aksesibilitas anggota Visibilitas dan aksesibilitas jenis dan anggota harus sedemikian rupa sehingga jenis dalam tanda tangan setiap anggota harus terlihat dan dapat diakses setiap kali anggota itu sendiri terlihat dan dapat diakses. Misalnya, metode publik yang terlihat dari luar rakitannya tidak akan memiliki argumen yang jenisnya hanya terlihat di dalam rakitan. Visibilitas dan aksesibilitas jenis yang menyusun jenis generik yang dibuat instansnya yang digunakan dalam tanda tangan anggota mana pun harus terlihat dan dapat diakses setiap kali anggota itu sendiri terlihat dan dapat diakses. Misalnya, jenis generik yang dibuat instansnya yang ada dalam tanda tangan anggota yang terlihat dari luar rakitannya tidak akan memiliki argumen generik yang jenisnya hanya terlihat di dalam rakitan. 12
Larik Array Larik harus memiliki elemen dengan jenis yang sesuai dengan CLS, dan semua dimensi jenis harus memiliki batas nol yang lebih rendah. Hanya fakta bahwa item adalah sebuah larik dan jenis elemen larik yang akan diperlukan untuk membedakan antara kelebihan beban. Ketika kelebihan beban didasarkan pada dua atau lebih jenis larik, jenis elemen harus diberi nama jenis. 16
Atribut Atribut Atribut harus berjenis System.Attribute, atau jenis yang mewarisinya. 41
Atribut Atribut CLS hanya mengizinkan subset dari pengodean atribut kustom. Satu-satunya jenis yang akan muncul dalam pengodean ini adalah (lihat Partisi IV): System.Type, System.String, System.Char, System.Boolean, System.Byte, System.Int16, System.Int32, System.Int64, System.Single, System.Doubledan jenis enumerasi apa pun berdasarkan jenis bilangan bulat dasar yang mematuhi CLS. 34
Atribut Atribut CLS tidak mengizinkan pengubah yang diperlukan terlihat secara publik (modreq, lihat Partisi II), tetapi mengizinkan pengubah opsional (modopt, lihat Partisi II) yang tidak dipahaminya. 35
Konstruktor Konstruktor Konstruktor objek harus memanggil beberapa konstruktor instans dari kelas dasarnya sebelum akses apa pun terjadi pada data instans yang diwariskan. (Ini tidak berlaku untuk jenis nilai, yang tidak perlu memiliki konstruktor.) 21
Konstruktor Konstruktor Konstruktor objek tidak boleh dipanggil kecuali sebagai bagian dari penciptaan suatu objek, dan suatu objek tidak boleh diinisialisasi dua kali. 22
Enumerasi Enumerasi Jenis enum yang mendasari adalah jenis bilangan bulat CLS bawaan, nama bidang harus berupa "value__", dan bidang itu harus ditandai RTSpecialName. 7
Enumerasi Enumerasi Ada dua jenis enum yang berbeda, yang diindikaiskan oleh ada atau tidak adanya atribut kustom (lihat Pustaka Partisi IV) System.FlagsAttribute. Yang satu merepresentasikan nilai bilangan bulat bernama; yang lain merepresentasikan bendera bit bernama yang dapat digabungkan untuk menghasilkan nilai yang tidak bernama. Nilai dari enum tidak terbatas pada nilai yang ditentukan. 8
Enumerasi Enumerasi Bidang statik literal dari enum harus memiliki jenis enum itu sendiri. 9
Acara Peristiwa Metode yang mengimplementasikan suatu peristiwa harus ditandai SpecialName dalam metadata. 29
Acara Peristiwa Aksesibilitas suatu peristiwa dan pengaksesnya harus identik. 30
Acara Peristiwa Metode add dan remove untuk suatu peristiwa keduanya akan ada atau tidak ada. 31
Acara Peristiwa Metode add dan remove untuk suatu peristiwa masing-masing harus mengambil satu parameter yang jenisnya mendefinisikan jenis peristiwa dan yang akan berasal dari System.Delegate. 32
Acara Peristiwa Peristiwa harus mematuhi pola penamaan tertentu. Atribut SpecialName yang dimaksud dalam aturan CLS 29 akan diabaikan dalam perbandingan nama yang sesuai dan harus mematuhi aturan pengidentifikasi. 33
Pengecualian Pengecualian Objek yang dilemparkan harus berjenis System.Exception atau jenis yang mewarisinya. Meskipun demikian, metode yang sesuai dengan CLS tidak diperlukan untuk memblokir propagasi jenis pengecualian lainnya. 40
Umum Aturan kepatuhan CLS Aturan CLS hanya berlaku untuk bagian-bagian dari jenis yang dapat diakses atau terlihat dari luar rakitan yang menentukan. 1
Umum Aturan kepatuhan CLS Anggota jenis yang tidak mematuhi CLS tidak akan ditandai sesuai dengan CLS. 2
Generik Jenis dan anggota generik Jenis bersarang harus memiliki setidaknya parameter generik sebanyak jenis penutup. Parameter generik dalam jenis bersarang sesuai menurut posisinya dengan parameter generik dalam jenis penutupnya. 42
Generik Jenis dan anggota generik Nama jenis generik harus mengodekan jumlah parameter tipe yang dideklarasikan pada jenis non-bersarang, atau yang baru diperkenalkan ke jenis jika bersarang, sesuai dengan aturan yang ditentukan di atas. 43
Generik Jenis dan anggota generik Jenis generik harus mendeklarasikan ulang batasan yang cukup untuk menjamin bahwa setiap batasan pada jenis dasar, atau antarmuka akan terpenuhi oleh batasan jenis generik. 44
Generik Jenis dan anggota generik Jenis yang digunakan sebagai batasan pada parameter generik itu sendiri harus sesuai dengan CLS. 45
Generik Jenis dan anggota generik Visibilitas dan aksesibilitas anggota (termasuk jenis bersarang) dalam jenis generik yang dibuat instansnya harus dianggap dicakup ke pembuatan instans tertentu daripada deklarasi jenis generik secara keseluruhan. Dengan asumsi ini, aturan visibilitas dan aksesibilitas aturan CLS 12 masih berlaku. 46
Generik Jenis dan anggota generik Untuk setiap metode generik abstrak atau virtual, harus ada implementasi konkret (nonabstrak) default 47
Antarmuka Antarmuka Antarmuka yang sesuai dengan CLS tidak memerlukan definisi metode yang tidak sesuai dengan CLS untuk menerapkannya. 18
Antarmuka Antarmuka Antarmuka yang sesuai dengan CLS tidak akan mendefinisikan metode statik, juga tidak akan menentukan bidang. 19
Anggota Anggota jenis secara umum Bidang dan metode statik global tidak sesuai dengan CLS. 36
Anggota -- Nilai statik literal ditentukan dengan menggunakan metadata inisialisasi bidang. Literal yang mematuhi CLS harus memiliki nilai yang ditentukan dalam metadata inisialisasi bidang yang memiliki jenis yang sama persis dengan literal (atau dari jenis yang mendasari, jika literal tersebut adalah enum). 13
Anggota Anggota jenis secara umum Batasan vararg bukan bagian dari CLS, dan satu-satunya konvensi panggilan yang didukung oleh CLS adalah konvensi panggilan terkelola standar. 15
Konvensi penamaan Konvensi penamaan Rakitan harus mengikuti Lampiran 7 Laporan Teknis 15 dari Unicode Standard3.0 yang mengatur kumpulan karakter yang diizinkan untuk memulai dan disertakan dalam pengidentifikasi, tersedia secara online di Formulir Normalisasi Unicode. Pengidentifikasi harus dalam format kanonik yang ditentukan oleh Formulir Normalisasi Unicode C. Untuk tujuan CLS, dua pengidentifikasi sama jika pemetaan huruf kecil mereka (seperti yang ditentukan oleh pemetaan huruf kecil Unicode one-to-one yang tidak sensitif lokal) sama. Artinya, untuk dua pengidentifikasi yang dianggap berbeda di bawah CLS, mereka akan berbeda dalam lebih dari sekadar ukuran huruf mereka. Namun, untuk menimpa definisi yang diwariskan, CLI memerlukan penggunaan pengodean yang tepat dari deklarasi asli. 4
Kelebihan beban Konvensi penamaan Semua nama yang diperkenalkan dalam lingkup yang sesuai dengan CLS harus berbeda secara independen dari jenisnya, kecuali jika nama-nama itu identik dan diselesaikan melalui kelebihan beban. Artinya, sementara CTS mengizinkan satu jenis untuk menggunakan nama yang sama untuk metode dan bidang, CLS tidak. 5
Kelebihan beban Konvensi penamaan Bidang dan jenis bersarang harus sudah berbeda menurut perbandingan pengidentifikasi, meskipun CTS mengizinkan tanda tangan yang berbeda untuk dibedakan. Metode, properti, dan peristiwa yang memiliki nama yang sama (dengan perbandingan pengidentifikasi) akan berbeda lebih dari sekadar jenis pengembalian, kecuali sebagaimana ditentukan dalam Aturan CLS 39 6
Kelebihan beban Kelebihan beban Hanya properti dan metode yang dapat kelebihan beban. 37
Kelebihan beban Kelebihan beban Properti dan metode dapat kelebihan beban hanya berdasarkan jumlah dan jenis parameternya, kecuali operator konversi bernama op_Implicit dan op_Explicit, yang juga dapat kelebihan beban berdasarkan jenis pengembaliannya. 38
Kelebihan beban -- Jika dua atau lebih metode yang sesuai dengan CLS yang dideklarasikan dalam jenis memiliki nama yang sama dan, untuk satu set tertentu dari pembuatan instans jenis, mereka memiliki parameter dan jenis pengembalian yang sama, maka semua metode ini harus setara secara semantik pada pembuatan instans jenis tersebut. 48
Properti Properti Metode yang mengimplementasikan metode getter dan setter properti harus ditandai SpecialName dalam metadata. 24
Properti Properti Pengakses properti semuanya harus statik, semuanya virtual, atau semuanya merupakan instans. 26
Properti Properti Jenis properti harus merupakan jenis pengembalian dari getter dan jenis argumen terakhir dari setter. Jenis parameter properti harus merupakan jenis parameter untuk getter dan jenis semua kecuali parameter akhir dari setter. Semua jenis ini harus sesuai dengan CLS, dan tidak akan dikelola pointer (yaitu, tidak akan diteruskan oleh referensi). 27
Properti Properti Properti harus mematuhi pola penamaan tertentu. Atribut SpecialName yang dimaksud dalam aturan CLS 24 harus diabaikan dalam perbandingan nama yang sesuai dan harus mematuhi aturan pengidentifikasi. Properti harus memiliki metode getter, metode setter, atau keduanya. 28
Konversi jenis Konversi jenis Jika op_Implicit atau op_Explicit disediakan, cara alternatif untuk memberikan konversi eksplisit harus disediakan. 39
Jenis Jenis dan tanda tangan anggota jenis Jenis nilai kotak tidak sesuai dengan CLS. 3
Jenis Jenis dan tanda tangan anggota jenis Semua jenis yang muncul dalam tanda tangan harus sesuai dengan CLS. Semua jenis yang menyusun jenis generik yang dibuat instansnya harus sesuai dengan CLS. 11
Jenis Jenis dan tanda tangan anggota jenis Referensi yang diberi jenis tidak sesuai dengan CLS. 14
Jenis Jenis dan tanda tangan anggota jenis Jenis pointer yang tidak dikelola tidak sesuai dengan CLS. 17
Jenis Jenis dan tanda tangan anggota jenis Kelas, jenis nilai, dan antarmuka yang sesuai dengan CLS tidak memerlukan implementasi anggota yang tidak mematuhi CLS 20
Jenis Jenis dan tanda tangan anggota jenis System.Object sesuai dengan CLS. Setiap kelas lain yang sesuai dengan CLS akan mewarisi dari kelas yang sesuai dengan CLS. 23

Indeks ke subbagian:

Jenis dan tanda tangan anggota jenis

Jenis System.Object sesuai dengan CLS dan merupakan jenis dasar dari semua jenis objek dalam sistem jenis .NET. Pewarisan dalam .NET bisa bersifat implisit (misalnya, kelas String secara implisit mewarisi dari kelas Object) atau eksplisit (misalnya, kelas CultureNotFoundException secara eksplisit mewarisi dari kelas ArgumentException, yang secara eksplisit mewarisi dari kelas Pengecualian. Agar jenis turunan sesuai dengan CLS, jenis dasarnya juga harus sesuai dengan CLS.

Contoh berikut menunjukkan jenis turunan yang jenis dasarnya tidak sesuai dengan CLS. Ini mendefinisikan kelas Counter dasar yang menggunakan bilangan bulat 32-bit tidak bertanda sebagai penghitung. Karena kelas menyediakan fungsi penghitung dengan membungkus bilangan bulat tidak bertanda, kelas ditandai sebagai tidak sesuai dengan CLS. Akibatnya, kelas turunan, NonZeroCounter, juga tidak sesuai dengan 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
'                 ~~~~~~~~~~~~~~

Semua jenis yang muncul dalam tanda tangan anggota, termasuk jenis pengembalian atau jenis properti metode, harus sesuai dengan CLS. Selain itu, untuk jenis generik:

  • Semua jenis yang menyusun jenis generik yang dibuat instansnya harus sesuai dengan CLS.

  • Semua jenis yang digunakan sebagai batasan pada parameter generik harus sesuai dengan CLS.

Sistem jenis umum .NET mencakup banyak jenis bawaan yang didukung langsung oleh runtime bahasa umum dan secara khusus dikodekan dalam metadata rakitan. Dari jenis intrinsik ini, jenis yang tercantum dalam tabel berikut sesuai dengan CLS.

Jenis yang sesuai dengan CLS Deskripsi
Byte Bilangan bulat tidak bertanda 8-bit
Int16 Bilangan bulat bertanda 16-bit
Int32 Bilangan bulat bertanda 32-bit
Int64 Bilangan bulat bertanda 64-bit
Panjang Nilai floating-point setengah presisi
Satu Nilai floating-point presisi tunggal
Laju Nilai floating-point presisi ganda
Boolean jenis nilai true atau false
Char Unit kode yang dikodekan UTF-16
Desimal Angka desimal non-floating-point
IntPtr Pointer atau handel berukuran yang ditentukan platform
String Koleksi objek Char nol, satu, atau lebih

Jenis intrinsik yang tercantum dalam tabel berikut tidak sesuai dengan CLS.

Jenis yang tidak sesuai Deskripsi Alternatif kompatibel CLS
SByte Jenis data bilangan bulat bertanda 8-bit Int16
UInt16 Bilangan bulat tidak bertanda 16-bit Int32
UInt32 Bilangan bulat tidak bertanda 32-bit Int64
UInt64 Bilangan bulat tidak bertanda 64-bit Int64 (mungkin meluap), BigInteger, atau Double
UIntPtr Pointer atau handel yang tidak bertanda IntPtr

Pustaka Kelas .NET atau pustaka kelas lainnya mungkin menyertakan jenis lain yang tidak sesuai dengan CLS, misalnya:

  • Jenis nilai kotak. Contoh C# berikut membuat kelas yang memiliki properti publik jenis int* bernama Value. Karena int* adalah jenis nilai kotak, pengompilasi memberinya bendera sebagai tidak mematuhi 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
    
  • Referensi yang diberi jenis, yang merupakan konstruksi khusus yang berisi referensi ke objek dan referensi ke jenis. Referensi yang diberi jenis direpresentasikan dalam .NET oleh kelas TypedReference.

Jika jenis tidak sesuai dengan CLS, Anda harus menerapkan atribut CLSCompliantAttribute dengan nilai isCompliantfalse ke jenis. Untuk informasi selengkapnya, lihat bagian atribut CLSCompliantAttribute.

Contoh berikut menggambarkan masalah kepatuhan CLS dalam tanda tangan metode dan dalam pembuatan instans jenis generik. Ini mendefinisikan InvoiceItem kelas dengan properti jenis UInt32, properti jenis Nullable<UInt32>, dan konstruktor dengan parameter jenis UInt32 dan Nullable<UInt32>. Anda mendapatkan empat peringatan pengompilasi ketika Anda mencoba mengompilasi contoh ini.

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
'                       ~~~~~~~~~

Untuk menghilangkan peringatan pengompilasi, ganti jenis yang tidak mematuhi CLS di antarmuka publik InvoiceItem dengan jenis yang sesuai:

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

Selain jenis tertentu yang tercantum, beberapa kategori jenis tidak sesuai dengan CLS. Ini termasuk jenis pointer yang tidak dikelola dan jenis pointer fungsi. Contoh berikut menghasilkan peringatan pengompilasi karena menggunakan pointer ke bilangan bulat untuk membuat larik bilangan bulat.

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

Untuk kelas abstrak yang mematuhi CLS (yaitu, kelas yang ditandai sebagai abstract dalam C# atau sebagai MustInherit dalam Visual Basic), semua anggota kelas juga harus mematuhi CLS.

Konvensi penamaan

Karena beberapa bahasa pemrograman tidak peka huruf besar/kecil, pengidentifikasi (seperti nama namespace, jenis, dan anggota) harus berbeda lebih dari ukuran hurufnya. Dua pengidentifikasi dianggap setara jika pemetaan huruf kecilnya sama. Contoh C# berikut mendefinisikan dua kelas publik, Person dan person. Karena mereka hanya berbeda berdasarkan ukuran huruf, pengompilasi C # memberi mereka bendera sebagai tidak sesuai dengan 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)

Pengidentifikasi bahasa pemrograman, seperti nama namespace, jenis, dan anggota, harus sesuai dengan Standar Unicode. Ini berarti bahwa:

  • Karakter pertama dari pengidentifikasi dapat berupa huruf besar, huruf kecil, huruf besar dalam judul, huruf pengubah, huruf lain, atau nomor huruf Unicode apa pun. Untuk informasi tentang kategori karakter Unicode, lihat enumerasi System.Globalization.UnicodeCategory.

  • Karakter berikutnya dapat berasal dari salah satu kategori sebagai karakter pertama, dan juga dapat mencakup tanda non-spasi, tanda penggabung spasi, angka desimal, tanda baca konektor, dan kode pemformatan.

Sebelum Anda membandingkan pengidentifikasi, Anda harus memfilter kode pemformatan dan mengonversi pengidentifikasi ke Bentuk Normalisasi Unicode C, karena satu karakter dapat direpresentasikan oleh beberapa unit kode UTF-16-encoded. Urutan karakter yang menghasilkan unit kode yang sama dalam Bentuk Normalisasi Unicode C tidak sesuai dengan CLS. Contoh berikut mendefinisikan properti bernama â„«, yang terdiri dari karakter ANGSTROM SIGN (U+212B), dan properti kedua bernama Ã…, yang terdiri dari karakter LATIN CAPITAL LETTER A WITH RING ABOVE (U+00C5). Pengompilasi C# dan Visual Basic memberi bendera pada kode sumber sebagai tidak sesuai 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
'                       ~

Nama anggota dalam lingkup tertentu (seperti namespace dalam rakitan, jenis dalam namespace, atau anggota dalam jenis) harus unik kecuali untuk nama yang diselesaikan melalui kelebihan beban. Persyaratan ini lebih ketat daripada sistem jenis umum, yang memungkinkan beberapa anggota dalam lingkup untuk memiliki nama yang identik selama mereka adalah jenis anggota yang berbeda (misalnya, satu adalah metode dan satunya adalah bidang). Khususnya, untuk anggota jenis:

  • Bidang dan jenis bersarang dibedakan berdasarkan nama saja.

  • Metode, properti, dan peristiwa yang memiliki nama yang sama harus berbeda lebih dari sekadar jenis pengembalian.

Contoh berikut menggambarkan persyaratan bahwa nama anggota harus unik dalam cakupannya. Ini mendefinisikan kelas bernama Converter yang mencakup empat anggota bernama Conversion. Tiga adalah metode, dan satu adalah properti. Metode yang menyertakan parameter Int64 diberi nama unik, tetapi dua metode dengan parameter Int32 tidak, karena nilai pengembalian tidak dianggap sebagai bagian dari tanda tangan anggota. Properti Conversion juga melanggar persyaratan ini, karena properti tidak dapat memiliki nama yang sama dengan metode yang kelebihan beban.

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
'                                ~~~~~~~~~~

Bahasa individual mencakup kata kunci yang unik, sehingga bahasa yang menargetkan runtime bahasa umum juga harus menyediakan semacam mekanisme untuk merujuk pengidentifikasi (seperti nama jenis) yang bertepatan dengan kata kunci. Misalnya, case adalah kata kunci dalam C# dan Visual Basic. Namun, contoh Visual Basic berikut mampu membedakan kelas bernama case dari kata kunci case dengan menggunakan kurung kurawal buka dan tutup. Jika tidak, contoh akan menghasilkan pesan kesalahan, "Kata kunci tidak valid sebagai pengidentifikasi," dan gagal dikompilasi.

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

Contoh C# berikut dapat membuat instans kelas case dengan menggunakan simbol @ untuk membedakan pengidentifikasi dari kata kunci bahasa. Tanpa itu, pengompilasi C# akan menampilkan dua pesan kesalahan, "Jenis yang diharapkan" dan "Istilah ekspresi tidak valid 'case'."

using System;

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

Konversi jenis

Spesifikasi Bahasa Umum mendefinisikan dua operator konversi:

  • op_Implicit, yang digunakan untuk memperluas konversi yang tidak mengakibatkan hilangnya data atau presisi. Misalnya, struktur Decimal menyertakan operator op_Implicit yang kelebihan beban untuk mengonversi nilai jenis integral dan nilai Char menjadi nilai Decimal.

  • op_Explicit, yang digunakan untuk mempersempit konversi yang dapat mengakibatkan hilangnya besaran (nilai dikonversi menjadi nilai yang memiliki rentang yang lebih kecil) atau presisi. Misalnya, struktur Decimal menyertakan operator op_Explicit yang kelebihan beban untuk mengonversi nilai Double dan Single menjadi Decimal dan mengonversi nilai Decimal menjadi nilai integral, Double, Single, dan Char.

Namun, tidak semua bahasa mendukung operator kelebihan beban atau definisi operator kustom. Jika Anda memilih untuk mengimplementasikan operator konversi ini, Anda juga harus menyediakan cara alternatif untuk melakukan konversi. Kami menyarankan agar Anda menyediakan metode FromXxx dan ToXxx.

Contoh berikut mendefinisikan konversi implisit dan eksplisit yang sesuai dengan CLS. Ini membuat kelas UDouble yang mewakili angka floating-point yang tidak bertanda dan berpresisi ganda. Ini menyediakan konversi implisit dari UDouble ke Double dan untuk konversi eksplisit dari UDouble ke Single, Double ke UDouble, dan Single ke UDouble. Ini juga mendefinisikan metode ToDouble sebagai alternatif untuk operator konversi implisit dan metode ToSingle, FromDoubledan FromSingle sebagai alternatif untuk operator konversi eksplisit.

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

Larik

Larik yang sesuai dengan CLS sesuai dengan aturan berikut:

  • Semua dimensi larik harus memiliki batas bawah nol. Contoh berikut membuat larik yang tidak sesuai dengan CLS dengan batas bawah satu. Meskipun ada atribut CLSCompliantAttribute, pengompilasi tidak mendeteksi bahwa larik yang dikembalikan oleh metode Numbers.GetTenPrimes tidak sesuai dengan 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
    
  • Semua elemen larik harus terdiri dari jenis yang sesuai dengan CLS. Contoh berikut mendefinisikan dua metode yang mengembalikan larik yang tidak sesuai dengan CLS. Yang pertama mengembalikan larik nilai UInt32. Yang kedua mengembalikan larik Object yang menyertakan nilai Int32 dan UInt32. Meskipun pengompilasi mengidentifikasi larik pertama sebagai tidak sesuai karena jenisnya UInt32, ia gagal mengenali bahwa larik kedua menyertakan elemen yang tidak mematuhi 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()
    '                              ~~~~~~~~~~~~
    
  • Resolusi kelebihan beban untuk metode yang memiliki parameter larik didasarkan pada fakta bahwa mereka adalah larik dan pada jenis elemen mereka. Untuk alasan ini, definisi berikut dari metode GetSquares yang kelebihan beban mematuhi 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
    

Antarmuka

Antarmuka yang sesuai dengan CLS dapat menentukan properti, peristiwa, dan metode virtual (metode tanpa implementasi). Antarmuka yang sesuai dengan CLS tidak dapat memiliki hal berikut:

  • Metode statik atau bidang statik. Pengompilasi C# dan Visual Basic menghasilkan kesalahan pengompilasi jika Anda menentukan anggota statik dalam antarmuka.

  • Bidang. Pengompilasi C# dan Visual Basic menghasilkan kesalahan pengompilasi jika Anda menentukan bidang dalam antarmuka.

  • Metode yang tidak sesuai dengan CLS. Misalnya, definisi antarmuka berikut menyertakan sebuah metode, INumber.GetUnsigned, yang ditandai sebagai tidak mematuhi CLS. Contoh ini menghasilkan peringatan pengompilasi.

    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
    '                                      ~~~~~~~~~~~
    

    Karena aturan ini, jenis yang mematuhi CLS tidak diharuskan untuk mengimplementasikan anggota yang tidak mematuhi CLS. Jika kerangka kerja yang sesuai dengan CLS mengekspos kelas yang mengimplementasikan antarmuka yang tidak sesuai dengan CLS, itu juga harus memberikan implementasi konkret atas semua anggota yang tidak mematuhi CLS.

Pengompilasi bahasa yang sesuai dengan CLS juga harus memungkinkan kelas untuk menyediakan implementasi terpisah dari anggota yang memiliki nama dan tanda tangan yang sama di beberapa antarmuka. C# dan Visual Basic mendukung implementasi antarmuka eksplisit untuk menyediakan implementasi yang berbeda dari metode yang bernama identik. Visual Basic juga mendukung kata kunci Implements, yang memungkinkan Anda untuk secara eksplisit menunjuk antarmuka dan anggota mana yang diterapkan anggota tertentu. Contoh berikut menggambarkan skenario ini dengan menentukan kelas Temperature yang mengimplementasikan antarmuka ICelsius dan IFahrenheit sebagai implementasi antarmuka eksplisit.

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: {0} degrees",
                        cTemp.GetTemperature());
      Console.WriteLine("Temperature in Fahrenheit: {0} degrees",
                        fTemp.GetTemperature());
   }
}
// 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

Enumerasi

Enumerasi yang sesuai dengan CLS harus mengikuti aturan-aturan ini:

  • Jenis yang mendasari enumerasi harus berupa bilangan bulat intrinsik yang mematuhi CLS (Byte, Int16, Int32, atau Int64). Misalnya, kode berikut mencoba menentukan enumerasi yang jenis yang mendasarinya adalah UInt32 dan menghasilkan peringatan pengompilasi.

    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
    '                ~~~~
    
  • Jenis enumerasi harus memiliki satu bidang instans bernama Value__ yang ditandai dengan atribut FieldAttributes.RTSpecialName. Ini memungkinkan Anda untuk mereferensikan nilai bidang secara implisit.

  • Enumerasi mencakup bidang statik literal yang jenisnya cocok dengan jenis enumerasi itu sendiri. Misalnya, jika Anda menentukan enumerasi State dengan nilai State.On dan State.Off, State.On dan State.Off keduanya adalah bidang statik literal yang jenisnya adalah State.

  • Ada dua jenis enumerasi:

    • Enumerasi yang mewakili sekumpulan nilai bilangan bulat yang saling eksklusif dan bernama. Jenis enumerasi ini diindikasikan dengan tidak adanya atribut kustom System.FlagsAttribute.

    • Enumerasi yang merepresentasikan satu set bendera bit yang dapat digabungkan untuk menghasilkan nilai yang tidak bernama. Jenis enumerasi ini diindikasikan dengan adanya atribut kustom System.FlagsAttribute.

    Untuk informasi lainnya, lihat dokumentasi untuk struktur Enum.

  • Nilai enumerasi tidak terbatas pada rentang nilai yang ditentukan. Dengan kata lain, rentang nilai dalam enumerasi adalah rentang nilai yang mendasarinya. Anda dapat menggunakan metode Enum.IsDefined untuk menentukan apakah nilai yang ditentukan adalah anggota enumerasi.

Anggota jenis pada umumnya

Spesifikasi Bahasa Umum mengharuskan semua bidang dan metode untuk diakses sebagai anggota kelas tertentu. Oleh karena itu, bidang dan metode statik global (yaitu, bidang atau metode statik yang didefinisikan terpisah dari jenis) tidak sesuai dengan CLS. Jika Anda mencoba menyertakan bidang atau metode global dalam kode sumber Anda, pengompilasi C# dan Visual Basic menghasilkan kesalahan pengompilasi.

Spesifikasi Bahasa Umum hanya mendukung konvensi panggilan terkelola standar. Ini tidak mendukung konvensi dan metode panggilan yang tidak dikelola dengan daftar argumen variabel yang ditandai dengan kata kunci varargs. Untuk daftar argumen variabel yang kompatibel dengan konvensi panggilan terkelola standar, gunakan atribut ParamArrayAttribute atau implementasi bahasa individual, seperti kata kunci params di C# dan kata kunci ParamArray dalam Visual Basic.

Aksesibilitas anggota

Menimpa anggota yang diwariskan tidak dapat mengubah aksesibilitas anggota tersebut. Misalnya, metode publik di kelas dasar tidak dapat ditimpa oleh metode privat di kelas turunan. Ada satu pengecualian: anggota protected internal (dalam C#) atau Protected Friend (dalam Visual Basic) dalam satu rakitan yang ditimpa oleh jenis dalam rakitan yang berbeda. Dalam hal ini, aksesibilitas penimpaan adalah Protected.

Contoh berikut mengilustrasikan kesalahan yang dihasilkan ketika atribut CLSCompliantAttribute diatur ke true, dan Human, yang merupakan kelas yang berasal dari Animal, mencoba mengubah aksesibilitas properti Species dari publik ke privat. Contoh berhasil dikompilasi jika aksesibilitasnya diubah menjadi publik.

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

Jenis dalam tanda tangan anggota harus dapat diakses setiap kali anggota tersebut dapat diakses. Misalnya, ini berarti bahwa anggota publik tidak dapat menyertakan parameter yang jenisnya bersifat privat, terlindungi, atau internal. Contoh berikut mengilustrasikan kesalahan pengompilasi yang dihasilkan ketika konstruktor kelas StringWrapper mengekspos nilai enumerasi StringOperationType internal yang menentukan bagaimana nilai string harus dibungkus.

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)
'                              ~~~~~~~~~~~~~~~~~~~

Jenis dan anggota generik

Jenis bersarang selalu memiliki setidaknya parameter generik sebanyak jenis penutupnya. Ini sesuai dengan posisi terhadap parameter generik dalam jenis penutup. Jenis generik juga dapat menyertakan parameter generik baru.

Hubungan antara parameter jenis generik dari jenis yang mewadahi dan jenis bersarangnya dapat disembunyikan oleh sintaksis masing-masing bahasa. Dalam contoh berikut, jenis generik Outer<T> berisi dua kelas bersarang, Inner1A dan Inner1B<U>. Panggilan ke metode ToString, yang diwarisi setiap kelas dari Object.ToString(), menunjukkan bahwa setiap kelas bersarang menyertakan parameter jenis kelas yang mewadahinya.

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]

Nama jenis generik dikodekan dalam bentuk name`n, di mana name adalah nama jenis, ` adalah literal karakter, dan n adalah jumlah parameter yang dideklarasikan pada jenis, atau, untuk jenis generik bersarang, jumlah parameter jenis yang baru diperkenalkan. Pengodean nama jenis generik ini terutama menarik bagi pengembang yang menggunakan refleksi untuk mengakses jenis generik yang sesuai CLS di perpustakaan.

Jika batasan diterapkan ke jenis generik, semua jenis yang digunakan sebagai batasan juga harus sesuai dengan CLS. Contoh berikut mendefinisikan kelas bernama BaseClass yang tidak mematuhi CLS dan kelas generik bernama BaseCollection yang parameter jenisnya harus berasal dari BaseClass. Tetapi karena BaseClass tidak sesuai dengan CLS, pengompilasi mengeluarkan peringatan.

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)
'                                        ~~~~~~~~~

Jika jenis generik berasal dari jenis dasar generik, ia harus mendeklarasikan kembali batasan apa pun sehingga dapat menjamin bahwa batasan pada jenis dasar juga terpenuhi. Contoh berikut mendefinisikan Number<T> yang dapat merepresentasikan jenis numerik apa pun. Ini juga mendefinisikan kelas FloatingPoint<T> yang merepresentasikan nilai floating-point. Namun, kode sumber gagal dikompilasi, karena tidak menerapkan batasan pada Number<T> (bahwa T harus merupakan jenis nilai) ke 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>
{
   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)
'                                                          ~

Contoh berhasil dikompilasi jika batasan ditambahkan ke kelas 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

Spesifikasi Bahasa Umum memberlakukan model per pembuatan instans konservatif untuk jenis bersarang dan anggota yang dilindungi. Jenis generik terbuka tidak dapat mengekspos bidang atau anggota dengan tanda tangan yang berisi pembuatan instans tertentu dari jenis generik yang bersarang dan dilindungi. Jenis non-generik yang memperluas pembuatan instans tertentu dari kelas atau antarmuka dasar generik tidak dapat mengekspos bidang atau anggota dengan tanda tangan yang berisi pembuatan instans yang berbeda dari jenis generik bersarang dan dilindungi.

Contoh berikut mendefinisikan suatu jenis generik, C1<T> (atau C1(Of T) dalam Visual Basic), dan suatu kelas yang dilindungi, C1<T>.N (atau C1(Of T).N dalam Visual Basic). C1<T> memiliki dua metode, M1 dan M2. Namun, M1 tidak sesuai dengan CLS karena mencoba mengembalikan objek C1<int>.N (atau C1(Of Integer).N) dari C1<T> (atau C1(Of T)). Kelas kedua, C2, berasal dari C1<long> (atau C1(Of Long)). Kelas ini memiliki dua metode, M3 dan M4. M3 tidak sesuai dengan CLS karena mencoba mengembalikan objek C1<int>.N (atau C1(Of Integer).N) dari subkelas C1<long>. Pengompilasi bahasa bisa menjadi lebih ketat. Dalam contoh ini, Visual Basic menampilkan kesalahan saat mencoba mengompilasi 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)  
'                             ~~~~~~~~~~~~~

Konstruktor

Konstruktor di kelas dan struktur yang sesuai dengan CLS harus mengikuti aturan-aturan ini:

  • Konstruktor kelas turunan harus memanggil konstruktor instans dari kelas dasarnya sebelum mengakses data instans yang diwariskan. Persyaratan ini karena konstruktor kelas dasar tidak diwarisi oleh kelas turunannya. Aturan ini tidak berlaku untuk struktur, yang tidak mendukung pewarisan langsung.

    Biasanya, pengompilasi menegakkan aturan ini secara independen dari kepatuhan CLS, seperti yang ditunjukkan oleh contoh berikut. Ini membuat kelas Doctor yang berasal dari kelas Person, tetapi kelas Doctor gagal memanggil konstruktor kelas Person untuk menginisialisasi bidang instans yang diwariskan.

    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()
    '                  ~~~
    
  • Konstruktor objek tidak dapat dipanggil kecuali untuk membuat objek. Selain itu, suatu objek tidak dapat diinisialisasi dua kali. Misalnya, ini berarti bahwa Object.MemberwiseClone dan metode deserialisasi tidak boleh memanggil konstruktor.

Properti

Properti dalam jenis yang sesuai dengan CLS harus mengikuti aturan berikut:

  • Properti harus memiliki setter, getter, atau keduanya. Dalam rakitan, ini diimplementasikan sebagai metode khusus, yang berarti bahwa mereka akan muncul sebagai metode terpisah (getter diberi nama get_propertyname dan setter adalah set_propertyname) yang ditandai sebagai SpecialName dalam metadata rakitan. Pengompilasi C# dan Visual Basic menerapkan aturan ini secara otomatis tanpa perlu menerapkan atribut CLSCompliantAttribute.

  • Jenis properti adalah jenis pengembalian getter properti dan argumen terakhir dari setter. Jenis ini harus sesuai dengan CLS, dan argumen tidak dapat ditetapkan ke properti oleh referensi (yaitu, mereka tidak dapat berupa pointer terkelola).

  • Jika sebuah properti memiliki getter dan setter, keduanya harus virtual, stati, atau instans. Pengompilasi C# dan Visual Basic secara otomatis menegakkan aturan ini melalui sintaksis definisi properti mereka.

Acara

Peristiwa didefinisikan berdasarkan namanya dan jenisnya. Jenis peristiwa adalah delegasi yang digunakan untuk menunjukkan peristiwa. Misalnya, peristiwa AppDomain.AssemblyResolve berjenis ResolveEventHandler. Selain peristiwa itu sendiri, tiga metode dengan nama berdasarkan nama peristiwa menyediakan implementasi peristiwa dan ditandai sebagai SpecialName dalam metadata rakitan:

  • Metode untuk menambahkan penanganan aktivitas, bernama add_EventName. Misalnya, metode langganan peristiwa untuk peristiwa AppDomain.AssemblyResolve diberi nama add_AssemblyResolve.

  • Metode untuk menambahkan penanganan aktivitas, bernama remove_EventName. Misalnya, metode penghapusan untuk peristiwa AppDomain.AssemblyResolve diberi nama remove_AssemblyResolve.

  • Metode untuk mengindikasikan bahwa peristiwa telah terjadi, bernama raise_EventName.

Catatan

Sebagian besar aturan Spesifikasi Bahasa Umum mengenai peristiwa diimplementasikan oleh pengompilasi bahasa dan transparan untuk pengembang komponen.

Metode untuk menambahkan, menghapus, dan menaikkan peristiwa harus memiliki aksesibilitas yang sama. Mereka semua juga harus bersifat statik, berupa instans, atau virtual. Metode untuk menambahkan dan menghapus suatu peristiwa memiliki satu parameter yang jenisnya adalah jenis delegasi acara. Metode tambah dan hapus harus keduanya ada atau keduanya tidak ada.

Contoh berikut mendefinisikan kelas bernama Temperature yang mematuhi CLS yang menaikkan peristiwa TemperatureChanged jika perubahan suhu antara dua pembacaan sama atau melebihi nilai ambang batas. Kelas Temperature secara eksplisit mendefinisikan metode raise_TemperatureChanged sehingga dapat secara selektif menjalankan penanganan aktivitas.

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 {0} to {1}", e.OldTemperature, e.CurrentTemperature);
   }

   public void DuplicateTemperatureNotification(Object sender, TemperatureChangedEventArgs e)
   {
      Console.WriteLine("Notification 2: The temperature changed from {0} to {1}", e.OldTemperature, 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

Overload

Spesifikasi Bahasa Umum memberlakukan persyaratan berikut pada anggota yang kelebihan beban:

  • Anggota dapat kelebihan beban berdasarkan jumlah parameter dan jenis parameter apa pun. Memanggil konvensi, jenis kembali, pengubah kustom yang diterapkan pada metode atau parameternya, dan apakah parameter dilewatkan berdasarkan nilai atau dengan referensi tidak dipertimbangkan saat membedakan antara kelebihan beban. Misalnya, lihat kode untuk persyaratan bahwa nama harus unik dalam cakupan di bagian Konvensi penamaan.

  • Hanya properti dan metode yang dapat kelebihan beban. Bidang dan peristiwa tidak dapat kelebihan beban.

  • Metode generik dapat kelebihan beban berdasarkan jumlah parameter generiknya.

Catatan

Operator op_Explicit dan op_Implicit adalah pengecualian untuk aturan bahwa nilai pengembalian tidak dianggap sebagai bagian dari tanda tangan metode untuk resolusi kelebihan beban. Kedua operator ini dapat kelebihan beban berdasarkan parameter dan nilai pengembaliannya.

Pengecualian

Objek pengecualian harus berasal dari System.Exception atau dari jenis lain yang berasal dari System.Exception. Contoh berikut mengilustrasikan kesalahan pengompilasi yang terjadi ketika kelas kustom bernama ErrorClass digunakan untuk penanganan pengecualian.

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
'             ~~~~~~~~~~~~~~

Untuk memperbaiki kesalahan ini, kelas ErrorClass harus mewarisi dari System.Exception. Selain itu, properti Message harus ditimpa. Contoh berikut mengoreksi kesalahan ini untuk menentukan kelas ErrorClass yang sesuai dengan CLS.

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

Atribut

Dalam rakitan .NET, atribut kustom menyediakan mekanisme yang dapat diperluas untuk menyimpan atribut kustom dan mengambil metadata tentang objek pemrograman, seperti rakitan, jenis, anggota, dan parameter metode. Atribut kustom harus berasal dari System.Attribute atau dari jenis yang berasal dari System.Attribute.

Contoh berikut melanggar aturan ini. Ini mendefinisikan kelas NumericAttribute yang tidak berasal dari System.Attribute. Kesalahan pengompilasi hanya terjadi ketika atribut yang tidak sesuai dengan CLS diterapkan, bukan saat kelas ditentukan.

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
'     ~~~~~~~~~~~~~

Konstruktor atau properti atribut yang sesuai dengan CLS hanya dapat mengekspos jenis berikut:

Contoh berikut mendefinisikan kelas DescriptionAttribute yang berasal dari Atribut. Konstruktor kelas memiliki parameter jenis Descriptor, sehingga kelas tidak sesuai dengan CLS. Pengompilasi C# memancarkan peringatan tetapi berhasil mengompilasi, sedangkan pengompilasi Visual Basic tidak mengeluarkan peringatan atau kesalahan.

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

Atribut CLSCompliantAttribute

Atribut CLSCompliantAttribute digunakan untuk mengindikasikan apakah elemen program mematuhi Spesifikasi Bahasa Umum. Konstruktor CLSCompliantAttribute(Boolean) mencakup satu parameter yang diperlukan, isCompliant, yang mengindikasikan apakah elemen program mematuhi CLS.

Pada waktu kompilasi, pengompilasi mendeteksi elemen yang tidak sesuai yang dianggap sesuai dengan CLS dan mengeluarkan peringatan. Pengompilasi tidak memancarkan peringatan untuk jenis atau anggota yang secara eksplisit dinyatakan tidak patuh.

Pengembang komponen dapat menggunakan atribut CLSCompliantAttribute dengan dua cara:

  • Untuk menentukan bagian antarmuka publik yang diekspos oleh komponen yang sesuai dengan CLS dan bagian-bagian yang tidak sesuai dengan CLS. Ketika atribut digunakan untuk menandai elemen program tertentu sebagai sesuai dengan CLS, penggunaannya menjamin bahwa elemen-elemen tersebut dapat diakses dari semua bahasa dan alat yang menargetkan .NET.

  • Untuk memastikan bahwa antarmuka publik perpustakaan komponen hanya mengekspos elemen program yang sesuai dengan CLS. Jika elemen tidak sesuai dengan CLS, pengompilasi umumnya akan mengeluarkan peringatan.

Peringatan

Dalam beberapa kasus, pengompilasi bahasa memberlakukan aturan yang mematuhi CLS terlepas dari apakah atribut CLSCompliantAttribute digunakan. Misalnya, mendefinisikan anggota statik dalam antarmuka melanggar aturan CLS. Dalam hal ini, jika Anda menentukan anggota static (dalam C#) atau Shared (dalam Visual Basic) di antarmuka, pengompilasi C# dan Visual Basic menampilkan pesan kesalahan dan gagal mengompilasi aplikasi.

Atribut CLSCompliantAttribute ditandai dengan atribut AttributeUsageAttribute yang memiliki nilai AttributeTargets.All. Nilai ini memungkinkan Anda menerapkan atribut CLSCompliantAttribute ke elemen program apa pun, termasuk rakitan, modul, jenis (kelas, struktur, enumerasi, antarmuka, dan delegasi), jenis anggota (konstruktor, metode, properti, bidang, dan peristiwa), parameter, parameter generik, dan nilai pengembalian. Namun, dalam praktiknya, Anda harus menerapkan atribut hanya untuk rakitan, jenis, dan anggota jenis. Jika tidak, pengompilasi mengabaikan atribut dan terus menghasilkan peringatan pengompilasi setiap kali mereka menemukan parameter, parameter generik, atau nilai pengembalian yang tidak sesuai di antarmuka publik perpustakaan Anda.

Nilai atribut CLSCompliantAttribute diwariskan oleh elemen program yang terkandung. Misalnya, jika rakitan ditandai sebagai sesuai dengan CLS, jenisnya juga sesuai dengan CLS. Jika jenis ditandai sebagai sesuai dengan CLS, jenis bersarang dan anggotanya juga sesuai dengan CLS.

Anda dapat secara eksplisit menimpa kepatuhan yang diwariskan dengan menerapkan atribut CLSCompliantAttribute ke elemen program yang terkandung. Misalnya, Anda dapat menggunakan atribut CLSCompliantAttribute dengan nilai isCompliantfalse untuk menentukan jenis yang tidak sesuai dalam rakitan yang sesuai, dan Anda dapat menggunakan atribut dengan nilai isComplianttrue untuk menentukan jenis yang sesuai dalam rakitan yang tidak sesuai. Anda juga dapat menentukan anggota yang tidak patuh dalam jenis yang sesuai. Namun, jenis yang tidak sesuai tidak dapat memiliki anggota yang sesuai, sehingga Anda tidak dapat menggunakan atribut dengan nilai isComplianttrue untuk menimpa pewarisan dari jenis yang tidak sesuai.

Saat mengembangkan komponen, Anda harus selalu menggunakan atribut CLSCompliantAttribute untuk menunjukkan apakah rakitan, jenisnya, dan anggotanya mematuhi CLS.

Untuk membuat komponen yang mematuhi CLS:

  1. Gunakan CLSCompliantAttribute untuk menandai rakitan Anda sebagai mematuhi CLS.

  2. Tandai jenis yang diekspos secara publik di rakitan yang tidak sesuai dengan CLS sebagai tidak patuh.

  3. Tandai setiap anggota yang diekspos secara publik dalam jenis yang mematuhi CLS sebagai tidak patuh.

  4. Sediakan alternatif yang sesuai dengan CLS untuk anggota yang tidak mematuhi CLS.

Jika Anda telah berhasil menandai semua jenis dan anggota yang tidak sesuai, pengompilasi Anda tidak boleh mengeluarkan peringatan ketidakpatuhan apa pun. Namun, Anda harus mengindikasikan anggota mana yang tidak mematuhi CLS dan mencantumkan alternatif yang sesuai dengan CLS mereka dalam dokumentasi produk Anda.

Contoh berikut menggunakan atribut CLSCompliantAttribute untuk menentukan rakitan yang mematuhi CLS dan jenis, CharacterUtilities, yang memiliki dua anggota yang tidak mematuhi CLS. Karena kedua anggota ditandai dengan atribut CLSCompliant(false), pengompilasi tidak menghasilkan peringatan. Kelas ini juga menyediakan alternatif yang sesuai dengan CLS untuk kedua metode tersebut. Biasanya, kami hanya akan menambahkan dua kelebihan beban ke metode ToUTF16 untuk menyediakan alternatif yang mematuhi CLS. Namun, karena metode tidak dapat kelebihan beban berdasarkan nilai pengembalian, nama metode yang sesuai dengan CLS berbeda dari nama metode yang tidak sesuai.

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

Jika Anda mengembangkan aplikasi alih-alih pustaka (yaitu, jika Anda tidak mengekspos jenis atau anggota yang dapat dikonsumsi oleh pengembang aplikasi lain), kepatuhan CLS dari elemen program yang dikonsumsi aplikasi Anda dianggap penting hanya jika bahasa Anda tidak mendukungnya. Dalam hal ini, pengompilasi bahasa Anda akan menghasilkan kesalahan ketika Anda mencoba menggunakan elemen yang tidak sesuai dengan CLS.

Interoperabilitas lintas bahasa

Kemandirian bahasa memiliki beberapa kemungkinan makna. Satu makna melibatkan konsumsi jenis dengan mulus yang ditulis dalam satu bahasa dari aplikasi yang ditulis dalam bahasa lain. Makna kedua, yang merupakan fokus dari artikel ini, melibatkan menggabungkan kode yang ditulis dalam berbagai bahasa menjadi satu rakitan .NET.

Contoh berikut mengilustrasikan interoperabilitas lintas bahasa dengan membuat pustaka kelas bernama Utilities.dll yang mencakup dua kelas, NumericLib dan StringLib. Kelas NumericLib ditulis dalam C#, dan kelas StringLib ditulis dalam Visual Basic. Berikut adalah kode sumber untuk StringUtil.vb, yang mencakup satu anggota, ToTitleCase, di kelasnya 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

Berikut adalah kode sumber untuk NumberUtil.cs, yang mendefinisikan kelas NumericLib yang memiliki dua anggota, IsEven dan NearZero.

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;
   }
}

Untuk mengemas dua kelas dalam satu rakitan, Anda harus mengompilasinya ke dalam modul. Untuk mengompilasi file kode sumber Visual Basic ke dalam modul, gunakan perintah ini:

vbc /t:module StringUtil.vb

Untuk informasi selengkapnya tentang sintaks baris perintah pengompilasi Visual Basic, lihat Membangun dari Baris Perintah.

Untuk mengompilasi file kode sumber C# ke dalam modul, gunakan perintah ini:

csc /t:module NumberUtil.cs

Anda kemudian menggunakan opsi Linker untuk mengompilasi dua modul ke dalam satu rakitan:

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

Contoh berikut kemudian memanggil metode NumericLib.NearZero dan StringLib.ToTitleCase. Baik kode Visual Basic dan kode C# dapat mengakses metode di kedua kelas.

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

Untuk mengompilasi kode Visual Basic, gunakan perintah ini:

vbc example.vb /r:UtilityLib.dll

Untuk mengompilasi dengan C#, ubah nama pengompilasi dari vbc ke csc, dan ubah ekstensi file dari .vb ke .cs:

csc example.cs /r:UtilityLib.dll