Taalonafhankelijkheid en taalonafhankelijke onderdelen

.NET is taalonafhankelijk. Dit betekent dat u als ontwikkelaar kunt ontwikkelen in een van de vele talen die gericht zijn op .NET-implementaties, zoals C#, F# en Visual Basic. U hebt toegang tot de typen en leden van klassenbibliotheken die zijn ontwikkeld voor .NET-implementaties zonder dat u de taal hoeft te kennen waarin ze oorspronkelijk zijn geschreven en zonder dat u de conventies van de oorspronkelijke taal hoeft te volgen. Als u een onderdeelontwikkelaar bent, is uw onderdeel toegankelijk voor elke .NET-app, ongeacht de taal.

Notitie

In dit eerste deel van dit artikel wordt het maken van taalonafhankelijke onderdelen besproken, dat wil gezegd, onderdelen die kunnen worden gebruikt door apps die in elke taal zijn geschreven. U kunt ook één onderdeel of app maken op basis van broncode die in meerdere talen is geschreven; zie Interoperabiliteit tussen talen in het tweede deel van dit artikel.

Als u volledig wilt communiceren met andere objecten die in elke taal zijn geschreven, moeten objecten alleen aan bellers worden blootgesteld die voor alle talen gebruikelijk zijn. Deze algemene set functies wordt gedefinieerd door de Common Language Specification (CLS), een set regels die van toepassing zijn op gegenereerde assembly's. De Common Language Specification is gedefinieerd in Partition I, Components 7 tot en met 11 van de ECMA-335 Standard: Common Language Infrastructure.

Als uw onderdeel voldoet aan de Algemene taalspecificatie, is het gegarandeerd cls-compatibel en is het toegankelijk vanuit code in assembly's die zijn geschreven in elke programmeertaal die de CLS ondersteunt. U kunt bepalen of uw onderdeel tijdens het compileren voldoet aan de Common Language Specification door het kenmerk CLSCompliantAttribute toe te passen op uw broncode. Zie het kenmerk CLSCompliantAttribute voor meer informatie.

CLS-nalevingsregels

In deze sectie worden de regels besproken voor het maken van een CLS-compatibel onderdeel. Zie Partition I, Component 11 van de ECMA-335 Standard: Common Language Infrastructure voor een volledige lijst met regels.

Notitie

De Common Language Specification bespreekt elke regel voor CLS-naleving zoals deze van toepassing is op consumenten (ontwikkelaars die programmatisch toegang hebben tot een onderdeel dat compatibel is met CLS), frameworks (ontwikkelaars die een taalcompilator gebruiken om CLS-compatibele bibliotheken te maken) en extenders (ontwikkelaars die een hulpprogramma maken, zoals een taalcompilator of een codeparser waarmee CLS-compatibele onderdelen worden gemaakt). Dit artikel richt zich op de regels die van toepassing zijn op frameworks. Houd er echter rekening mee dat sommige regels die van toepassing zijn op extenders ook van toepassing kunnen zijn op assembly's die zijn gemaakt met Behulp van Reflection.Emit.

Als u een onderdeel wilt ontwerpen dat onafhankelijk is van de taal, hoeft u alleen de regels voor CLS-naleving toe te passen op de openbare interface van uw onderdeel. Uw privé-implementatie hoeft niet aan de specificatie te voldoen.

Belangrijk

De regels voor CLS-naleving zijn alleen van toepassing op de openbare interface van een onderdeel, niet op de privé-implementatie ervan.

Niet-ondertekende gehele getallen zijn Byte bijvoorbeeld niet cls-compatibel. Omdat in de Person klasse in het volgende voorbeeld een eigenschap van het Age type UInt16wordt weergegeven, wordt in de volgende code een compilerwaarschuwing weergegeven.

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

U kunt de Person klasse CLS-compatibel maken door het type van de Age eigenschap UInt16 te Int16wijzigen in , een geheel getal dat compatibel is met CLS en een 16-bits geheel getal dat is ondertekend. U hoeft het type privéveld personAge niet te wijzigen.

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

De openbare interface van een bibliotheek bestaat uit het volgende:

  • Definities van openbare klassen.

  • Definities van de openbare leden van openbare klassen en definities van leden die toegankelijk zijn voor afgeleide klassen (dat wil gezegd beveiligde leden).

  • Parameters en retourtypen van openbare methoden van openbare klassen en parameters en retourtypen die toegankelijk zijn voor afgeleide klassen.

De regels voor CLS-naleving worden vermeld in de volgende tabel. De tekst van de regels wordt volledig overgenomen uit de ECMA-335 Standard: Common Language Infrastructure, dat copyright 2012 is van Ecma International. Meer gedetailleerde informatie over deze regels vindt u in de volgende secties.

Categorie Onderwerp Regel Regelnummer
Toegankelijkheid Toegankelijkheid van leden Toegankelijkheid wordt niet gewijzigd bij het overschrijven van overgenomen methoden, behalve wanneer een methode wordt overschreven die is overgenomen van een andere assembly met toegankelijkheid family-or-assembly. In dit geval heeft de onderdrukking toegankelijkheid family. 10
Toegankelijkheid Toegankelijkheid van leden De zichtbaarheid en toegankelijkheid van typen en leden moeten zodanig zijn dat typen in de handtekening van een lid zichtbaar en toegankelijk zijn wanneer het lid zelf zichtbaar en toegankelijk is. Een openbare methode die buiten de assembly zichtbaar is, heeft bijvoorbeeld geen argument waarvan het type alleen binnen de assembly zichtbaar is. De zichtbaarheid en toegankelijkheid van typen die een geïnstantieerd algemeen type vormen dat in de handtekening van een lid wordt gebruikt, zijn zichtbaar en toegankelijk wanneer het lid zelf zichtbaar en toegankelijk is. Een geïnstantieerd algemeen type dat aanwezig is in de handtekening van een lid dat buiten de assembly zichtbaar is, heeft bijvoorbeeld geen algemeen argument waarvan het type alleen binnen de assembly zichtbaar is. 12
Matrices Arrays Matrices hebben elementen met een CLS-compatibel type en alle dimensies van de matrix moeten ondergrenzen van nul hebben. Alleen het feit dat een item een matrix is en het elementtype van de matrix moet worden vereist om onderscheid te maken tussen overbelastingen. Wanneer overbelasting is gebaseerd op twee of meer matrixtypen, worden de elementtypen benoemd. 16
Kenmerken Kenmerken Kenmerken moeten van het type System.Attributezijn, of een type dat hiervan overkomt. 41
Kenmerken Kenmerken De CLS staat alleen een subset van de coderingen van aangepaste kenmerken toe. De enige typen die in deze coderingen worden weergegeven, zijn (zie Partitie IV): System.Type, , System.ByteSystem.Int16System.BooleanSystem.StringSystem.CharSystem.Int32, System.Int64, System.Singleen System.Doubleelk opsommingstype op basis van een geheel getal dat compatibel is met CLS. 34
Kenmerken Kenmerken De CLS staat niet openbaar zichtbare vereiste modifiers toe (modreqzie Partitie II), maar staat optionele modifiers toe (modoptzie Partitie II) die het niet begrijpt. 35
Constructors Constructeurs Een objectconstructor roept een instantieconstructor van de basisklasse aan voordat er toegang wordt geboden tot overgenomen exemplaargegevens. (Dit geldt niet voor waardetypen die geen constructors nodig hebben.) 21
Constructors Constructeurs Een objectconstructor mag niet worden aangeroepen, behalve als onderdeel van het maken van een object en een object mag niet tweemaal worden geïnitialiseerd. 22
Opsommingen Opsommingen Het onderliggende type van een enum moet een ingebouwd CLS-geheel getal zijn, de naam van het veld moet "value__" zijn en dat veld moet worden gemarkeerd RTSpecialName. 7
Opsommingen Opsommingen Er zijn twee verschillende soorten opsommingen, aangegeven door de aanwezigheid of afwezigheid van het System.FlagsAttribute aangepaste kenmerk (zie Partition IV Library). Eén vertegenwoordigt benoemde geheel getalwaarden; de andere vertegenwoordigt benoemde bitvlagmen die kunnen worden gecombineerd om een niet-benoemde waarde te genereren. De waarde van een enum is niet beperkt tot de opgegeven waarden. 8
Opsommingen Opsommingen Letterlijke statische velden van een enum moeten het type enum zelf hebben. 9
gebeurtenis Gebeurtenissen De methoden voor het implementeren van een gebeurtenis worden gemarkeerd SpecialName in de metagegevens. 29
gebeurtenis Gebeurtenissen De toegankelijkheid van een gebeurtenis en haar toegangsrechten is identiek. 30
gebeurtenis Gebeurtenissen De add en remove methoden voor een gebeurtenis zijn beide aanwezig of afwezig. 31
gebeurtenis Gebeurtenissen De add en remove methoden voor een gebeurtenis nemen elk één parameter waarvan het type het type van de gebeurtenis definieert en dat moet worden afgeleid van System.Delegate. 32
gebeurtenis Gebeurtenissen Gebeurtenissen moeten voldoen aan een specifiek naamgevingspatroon. Het kenmerk SpecialName waarnaar in CLS-regel 29 wordt verwezen, wordt genegeerd in de juiste naamvergelijkingen en voldoet aan de id-regels. 33
Uitzonderingen Uitzonderingen Objecten die worden gegenereerd, zijn van het type System.Exception of een type dat hiervan overdraagt. Toch zijn CLS-compatibele methoden niet vereist om de doorgifte van andere soorten uitzonderingen te blokkeren. 40
Algemeen CLS-nalevingsregels CLS-regels zijn alleen van toepassing op die onderdelen van een type dat toegankelijk of zichtbaar is buiten de gedefinieerde assembly. 1
Algemeen CLS-nalevingsregels Leden van niet-CLS-compatibele typen worden niet gemarkeerd als CLS-compatibel. 2
Generics Algemene typen en leden Geneste typen moeten ten minste zoveel algemene parameters hebben als het insluittype. Algemene parameters in een genest type komen overeen met de algemene parameters in het bijbehorende type. 42
Generics Algemene typen en leden De naam van een algemeen type coderen het aantal parameters van het type dat is opgegeven voor het niet-geneste type, of nieuwe invoer voor het type indien genest, volgens de hierboven gedefinieerde regels. 43
Generics Algemene typen en leden Een algemeen type moet voldoende beperkingen opnieuw declareren om te garanderen dat eventuele beperkingen op het basistype of interfaces zouden worden nageleefd door de algemene typebeperkingen. 44
Generics Algemene typen en leden Typen die worden gebruikt als beperkingen voor algemene parameters, moeten zelf CLS-compatibel zijn. 45
Generics Algemene typen en leden De zichtbaarheid en toegankelijkheid van leden (inclusief geneste typen) in een geïnstantieerd algemeen type worden beschouwd als een bereik van de specifieke instantie in plaats van de algemene typedeclaratie als geheel. Ervan uitgaande, zijn de zichtbaarheids- en toegankelijkheidsregels van CLS-regel 12 nog steeds van toepassing. 46
Generics Algemene typen en leden Voor elke abstracte of virtuele generieke methode is er een standaard concrete (niet-abstract) implementatie 47
Interfaces Interfaces VOOR CLS-compatibele interfaces is niet de definitie van niet-CLS-compatibele methoden vereist om deze te implementeren. 18
Interfaces Interfaces CLS-compatibele interfaces definiëren geen statische methoden, noch definiëren ze velden. 19
Leden Type leden in het algemeen Globale statische velden en -methoden zijn niet cls-compatibel. 36
Leden -- De waarde van een letterlijke statische waarde wordt opgegeven met behulp van metagegevens van veld initialisatie. Een letterlijke cls-compatibele waarde moet een waarde hebben die is opgegeven in metagegevens van veld initialisatie die exact hetzelfde type is als de letterlijke waarde (of van het onderliggende type, als die letterlijke waarde een enumis). 13
Leden Type leden in het algemeen De beperking vararg maakt geen deel uit van de CLS en de enige oproepconventie die wordt ondersteund door de CLS, is de standaard beheerde oproepconventie. 15
Naamconventies Naamconventies Assembly's volgen bijlage 7 van technisch rapport 15 van de Unicode Standard3.0 voor de set tekens die mogen worden gestart en opgenomen in id's, die online beschikbaar zijn bij Unicode Normalization Forms. Id's hebben de canonieke indeling die is gedefinieerd door Unicode Normalization Form C. Voor CLS-doeleinden zijn twee id's hetzelfde als de toewijzingen in kleine letters (zoals opgegeven door de Unicode-landinstellingen, een-op-een-toewijzingen in kleine letters) hetzelfde zijn. Dat wil gezegd dat twee id's in het kader van de CLS anders moeten worden beschouwd dan alleen hun geval. Als u echter een overgenomen definitie wilt overschrijven, moet de CLI de exacte codering van de oorspronkelijke declaratie gebruiken. 4
Overbelasting Naamconventies Alle namen die in een CLS-compatibel bereik worden geïntroduceerd, zijn onafhankelijk van soort, behalve wanneer de namen identiek en opgelost zijn via overbelasting. Dat wil gezegd, terwijl de CTS één type toestaat om dezelfde naam te gebruiken voor een methode en een veld, de CLS niet. 5
Overbelasting Naamconventies Velden en geneste typen moeten afzonderlijk worden onderscheiden door identificatievergelijking, ook al kunnen afzonderlijke handtekeningen worden onderscheiden door de CTS. Methoden, eigenschappen en gebeurtenissen met dezelfde naam (op id-vergelijking) verschillen met meer dan alleen het retourtype, behalve zoals opgegeven in CLS-regel 39 6
Overbelasting Overbelastingen Alleen eigenschappen en methoden kunnen overbelast worden. 37
Overbelasting Overbelastingen Eigenschappen en methoden kunnen alleen worden overbelast op basis van het aantal en de typen van hun parameters, behalve de conversieoperators met de naam op_Implicit en op_Explicit, die ook kunnen worden overbelast op basis van hun retourtype. 38
Overbelasting -- Als twee of meer CLS-compatibele methoden die zijn gedeclareerd in een type dezelfde naam hebben en voor een specifieke set type instantiëringen dezelfde parameter en retourtypen hebben, zijn al deze methoden semantisch gelijkwaardig bij die type instantiëringen. 48
Eigenschappen Eigenschappen De methoden waarmee de getter- en settermethoden van een eigenschap worden geïmplementeerd, worden gemarkeerd SpecialName in de metagegevens. 24
Eigenschappen Eigenschappen De accessors van een eigenschap zijn allemaal statisch, allemaal virtueel of alle exemplaren. 26
Eigenschappen Eigenschappen Het type eigenschap is het retourtype van de getter en het type van het laatste argument van de setter. De typen van de parameters van de eigenschap moeten de typen van de parameters zijn voor de getter en de typen van alle, maar de laatste parameter van de setter. Al deze typen zijn CLS-compatibel en worden geen beheerde aanwijzers (dat wil gezegd, worden niet doorgegeven door verwijzing). 27
Eigenschappen Eigenschappen Eigenschappen moeten voldoen aan een specifiek naamgevingspatroon. Het SpecialName kenmerk waarnaar in CLS-regel 24 wordt verwezen, wordt genegeerd in de juiste naamvergelijkingen en voldoet aan de identificatieregels. Een eigenschap heeft een gettermethode, een settermethode of beide. 28
Type conversie Typeconversie Indien er op_Implicit of op_Explicit wordt verstrekt, wordt een alternatief middel geboden om de dwang op te geven. 39
Typen Typen en typen lidhandtekeningen Waardetypen in vakken zijn niet CLS-compatibel. 3
Typen Typen en typen lidhandtekeningen Alle typen die in een handtekening worden weergegeven, zijn CLS-compatibel. Alle typen die een geïnstantieerd algemeen type opstellen, zijn CLS-compatibel. 11
Typen Typen en typen lidhandtekeningen Getypte verwijzingen zijn niet CLS-compatibel. 14
Typen Typen en typen lidhandtekeningen Niet-beheerde aanwijzertypen zijn niet CLS-compatibel. 17
Typen Typen en typen lidhandtekeningen CLS-compatibele klassen, waardetypen en interfaces vereisen geen implementatie van niet-CLS-compatibele leden 20
Typen Typen en typen lidhandtekeningen System.Object is CLS-compatibel. Elke andere CLS-compatibele klasse neemt over van een CLS-compatibele klasse. 23

Indexeren naar subsecties:

Typen en handtekeningen voor typeleden

Het type System.Object is CLS-compatibel en is het basistype van alle objecttypen in het .NET-typesysteem. Overname in .NET is impliciet (bijvoorbeeld de tekenreeksklasse neemt impliciet over van de Object klasse) of expliciet (bijvoorbeeld de klasse CultureNotFoundException neemt expliciet over van de klasse ArgumentException , die expliciet wordt overgenomen van de uitzonderingsklasse . Als een afgeleid type CLS-compatibel moet zijn, moet het basistype ook CLS-compatibel zijn.

In het volgende voorbeeld ziet u een afgeleid type waarvan het basistype niet cls-compatibel is. Hiermee wordt een basisklasse Counter gedefinieerd die een niet-ondertekend 32-bits geheel getal als teller gebruikt. Omdat de klasse tellerfunctionaliteit biedt door een geheel getal zonder teken te verpakken, wordt de klasse gemarkeerd als niet-CLS-compatibel. Als gevolg hiervan is een afgeleide klasse, NonZeroCounterook niet CLS-compatibel.

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

Alle typen die worden weergegeven in lidhandtekeningen, inclusief het retourtype van een methode of een eigenschapstype, moeten CLS-compatibel zijn. Daarnaast geldt voor algemene typen:

  • Alle typen die een geïnstantieerd algemeen type vormen, moeten CLS-compatibel zijn.

  • Alle typen die worden gebruikt als beperkingen voor algemene parameters, moeten CLS-compatibel zijn.

Het algemene .NET-typesysteem bevat veel ingebouwde typen die rechtstreeks worden ondersteund door de algemene taalruntime en die speciaal worden gecodeerd in de metagegevens van een assembly. Van deze intrinsieke typen zijn de typen die worden vermeld in de volgende tabel CLS-compatibel.

CLS-compatibel type Beschrijving
Byte 8-bits geheel getal zonder teken
Int16 16-bits geheel getal ondertekend
Int32 32-bits geheel getal ondertekend
Int64 64-bits ondertekende gehele getallen
Half Drijvendekommawaarde met halve precisie
Één Drijvendekommawaarde met één precisie
Dubbel Dubbele precisie zwevende kommawaarde
Booleaanse waarde waar- of onwaarwaardetype
Char UTF-16 gecodeerde code-eenheid
Decimaal Niet-drijvende komma decimaal getal
IntPtr Aanwijzer of greep van een door het platform gedefinieerde grootte
Tekenreeks Verzameling van nul-, een of meer tekenobjecten

De intrinsieke typen die in de volgende tabel worden vermeld, zijn niet CLS-compatibel.

Niet-compatibel type Beschrijving CLS-conform alternatief
SByte 8-bits gegevenstype ondertekend geheel getal Int16
UInt16 16-bits geheel getal zonder teken Int32
UInt32 32-bits geheel getal zonder teken Int64
UInt64 64-bits geheel getal zonder teken Int64 (kan overlopen), BigInteger of Double
UIntPtr Niet-ondertekende aanwijzer of greep IntPtr

De .NET-klassebibliotheek of een andere klassebibliotheek kan andere typen bevatten die niet compatibel zijn met CLS, bijvoorbeeld:

  • Waardetypen in vakken. In het volgende C#-voorbeeld wordt een klasse gemaakt met een openbare eigenschap van het type int* met de naam Value. Omdat een int* waardetype in een vak is, markeert de compiler het als niet-CLS-compatibel.

    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
    
  • Getypte verwijzingen, die speciale constructies zijn die een verwijzing naar een object en een verwijzing naar een type bevatten. Getypte verwijzingen worden weergegeven in .NET door de TypedReference klasse.

Als een type niet cls-compatibel is, moet u het CLSCompliantAttribute kenmerk toepassen met een isCompliant waarde ervan false . Zie de sectie CLSCompliantAttribute-kenmerk voor meer informatie.

In het volgende voorbeeld ziet u het probleem van CLS-naleving in een methodehandtekening en in algemene type instantiëring. Hiermee wordt een InvoiceItem klasse gedefinieerd met een eigenschap van het type UInt32, een eigenschap van het type Nullable<UInt32>en een constructor met parameters van het type UInt32 en Nullable<UInt32>. U krijgt vier compilerwaarschuwingen wanneer u dit voorbeeld probeert te compileren.

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

Als u de compilerwaarschuwingen wilt elimineren, vervangt u de niet-CLS-compatibele typen in de InvoiceItem openbare interface door compatibele typen:

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

Naast de specifieke typen die worden vermeld, zijn sommige categorieën typen niet compatibel met CLS. Deze omvatten niet-beheerde aanwijzertypen en functiepointertypen. In het volgende voorbeeld wordt een compilerwaarschuwing gegenereerd omdat er een aanwijzer naar een geheel getal wordt gebruikt om een matrix met gehele getallen te maken.

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

Voor cls-compatibele abstracte klassen (dat wil gezegd, klassen die zijn gemarkeerd als abstract in C# of in MustInherit Visual Basic), moeten alle leden van de klasse ook CLS-compatibel zijn.

Naamconventies

Omdat sommige programmeertalen niet hoofdlettergevoelig zijn, moeten id's (zoals de namen van naamruimten, typen en leden) verschillen met meer dan hoofdletters. Twee id's worden als gelijkwaardig beschouwd als hun toewijzingen in kleine letters hetzelfde zijn. In het volgende C#-voorbeeld worden twee openbare klassen gedefinieerd en Personperson. Omdat ze alleen per geval verschillen, markeert de C#-compiler ze als niet CLS-compatibel.

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)

Programmeertaal-id's, zoals de namen van naamruimten, typen en leden, moeten voldoen aan de Unicode-standaard. Dit betekent het volgende:

  • Het eerste teken van een id kan elke Unicode-hoofdletter, kleine letter, titelletter, wijzigingsletter, andere letter of letter zijn. Zie de System.Globalization.UnicodeCategory opsomming voor informatie over Unicode-tekencategorieën.

  • Volgende tekens kunnen afkomstig zijn van een van de categorieën als het eerste teken en kunnen ook niet-spatiëringsmarkeringen, spatiëringsmarkeringen, decimale getallen, interpunctie van verbindingslijnen en opmaakcodes bevatten.

Voordat u id's vergelijkt, moet u opmaakcodes filteren en de id's converteren naar Unicode Normalization Form C, omdat één teken kan worden vertegenwoordigd door meerdere UTF-16-gecodeerde code-eenheden. Tekenreeksen die dezelfde code-eenheden produceren in Unicode Normalization Form C zijn niet CLS-compatibel. In het volgende voorbeeld wordt een eigenschap met de naam gedefinieerd, die bestaat uit het teken ANGSTROM SIGN (U+212B) en een tweede eigenschap met de naam Å, die bestaat uit het teken LATIJNSE HOOFDLETTER A MET RING BOVEN (U+00C5). Zowel de C#- als de Visual Basic-compilers markeren de broncode als niet-CLS-compatibel.

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

Lidnamen binnen een bepaald bereik (zoals de naamruimten binnen een assembly, de typen binnen een naamruimte of de leden binnen een type) moeten uniek zijn, met uitzondering van namen die worden opgelost door overbelasting. Deze vereiste is strenger dan die van het algemene typesysteem, waardoor meerdere leden binnen een bereik identieke namen kunnen hebben zolang ze verschillende soorten leden zijn (bijvoorbeeld een methode en één een veld is). Met name voor type leden:

  • Velden en geneste typen worden alleen onderscheiden door naam.

  • Methoden, eigenschappen en gebeurtenissen met dezelfde naam moeten verschillen met meer dan alleen het retourtype.

In het volgende voorbeeld ziet u de vereiste dat ledennamen uniek moeten zijn binnen hun bereik. Hiermee wordt een klasse gedefinieerd die Converter vier leden bevat met de naam Conversion. Drie zijn methoden en één is een eigenschap. De methode die een parameter bevat, heeft een Int64 unieke naam, maar de twee methoden met een Int32 parameter zijn niet, omdat retourwaarde niet wordt beschouwd als een deel van de handtekening van een lid. De Conversion eigenschap schendt deze vereiste ook, omdat eigenschappen niet dezelfde naam hebben als overbelaste methoden.

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

Afzonderlijke talen bevatten unieke trefwoorden, zodat talen die gericht zijn op de algemene taalruntime, ook een mechanisme bieden voor het verwijzen naar id's (zoals typenamen) die overeenkomen met trefwoorden. Is bijvoorbeeld case een trefwoord in zowel C# als Visual Basic. Het volgende Visual Basic-voorbeeld kan een klasse met de naam case van het case trefwoord echter niet eenduidig maken met behulp van accolades openen en sluiten. Anders wordt in het voorbeeld het foutbericht 'Trefwoord is niet geldig als id' gegenereerd en kan het niet worden gecompileerd.

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

In het volgende C#-voorbeeld kan de case klasse worden geïnstitueerd door het @ symbool te gebruiken om de id van het trefwoord taal niet eenduidig te maken. Zonder dit zou de C#-compiler twee foutberichten weergeven: 'Type verwacht' en 'Ongeldige expressieterm 'case'.

using System;

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

Type conversie

De Common Language Specification definieert twee conversieoperators:

  • op_Implicit, dat wordt gebruikt voor het breder maken van conversies die niet leiden tot verlies van gegevens of precisie. De Decimal structuur bevat bijvoorbeeld een overbelaste op_Implicit operator om waarden van integrale typen en Char waarden te converteren naar Decimal waarden.

  • op_Explicit, dat wordt gebruikt voor het beperken van conversies die kunnen leiden tot verlies van grootte (een waarde wordt geconverteerd naar een waarde met een kleiner bereik) of precisie. De structuur bevat bijvoorbeeld een overbelaste op_Explicit operator om waarden te converteren Double en Single naar Decimal en om waarden te converteren Decimal naar integrale waarden, Double, en SingleChar.Decimal

Niet alle talen ondersteunen echter overbelasting van operatoren of de definitie van aangepaste operators. Als u ervoor kiest om deze conversieoperators te implementeren, moet u ook een alternatieve manier bieden om de conversie uit te voeren. U wordt aangeraden xxx- en Toxxx-methoden op te gevenFrom.

In het volgende voorbeeld worden impliciete en expliciete cls-compatibele conversies gedefinieerd. Er wordt een UDouble klasse gemaakt die een niet-ondertekend, dubbelprecisie, drijvendekommagetal vertegenwoordigt. Het voorziet in impliciete conversies van en naar expliciete conversies van UDoubleUDoubleSingle, Double naar UDoubleen Single naar UDouble.Double Het definieert ook een ToDouble methode als alternatief voor de impliciete conversieoperator en de ToSingle, FromDoubleen FromSingle methoden als alternatieven voor de expliciete conversieoperators.

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

Matrices

CLS-compatibele matrices voldoen aan de volgende regels:

  • Alle dimensies van een matrix moeten een ondergrens van nul hebben. In het volgende voorbeeld wordt een niet-CLS-compatibele matrix met een ondergrens gemaakt. Ondanks de aanwezigheid van het CLSCompliantAttribute kenmerk detecteert de compiler niet dat de matrix die door de Numbers.GetTenPrimes methode wordt geretourneerd, niet cls-compatibel is.

    [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
    
  • Alle matrixelementen moeten bestaan uit CLS-compatibele typen. In het volgende voorbeeld worden twee methoden gedefinieerd waarmee niet-CLS-compatibele matrices worden geretourneerd. De eerste retourneert een matrix met UInt32 waarden. De tweede retourneert een Object matrix die waarden bevat en UInt32 bevatInt32. Hoewel de compiler de eerste matrix identificeert als niet-compatibel vanwege het UInt32 type, kan niet worden herkend dat de tweede matrix niet-CLS-compatibele elementen bevat.

    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()
    '                              ~~~~~~~~~~~~
    
  • Overbelastingsresolutie voor methoden met matrixparameters is gebaseerd op het feit dat ze matrices zijn en op hun elementtype. Daarom is de volgende definitie van een overbelaste GetSquares methode CLS-compatibel.

    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
    

Interfaces

CLS-compatibele interfaces kunnen eigenschappen, gebeurtenissen en virtuele methoden definiëren (methoden zonder implementatie). Een CLS-compatibele interface kan geen van de volgende opties hebben:

  • Statische methoden of statische velden. Zowel de C#- als de Visual Basic-compilers genereren compilerfouten als u een statisch lid in een interface definieert.

  • Velden. Zowel de C#- als de Visual Basic-compilers genereren compilerfouten als u een veld in een interface definieert.

  • Methoden die niet compatibel zijn met CLS. De volgende interfacedefinitie bevat bijvoorbeeld een methode, INumber.GetUnsigneddie is gemarkeerd als niet-CLS-compatibel. In dit voorbeeld wordt een compilerwaarschuwing gegenereerd.

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

    Vanwege deze regel zijn CLS-compatibele typen niet vereist om niet-CLS-compatibele leden te implementeren. Als een CLS-compatibel framework wel een klasse beschikbaar maakt die een niet-CLS-compatibele interface implementeert, moet het ook concrete implementaties bieden van alle niet-CLS-compatibele leden.

Cls-compatibele taalcompilatoren moeten ook toestaan dat een klasse afzonderlijke implementaties biedt van leden met dezelfde naam en handtekening in meerdere interfaces. Zowel C# als Visual Basic ondersteunen expliciete interface-implementaties om verschillende implementaties van identieke benoemde methoden te bieden. Visual Basic ondersteunt ook het Implements trefwoord, waarmee u expliciet kunt aangeven welke interface en welk lid een bepaald lid implementeert. In het volgende voorbeeld ziet u dit scenario door een Temperature klasse te definiëren waarmee de ICelsius en IFahrenheit interfaces worden geïmplementeerd als expliciete interface-implementaties.

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

Opsommingen

Cls-compatibele opsommingen moeten de volgende regels volgen:

  • Het onderliggende type van de opsomming moet een intrinsiek CLS-compatibel geheel getal (Byte, Int16, Int32of ) Int64zijn. Met de volgende code wordt bijvoorbeeld geprobeerd een opsomming te definiëren waarvan het onderliggende type is UInt32 en een compilerwaarschuwing genereert.

    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
    '                ~~~~
    
  • Een opsommingstype moet één exemplaarveld hebben met de naam Value__ die is gemarkeerd met het FieldAttributes.RTSpecialName kenmerk. Hiermee kunt u impliciet verwijzen naar de veldwaarde.

  • Een opsomming bevat letterlijke statische velden waarvan de typen overeenkomen met het type opsomming zelf. Als u bijvoorbeeld een State opsomming definieert met waarden van State.On en State.Off, State.On en State.Off beide letterlijke statische velden waarvan het type is State.

  • Er zijn twee soorten opsommingen:

    • Een opsomming die een reeks wederzijds exclusieve, benoemde gehele getallen vertegenwoordigt. Dit type opsomming wordt aangegeven door het ontbreken van het System.FlagsAttribute aangepaste kenmerk.

    • Een opsomming die een set bitvlagken vertegenwoordigt die kunnen combineren om een niet-benoemde waarde te genereren. Dit type opsomming wordt aangegeven door de aanwezigheid van het System.FlagsAttribute aangepaste kenmerk.

    Zie de documentatie voor de Enum structuur voor meer informatie.

  • De waarde van een opsomming is niet beperkt tot het bereik van de opgegeven waarden. Met andere woorden, het bereik van waarden in een opsomming is het bereik van de onderliggende waarde. U kunt de Enum.IsDefined methode gebruiken om te bepalen of een opgegeven waarde lid is van een opsomming.

Type leden in het algemeen

Voor de Common Language Specification moeten alle velden en methoden worden geopend als leden van een bepaalde klasse. Daarom zijn globale statische velden en methoden (dat wil gezegd, statische velden of methoden die zijn gedefinieerd buiten een type) niet cls-compatibel. Als u probeert een globaal veld of een algemene methode op te nemen in uw broncode, genereren zowel de C# als Visual Basic-compilers een compilerfout.

De Common Language Specification ondersteunt alleen de standaard beheerde oproepconventie. Het biedt geen ondersteuning voor niet-beheerde aanroepconventies en -methoden met lijsten met variabelenargumenten die zijn gemarkeerd met het varargs trefwoord. Voor lijsten met variabelenargumenten die compatibel zijn met de standaardconventie voor beheerde aanroepen, gebruikt u het ParamArrayAttribute kenmerk of de implementatie van de afzonderlijke taal, zoals het params trefwoord in C# en het ParamArray trefwoord in Visual Basic.

Toegankelijkheid van leden

Het overschrijven van een overgenomen lid kan de toegankelijkheid van dat lid niet wijzigen. Een openbare methode in een basisklasse kan bijvoorbeeld niet worden overschreven door een persoonlijke methode in een afgeleide klasse. Er is één uitzondering: een protected internal lid (in C#) of Protected Friend (in Visual Basic) in één assembly dat wordt overschreven door een type in een andere assembly. In dat geval is Protectedde toegankelijkheid van de onderdrukking .

In het volgende voorbeeld ziet u de fout die wordt gegenereerd wanneer het kenmerk CLSCompliantAttribute is ingesteld op true, en Humandat is een klasse die is afgeleid van Animal, probeert de toegankelijkheid van de Species eigenschap te wijzigen van openbaar naar privé. Het voorbeeld wordt gecompileerd als de toegankelijkheid is gewijzigd in openbaar.

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

Typen in de handtekening van een lid moeten toegankelijk zijn wanneer dat lid toegankelijk is. Dit betekent bijvoorbeeld dat een openbaar lid geen parameter kan opnemen waarvan het type privé, beveiligd of intern is. In het volgende voorbeeld ziet u de compilerfout die wordt weergegeven wanneer een StringWrapper klasseconstructor een interne StringOperationType opsommingswaarde weergeeft die bepaalt hoe een tekenreekswaarde moet worden verpakt.

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

Algemene typen en leden

Geneste typen hebben altijd minstens zoveel algemene parameters als hun insluittype. Deze komen overeen met de algemene parameters in het insluittype. Het algemene type kan ook nieuwe algemene parameters bevatten.

De relatie tussen de algemene typeparameters van een bevatd type en de geneste typen kan worden verborgen door de syntaxis van afzonderlijke talen. In het volgende voorbeeld bevat een algemeen type Outer<T> twee geneste klassen Inner1A en Inner1B<U>. De aanroepen naar de ToString methode, waarvan elke klasse overneemt Object.ToString(), laten zien dat elke geneste klasse de typeparameters van de bijbehorende klasse bevat.

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]

Algemene typenamen worden gecodeerd in de formuliernaam'n, waarbij de naam de typenaam is, ' is een letterlijk teken en n het aantal parameters dat is gedeclareerd voor het type, of, voor geneste algemene typen, het aantal nieuw geïntroduceerde typeparameters. Deze codering van algemene typenamen is voornamelijk van belang voor ontwikkelaars die reflectie gebruiken voor toegang tot algemene cls-klachttypen in een bibliotheek.

Als beperkingen worden toegepast op een algemeen type, moeten alle typen die als beperkingen worden gebruikt, ook CLS-compatibel zijn. In het volgende voorbeeld wordt een klasse gedefinieerd BaseClass die niet cls-compatibel is en een algemene klasse met de naam BaseCollection waarvan de typeparameter moet worden afgeleid BaseClass. Maar omdat BaseClass het niet cls-compatibel is, verzendt de compiler een waarschuwing.

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

Als een algemeen type is afgeleid van een algemeen basistype, moet het alle beperkingen opnieuw declareren, zodat aan de beperkingen voor het basistype kan worden voldaan. In het volgende voorbeeld wordt een Number<T> waarde gedefinieerd die elk numeriek type kan vertegenwoordigen. Er wordt ook een FloatingPoint<T> klasse gedefinieerd die een drijvendekommagewaarde vertegenwoordigt. De broncode kan echter niet worden gecompileerd, omdat deze de beperking niet toepast op Number<T> (dat T moet een waardetype zijn) op 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)
'                                                          ~

Het voorbeeld wordt gecompileerd als de beperking wordt toegevoegd aan de FloatingPoint<T> klasse.

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

De Common Language Specification legt een conservatief model per instantie op voor geneste typen en beveiligde leden. Open algemene typen kunnen geen velden of leden weergeven met handtekeningen die een specifieke instantie van een geneste, beveiligde algemene type bevatten. Niet-algemene typen die een specifieke instantie van een algemene basisklasse of interface uitbreiden, kunnen geen velden of leden beschikbaar maken met handtekeningen die een andere instantie van een geneste, beveiligde algemene type bevatten.

In het volgende voorbeeld wordt een algemeen type ( C1<T> of C1(Of T) in Visual Basic) en een beveiligde klasse C1<T>.N (of C1(Of T).N in Visual Basic) gedefinieerd. C1<T> heeft twee methoden, M1 en M2. Is echter niet CLS-compatibel omdat M1 er wordt geprobeerd een C1<int>.N (of C1(Of Integer).N) object van C1<T> (of C1(Of T)) te retourneren . Een tweede klasse, C2is afgeleid van C1<long> (of C1(Of Long)). Het heeft twee methoden, M3 en M4. M3 is niet cls-compatibel omdat wordt geprobeerd een C1<int>.N (of C1(Of Integer).N) object te retourneren uit een subklasse van C1<long>. Taalcompilers kunnen nog restrictiever zijn. In dit voorbeeld wordt in Visual Basic een fout weergegeven wanneer deze probeert te compileren 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)  
'                             ~~~~~~~~~~~~~

Constructors

Constructors in CLS-compatibele klassen en -structuren moeten de volgende regels volgen:

  • Een constructor van een afgeleide klasse moet de instantieconstructor van de basisklasse aanroepen voordat er toegang wordt verkregen tot overgenomen exemplaargegevens. Deze vereiste komt doordat basisklasseconstructors niet worden overgenomen door hun afgeleide klassen. Deze regel is niet van toepassing op structuren die geen directe overname ondersteunen.

    Compilers dwingen deze regel doorgaans onafhankelijk van CLS-naleving af, zoals in het volgende voorbeeld wordt weergegeven. Er wordt een Doctor klasse gemaakt die is afgeleid van een Person klasse, maar de Doctor klasse kan de klasseconstructor niet Person aanroepen om overgenomen exemplaarvelden te initialiseren.

    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()
    '                  ~~~
    
  • Een objectconstructor kan niet worden aangeroepen, behalve om een object te maken. Bovendien kan een object niet tweemaal worden geïnitialiseerd. Dit betekent bijvoorbeeld dat Object.MemberwiseClone en deserialisatiemethoden geen constructors mogen aanroepen.

Eigenschappen

Eigenschappen in cls-compatibele typen moeten de volgende regels volgen:

  • Een eigenschap moet een setter, een getter of beide hebben. In een assembly worden deze geïmplementeerd als speciale methoden, wat betekent dat ze worden weergegeven als afzonderlijke methoden (de getter heet get_eigenschapsnaam en de setter eigenschapsnaam isset_) gemarkeerd als SpecialName in de metagegevens van de assembly. De C#- en Visual Basic-compilers dwingen deze regel automatisch af zonder dat het CLSCompliantAttribute kenmerk hoeft te worden toegepast.

  • Het type van een eigenschap is het retourtype van de eigenschap getter en het laatste argument van de setter. Deze typen moeten cls-compatibel zijn en argumenten kunnen niet aan de eigenschap worden toegewezen door verwijzing (dat wil gezegd, ze kunnen geen beheerde aanwijzers worden).

  • Als een eigenschap zowel een getter als een setter heeft, moeten ze beide virtueel, zowel statisch als beide exemplaren zijn. De C#- en Visual Basic-compilers dwingen deze regel automatisch af via de syntaxis van de eigenschapsdefinitie.

gebeurtenis

Een gebeurtenis wordt gedefinieerd door de naam en het bijbehorende type. Het gebeurtenistype is een gemachtigde die wordt gebruikt om de gebeurtenis aan te geven. De gebeurtenis is bijvoorbeeld AppDomain.AssemblyResolve van het type ResolveEventHandler. Naast de gebeurtenis zelf bieden drie methoden met namen op basis van de gebeurtenisnaam de implementatie van de gebeurtenis en worden gemarkeerd als SpecialName in de metagegevens van de assembly:

  • Een methode voor het toevoegen van een gebeurtenis-handler met de naam add_EventName. De methode voor het gebeurtenisabonnement voor de gebeurtenis heeft bijvoorbeeld de AppDomain.AssemblyResolve naam add_AssemblyResolve.

  • Een methode voor het verwijderen van een gebeurtenis-handler met de naam remove_EventName. De verwijderingsmethode voor de gebeurtenis heeft bijvoorbeeld de AppDomain.AssemblyResolve naam remove_AssemblyResolve.

  • Een methode om aan te geven dat de gebeurtenis heeft plaatsgevonden, met de naam raise_EventName.

Notitie

De meeste regels van de Common Language Specification met betrekking tot gebeurtenissen worden geïmplementeerd door taalcompilers en zijn transparant voor onderdeelontwikkelaars.

De methoden voor het toevoegen, verwijderen en verhogen van de gebeurtenis moeten dezelfde toegankelijkheid hebben. Ze moeten ook allemaal statisch, exemplaar of virtueel zijn. De methoden voor het toevoegen en verwijderen van een gebeurtenis hebben één parameter waarvan het type gebeurtenisdelegeertype is. De methoden voor toevoegen en verwijderen moeten beide aanwezig zijn of beide afwezig zijn.

In het volgende voorbeeld wordt een cls-compatibele klasse gedefinieerd die Temperature een TemperatureChanged gebeurtenis genereert als de temperatuurwijziging tussen twee metingen gelijk is aan of een drempelwaarde overschrijdt. De Temperature klasse definieert expliciet een raise_TemperatureChanged methode, zodat deze selectief gebeurtenis-handlers kan uitvoeren.

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

Overloads

De Common Language Specification legt de volgende vereisten op voor overbelaste leden:

  • Leden kunnen worden overbelast op basis van het aantal parameters en het type parameter. Aanroepende conventie, retourtype, aangepaste modifiers die zijn toegepast op de methode of de parameter ervan, en of parameters worden doorgegeven door waarde of verwijzing, worden niet meegenomen wanneer onderscheid wordt gemaakt tussen overbelastingen. Zie bijvoorbeeld de code voor de vereiste dat namen uniek moeten zijn binnen een bereik in de sectie Naamconventies .

  • Alleen eigenschappen en methoden kunnen overbelast worden. Velden en gebeurtenissen kunnen niet worden overbelast.

  • Algemene methoden kunnen worden overbelast op basis van het aantal algemene parameters.

Notitie

De op_Explicit operators zijn op_Implicit uitzonderingen op de regel die de retourwaarde niet als onderdeel van een methodehandtekening voor overbelastingsresolutie beschouwd. Deze twee operators kunnen worden overbelast op basis van hun parameters en hun retourwaarde.

Uitzonderingen

Uitzonderingsobjecten moeten zijn afgeleid van System.Exception of van een ander type dat is afgeleid van System.Exception. In het volgende voorbeeld ziet u de compilerfout die resulteert wanneer een aangepaste klasse met de naam ErrorClass wordt gebruikt voor het verwerken van uitzonderingen.

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

Als u deze fout wilt corrigeren, moet de ErrorClass klasse overnemen van System.Exception. Bovendien moet de Message eigenschap worden overschreven. In het volgende voorbeeld worden deze fouten gecorrigeerd om een ErrorClass klasse te definiëren die cls-compatibel is.

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

Kenmerken

In .NET-assembly's bieden aangepaste kenmerken een uitbreidbaar mechanisme voor het opslaan van aangepaste kenmerken en het ophalen van metagegevens over programmeerobjecten, zoals assembly's, typen, leden en methodeparameters. Aangepaste kenmerken moeten zijn afgeleid van System.Attribute of van een type dat is afgeleid van System.Attribute.

In het volgende voorbeeld wordt deze regel geschonden. Hiermee wordt een NumericAttribute klasse gedefinieerd die niet is afgeleid van System.Attribute. Een compilerfout treedt alleen op wanneer het niet-CLS-compatibele kenmerk wordt toegepast, niet wanneer de klasse is gedefinieerd.

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

De constructor of de eigenschappen van een CLS-compatibel kenmerk kunnen alleen de volgende typen beschikbaar maken:

In het volgende voorbeeld wordt een DescriptionAttribute klasse gedefinieerd die is afgeleid van Kenmerk. De klasseconstructor heeft een parameter van het type Descriptor, dus de klasse is niet CLS-compatibel. De C#-compiler verzendt een waarschuwing, maar compileert met succes, terwijl de Visual Basic-compiler geen waarschuwing of een fout verzendt.

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

Het kenmerk CLSCompliantAttribute

Het kenmerk CLSCompliantAttribute wordt gebruikt om aan te geven of een programma-element voldoet aan de Common Language Specification. De CLSCompliantAttribute(Boolean) constructor bevat één vereiste parameter, isCompliant, die aangeeft of het programma-element CLS-compatibel is.

Tijdens het compileren detecteert de compiler niet-compatibele elementen die worden verondersteld cls-compatibel te zijn en een waarschuwing te verzenden. De compiler verzendt geen waarschuwingen voor typen of leden die expliciet als niet-compatibel zijn gedeclareerd.

Onderdeelontwikkelaars kunnen het CLSCompliantAttribute kenmerk op twee manieren gebruiken:

  • De onderdelen van de openbare interface definiëren die worden weergegeven door een onderdeel dat cls-compatibel is en de onderdelen die niet cls-compatibel zijn. Wanneer het kenmerk wordt gebruikt om bepaalde programma-elementen als CLS-compatibel te markeren, garandeert het gebruik ervan dat deze elementen toegankelijk zijn vanuit alle talen en hulpprogramma's die gericht zijn op .NET.

  • Om ervoor te zorgen dat de openbare interface van de onderdeelbibliotheek alleen programma-elementen beschikbaar maakt die compatibel zijn met CLS. Als elementen niet cls-compatibel zijn, geven compilers over het algemeen een waarschuwing.

Waarschuwing

In sommige gevallen dwingen taalcompilers CLS-compatibele regels af, ongeacht of het CLSCompliantAttribute kenmerk wordt gebruikt. Het definiëren van een statisch lid in een interface schendt bijvoorbeeld een CLS-regel. Als u in dit opzicht een static lid (in C#) of Shared (in Visual Basic) in een interface definieert, geven zowel de C#- als Visual Basic-compilers een foutbericht weer en kunnen de app niet compileren.

Het CLSCompliantAttribute kenmerk wordt gemarkeerd met een AttributeUsageAttribute kenmerk met een waarde van AttributeTargets.All. Met deze waarde kunt u het CLSCompliantAttribute kenmerk toepassen op elk programma-element, waaronder assembly's, modules, typen (klassen, structuren, opsommingen, interfaces en gemachtigden), typeleden (constructors, methoden, eigenschappen, velden en gebeurtenissen), parameters, algemene parameters en retourwaarden. In de praktijk moet u echter alleen het kenmerk toepassen op assembly's, typen en typeleden. Anders negeren compilers het kenmerk en blijven compilerwaarschuwingen genereren wanneer ze een niet-compatibele parameter, algemene parameter of retourwaarde tegenkomen in de openbare interface van uw bibliotheek.

De waarde van het CLSCompliantAttribute kenmerk wordt overgenomen door ingesloten programma-elementen. Als een assembly bijvoorbeeld is gemarkeerd als CLS-compatibel, zijn de typen ook CLS-compatibel. Als een type is gemarkeerd als CLS-compatibel, zijn de geneste typen en leden ook CLS-compatibel.

U kunt de overgenomen naleving expliciet overschrijven door het CLSCompliantAttribute kenmerk toe te passen op een opgenomen programma-element. U kunt bijvoorbeeld het kenmerk gebruiken met de isCompliant waarde voor het CLSCompliantAttribute definiëren van false een niet-compatibel type in een compatibele assembly en u kunt het kenmerk gebruiken met de isCompliant waarde van het definiëren van true een compatibel type in een niet-compatibele assembly. U kunt ook niet-compatibele leden definiëren in een compatibel type. Een niet-compatibel type kan echter geen compatibele leden hebben, dus u kunt het kenmerk niet gebruiken met een isCompliant waarde voor true het overschrijven van overname van een niet-compatibel type.

Wanneer u onderdelen ontwikkelt, moet u altijd het CLSCompliantAttribute kenmerk gebruiken om aan te geven of uw assembly, de typen en de leden cls-compatibel zijn.

Ga als volgende te werk om CLS-compatibele onderdelen te maken:

  1. Gebruik de CLSCompliantAttribute opdracht om uw assembly als CLS-compatibel te markeren.

  2. Markeer alle openbaar weergegeven typen in de assembly die niet cls-compatibel zijn als niet-compatibel.

  3. Markeer alle openbaar weergegeven leden in CLS-compatibele typen als niet-compatibel.

  4. Geef een CLS-compatibel alternatief op voor niet-CLS-compatibele leden.

Als u al uw niet-compatibele typen en leden hebt gemarkeerd, mag uw compiler geen waarschuwingen voor niet-naleving verzenden. U moet echter aangeven welke leden niet cls-compatibel zijn en hun CLS-compatibele alternatieven vermelden in uw productdocumentatie.

In het volgende voorbeeld wordt het kenmerk gebruikt voor het CLSCompliantAttribute definiëren van een CLS-compatibele assembly en een type, CharacterUtilitiesdat twee niet-CLS-compatibele leden heeft. Omdat beide leden zijn gelabeld met het CLSCompliant(false) kenmerk, produceert de compiler geen waarschuwingen. De klasse biedt ook een CLS-compatibel alternatief voor beide methoden. Normaal gesproken voegen we slechts twee overbelastingen toe aan de ToUTF16 methode om CLS-compatibele alternatieven te bieden. Omdat methoden echter niet kunnen worden overbelast op basis van de retourwaarde, verschillen de namen van de CLS-compatibele methoden van de namen van de niet-compatibele methoden.

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

Als u een app ontwikkelt in plaats van een bibliotheek (als u geen typen of leden beschikbaar maakt die door andere app-ontwikkelaars kunnen worden gebruikt), is de CLS-naleving van de programma-elementen die door uw app worden gebruikt, alleen van belang als uw taal deze niet ondersteunt. In dat geval genereert uw taalcompilator een fout wanneer u een niet-CLS-compatibel element probeert te gebruiken.

Interoperabiliteit tussen talen

Taalafhankelijkheid heeft een aantal mogelijke betekenissen. De ene betekenis omvat het naadloos gebruiken van typen die in de ene taal zijn geschreven vanuit een app die in een andere taal is geschreven. Een tweede betekenis, die de focus van dit artikel is, omvat het combineren van code die in meerdere talen in één .NET-assembly is geschreven.

In het volgende voorbeeld ziet u interoperabiliteit tussen talen door een klassebibliotheek met de naam Utilities.dll te maken die twee klassen bevat, NumericLib en StringLib. De NumericLib klasse is geschreven in C# en de StringLib klasse wordt geschreven in Visual Basic. Hier volgt de broncode voor StringUtil.vb, die één lid bevat, ToTitleCasein de StringLib klasse.

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

Hier volgt de broncode voor NumberUtil.cs, waarmee een NumericLib klasse wordt gedefinieerd die twee leden heeft en IsEvenNearZero.

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

Als u de twee klassen in één assembly wilt verpakken, moet u ze compileren in modules. Als u het Visual Basic-broncodebestand wilt compileren in een module, gebruikt u deze opdracht:

vbc /t:module StringUtil.vb

Zie Bouwen vanaf de opdrachtregel voor meer informatie over de opdrachtregelsyntaxis van de Visual Basic-compiler.

Als u het C#-broncodebestand wilt compileren in een module, gebruikt u deze opdracht:

csc /t:module NumberUtil.cs

Vervolgens gebruikt u de Linker-opties om de twee modules te compileren in een assembly:

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

In het volgende voorbeeld worden de NumericLib.NearZero en StringLib.ToTitleCase methoden aangeroepen. Zowel de Visual Basic-code als de C#-code hebben toegang tot de methoden in beide klassen.

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

Gebruik deze opdracht om de Visual Basic-code te compileren:

vbc example.vb /r:UtilityLib.dll

Als u wilt compileren met C#, wijzigt u de naam van de compiler in vbccscen wijzigt u de bestandsextensie van .vb in .cs:

csc example.cs /r:UtilityLib.dll