Jazyková nezávislost a komponenty nezávislé na jazyce

.NET je nezávislý na jazyce. To znamená, že jako vývojář můžete vyvíjet v jednom z mnoha jazyků, které cílí na implementace .NET, jako jsou C#, F# a Visual Basic. Můžete přistupovat k typům a členům knihoven tříd vyvinutých pro implementace .NET, aniž byste museli znát jazyk, ve kterém byly původně napsány, a aniž byste museli dodržovat žádné konvence původního jazyka. Pokud jste vývojář komponent, může k této komponentě přistupovat libovolná aplikace .NET bez ohledu na její jazyk.

Poznámka:

Tato první část tohoto článku popisuje vytváření komponent nezávislých na jazyce, to znamená komponent, které můžou využívat aplikace napsané v libovolném jazyce. Můžete také vytvořit jednu komponentu nebo aplikaci ze zdrojového kódu napsaného v několika jazycích; Viz Interoperabilita mezi jazyky v druhé části tohoto článku.

Pokud chcete plně pracovat s jinými objekty napsanými v libovolném jazyce, musí objekty vystavit volajícím jenom ty funkce, které jsou společné pro všechny jazyky. Tato společná sada funkcí je definována specifikací CLS (Common Language Specification ), což je sada pravidel, která platí pro vygenerovaná sestavení. Specifikace společného jazyka je definována v oddílu I, klauzule 7 až 11 standardu ECMA-335: Common Language Infrastructure.

Pokud vaše komponenta odpovídá specifikaci common language, je zaručeno, že je kompatibilní se specifikací CLS a je možné k ní přistupovat z kódu v sestaveních napsaných v libovolném programovacím jazyce, který podporuje CLS. Pomocí atributu CLSCompliantAttribute u zdrojového kódu můžete určit, jestli vaše komponenta odpovídá specifikaci common language v době kompilace. Další informace naleznete v tématu CLSCompliantAttribute atribut.

Pravidla dodržování předpisů CLS

Tato část popisuje pravidla pro vytvoření komponenty kompatibilní se specifikací CLS. Úplný seznam pravidel naleznete v oddílu I, doložka 11 standardu ECMA-335: Společná jazyková infrastruktura.

Poznámka:

Specifikace common language specification popisuje každé pravidlo dodržování předpisů CLS, protože se vztahuje na uživatele (vývojáři, kteří programově přistupují ke komponentě, která je kompatibilní se specifikací CLS), architektury (vývojáři, kteří používají kompilátor jazyka k vytváření knihoven kompatibilních se specifikací CLS) a rozšiřující moduly (vývojáři, kteří vytvářejí nástroj, jako je kompilátor jazyka nebo analyzátor kódu, který vytváří komponenty kompatibilní se specifikací CLS). Tento článek se zaměřuje na pravidla, která se vztahují na architektury. Upozorňujeme však, že některá pravidla, která se vztahují na rozšiřující moduly, se můžou vztahovat také na sestavení vytvořená pomocí Reflexe ionu. Vygenerovat.

Pokud chcete navrhnout komponentu, která je nezávislá na jazyce, stačí použít pravidla pro dodržování předpisů CLS jenom pro veřejné rozhraní vaší komponenty. Vaše privátní implementace nemusí odpovídat specifikaci.

Důležité

Pravidla dodržování předpisů CLS platí jenom pro veřejné rozhraní komponenty, ne pro jeho privátní implementaci.

Například jiná celá čísla bez znaménka než Byte nejsou kompatibilní se specifikací CLS. Person Protože třída v následujícím příkladu zveřejňuje Age vlastnost typu UInt16, následující kód zobrazí upozornění kompilátoru.

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

Třídu CLS můžete nastavit Person tak, že změníte typ Age vlastnosti na UInt16Int16, což je 16bitové celé číslo se 16bitovým podepsaným protokolem CLS. Nemusíte měnit typ soukromého personAge pole.

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

Veřejné rozhraní knihovny se skládá z následujících:

  • Definice veřejných tříd.

  • Definice veřejných členů veřejných tříd a definice členů, které jsou přístupné pro odvozené třídy (to znamená chráněné členy).

  • Parametry a návratové typy veřejných metod veřejných tříd a parametry a návratové typy metod, které jsou přístupné pro odvozené třídy.

Pravidla dodržování předpisů CLS jsou uvedená v následující tabulce. Text pravidel je převzat doslovně z ECMA-335 Standard: Common Language Infrastructure, což je Copyright 2012 ecma International. Podrobnější informace o těchto pravidlech najdete v následujících částech.

Kategorie Seznamte se s Pravidlo Číslo pravidla
Usnadnění Přístupnost členů Přístupnost se při přepsání zděděných metod nezmění, s výjimkou přepsání metody zděděné z jiného sestavení s přístupností family-or-assembly. V tomto případě má přepsání přístupnost family. 10
Usnadnění Přístupnost členů Viditelnost a přístupnost typů a členů musí být taková, aby typy v podpisu každého člena byly viditelné a přístupné, kdykoli je samotný člen viditelný a přístupný. Například veřejná metoda, která je viditelná mimo své sestavení, nesmí mít argument, jehož typ je viditelný pouze v rámci sestavení. Viditelnost a přístupnost typů, které vytvářejí vytvoření instance obecného typu použitého v podpisu libovolného člena, musí být viditelné a přístupné vždy, když je samotný člen viditelný a přístupný. Například vytvoření instance obecného typu v podpisu člena, který je viditelný mimo jeho sestavení, nesmí mít obecný argument, jehož typ je viditelný pouze v rámci sestavení. 12
Pole Pole Matice musí mít prvky s typem kompatibilním se specifikací CLS a všechny rozměry matice musí mít dolní mez nuly. Pouze skutečnost, že položka je pole a typ prvku pole musí být vyžadován k rozlišení mezi přetíženími. Při přetížení jsou založeny na dvou nebo více typech pole, typy prvků musí být pojmenované typy. 16
Atributy Atributy Atributy musí být typu System.Attributenebo typ, který z něj dědí. 41
Atributy Atributy CLS umožňuje pouze podmnožinu kódování vlastních atributů. Jedinými typy, které se mají objevit v těchto kódováních, jsou (viz oddíl IV): System.Type, System.String, , System.CharSystem.Boolean, System.ByteSystem.Int16System.Int32, System.Int64, System.Single, System.Doublea všechny typy výčtu založené na základním celočíselném typu kompatibilním se specifikací CLS. 34
Atributy Atributy CLS neumožňuje veřejně viditelné požadované modifikátory (modreqviz oddíl II), ale povoluje volitelné modifikátory (modoptviz oddíl II), kterým nerozumí. 35
Konstruktory Konstruktory Konstruktor objektu musí volat nějaký konstruktor instance své základní třídy předtím, než dojde k přístupu k zděděným datům instance. (Nevztahuje se to na typy hodnot, které nemusí mít konstruktory.) 21
Konstruktory Konstruktory Konstruktor objektů nesmí být volán pouze jako součást vytvoření objektu a objekt se neinicializuje dvakrát. 22
Výčty Výčty Podkladový typ výčtu musí být předdefinovaný celočíselná hodnota CLS, název pole musí být "value__" a toto pole musí být označeno RTSpecialName. 7
Výčty Výčty Existují dva různé druhy výčtů, které jsou označené přítomností nebo nepřítomností vlastního atributu (viz Knihovna IV oddílu System.FlagsAttribute ). Jedna představuje pojmenované celočíselné hodnoty; druhý představuje pojmenované bitové příznaky, které lze kombinovat a generovat nepojmenovanou hodnotu. Hodnota objektu enum není omezena na zadané hodnoty. 8
Výčty Výčty Literálová statická pole výčtu musí mít typ samotného výčtu. 9
Události Události Metody, které implementují událost, se označí SpecialName v metadatech. 29
Události Události Přístupnost události a jejích příslušenství musí být stejná. 30
Události Události remove Obě add metody události musí být přítomny nebo chybí. 31
Události Události Metody add a remove metody události musí mít jeden parametr, jehož typ definuje typ události a který se odvozuje ze system.Delegate. 32
Události Události Události se řídí konkrétním vzorem pojmenování. Atribut SpecialName uvedený v pravidle CLS 29 se ignoruje v odpovídajících porovnání názvů a dodržuje pravidla identifikátorů. 33
Výjimky Výjimky Objekty, které jsou vyvolány, musí být typu System.Exception nebo typ dědící z něj. Metody kompatibilní se specifikací CLS však nejsou nutné k blokování šíření jiných typů výjimek. 40
OBECNÉ Pravidla dodržování předpisů CLS Pravidla CLS se vztahují pouze na ty části typu, které jsou přístupné nebo viditelné mimo definující sestavení. 0
Obecné Pravidla dodržování předpisů CLS Členové nevyhovujících typům CLS nejsou označeni kompatibilními se specifikací CLS. 2
Obecné typy Obecné typy a členy Vnořené typy musí mít alespoň tolik obecných parametrů, jako je uzavřený typ. Obecné parametry v vnořeném typu odpovídají umístěním obecných parametrů v jeho uzavřeném typu. 42
Obecné typy Obecné typy a členy Název obecného typu zakóduje počet parametrů typu deklarovaných u nenořeného typu nebo nově zavedený do typu, pokud je vnořený, podle výše definovaných pravidel. 43
Obecné typy Obecné typy a členy Obecný typ musí předefinovat dostatečná omezení, aby bylo zaručeno, že všechna omezení základního typu nebo rozhraní budou splněna obecnými omezeními typu. 44
Obecné typy Obecné typy a členy Typy používané jako omezení obecných parametrů jsou samy o sobě kompatibilní se specifikací CLS. 45
Obecné typy Obecné typy a členy Viditelnost a přístupnost členů (včetně vnořených typů) v instanci obecného typu se považuje za vymezený na konkrétní instanci, nikoli na obecnou deklaraci typu jako celku. Za předpokladu, že pravidla viditelnosti a přístupnosti pravidla CLS 12 stále platí. 46
Obecné typy Obecné typy a členy Pro každou abstraktní nebo virtuální obecnou metodu musí existovat výchozí konkrétní implementace (nonabstract). 47
Rozhraní Rozhraní Rozhraní kompatibilní se specifikací CLS nevyžadují definici metod, které nedodržují předpisy CLS, aby je bylo možné implementovat. 18
Rozhraní Rozhraní Rozhraní kompatibilní se specifikací CLS nedefinují statické metody ani nedefinují pole. 19
Členové Obecné typy členů Globální statická pole a metody nejsou kompatibilní se specifikací CLS. 36
Členové -- Hodnota literálu statická je určena pomocí metadat inicializace polí. Literál kompatibilní se specifikací CLS musí mít hodnotu zadanou v metadatech inicializace polí, která jsou přesně stejného typu jako literál (nebo základní typ, pokud je tento literál typu enum). 13
Členové Obecné typy členů Omezení vararg není součástí CLS a jedinou konvencí volání podporovanou cls je standardní spravovaná konvence volání. 15
Zásady vytváření názvů Zásady vytváření názvů Sestavení se řídí přílohou 7 technické zprávy 15 standardu Unicode Standard3.0, která řídí sadu znaků povolených k zahájení a zahrnutí do identifikátorů, které jsou k dispozici online ve formulářích normalizace Unicode. Identifikátory musí být v kanonickém formátu definovaném normalizačním formulářem Unicode C. Pro účely CLS jsou dva identifikátory stejné, pokud jsou jejich malá písmena mapování (jak je specifikováno národní prostředí Unicode nerozlišující, mapování 1:1 malá písmena) stejné. To znamená, že pro dva identifikátory, které mají být považovány za odlišné podle CLS, se liší ve více než jednoduše jejich případě. K přepsání zděděné definice však rozhraní příkazového řádku vyžaduje přesné kódování původní deklarace. 4
Přetížení Zásady vytváření názvů Všechny názvy zavedené v oboru kompatibilním se specifikací CLS musí být odlišné nezávisle na druhu, s výjimkou případů, kdy jsou názvy identické a přetížené. To znamená, že zatímco CTS umožňuje jednomu typu použít stejný název pro metodu a pole, CLS ne. 5
Přetížení Zásady vytváření názvů Pole a vnořené typy se liší pouze porovnáním identifikátorů, i když CTS umožňuje rozlišovat jedinečné podpisy. Metody, vlastnosti a události se stejným názvem (porovnáním identifikátorů) se liší více než jen návratový typ, s výjimkou případů uvedených v pravidle CLS 39. 6
Přetížení Overloads Přetížit lze pouze vlastnosti a metody. 37
Přetížení Overloads Vlastnosti a metody lze přetížit pouze na základě počtu a typů jejich parametrů, s výjimkou operátorů op_Implicit převodu pojmenovaných a op_Explicit, které mohou být také přetíženy na základě jejich návratového typu. 38
Přetížení -- Pokud dvě nebo více metod kompatibilních se specifikací CLS deklarované v typu mají stejný název a pro určitou sadu instancí typů mají stejný parametr a návratové typy, budou všechny tyto metody sémanticky ekvivalentní v těchto instancích typu. 48
Vlastnosti Vlastnosti Metody, které implementují metody getter a setter vlastnosti, se označí SpecialName v metadatech. 24
Vlastnosti Vlastnosti Přístupové objekty vlastnosti musí být statické, všechny virtuální nebo všechny instance. 26
Vlastnosti Vlastnosti Typ vlastnosti musí být návratovým typem metody getter a typem posledního argumentu funkce setter. Typy parametrů vlastnosti musí být typy parametrů pro getter a typy všech kromě konečného parametru setter. Všechny tyto typy musí být kompatibilní se specifikací CLS a nesmí být spravovány ukazateli (to znamená, že se nepředá odkazem). 27
Vlastnosti Vlastnosti Vlastnosti musí odpovídat určitému vzoru pojmenování. Atribut SpecialName uvedený v pravidle CLS 24 se ignoruje v odpovídajících porovnání názvů a dodržuje pravidla identifikátorů. Vlastnost musí mít metodu getter, metodu setter nebo obojí. 28
Převod typu Převod typů Je-li poskytována op_Implicit nebo op_Explicit, poskytne se alternativní způsob poskytnutí převodu. 39
Typy Typy a podpisy členů typů Typy krabicových hodnot nedodržují předpisy CLS. 3
Typy Typy a podpisy členů typů Všechny typy uvedené v podpisu musí být kompatibilní se specifikací CLS. Všechny typy vytvářející instanci obecného typu musí být kompatibilní se specifikací CLS. 11
Typy Typy a podpisy členů typů Typované odkazy nejsou kompatibilní se specifikací CLS. 14
Typy Typy a podpisy členů typů Nespravované typy ukazatelů nejsou kompatibilní se specifikací CLS. 17
Typy Typy a podpisy členů typů Třídy, typy hodnot a rozhraní kompatibilní se specifikací CLS nevyžadují implementaci nekompatibilních členů CLS. 20
Typy Typy a podpisy členů typů System.Object je kompatibilní se specifikací CLS. Všechny ostatní třídy kompatibilní se specifikací CLS dědí z třídy kompatibilní se specifikací CLS. 23

Indexování pododdílů:

Typy a podpisy členů typu

Typ System.Object je kompatibilní se specifikací CLS a je základním typem všech typů objektů v systému typů .NET. Dědičnost v .NET je buď implicitní (například String třída implicitně dědí z Object třídy) nebo explicitní (například CultureNotFoundException třída explicitně dědí z ArgumentException třídy, která explicitně dědí z exception třídy. Aby byl odvozený typ kompatibilní se specifikací CLS, musí být jeho základní typ také kompatibilní se specifikací CLS.

Následující příklad ukazuje odvozený typ, jehož základní typ není kompatibilní se specifikací CLS. Definuje základní Counter třídu, která jako čítač používá 32bitové celé číslo bez znaménka. Vzhledem k tomu, že třída poskytuje funkce čítače zabalením celého čísla bez znaménka, třída je označena jako nekompatibilní se specifikací CLS. V důsledku toho odvozená třída NonZeroCounter, není také CLS kompatibilní.

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

Všechny typy, které se zobrazují v podpisech členů, včetně návratového typu metody nebo typu vlastnosti, musí být kompatibilní se specifikací CLS. Kromě toho platí pro obecné typy:

  • Všechny typy, které tvoří instanci obecného typu, musí být kompatibilní se specifikací CLS.

  • Všechny typy používané jako omezení obecných parametrů musí být kompatibilní se specifikací CLS.

Systém běžných typů .NET obsahuje mnoho předdefinovaných typů, které jsou podporovány přímo modulem CLR (Common Language Runtime) a jsou speciálně kódovány v metadatech sestavení. Z těchto vnitřních typů jsou typy uvedené v následující tabulce kompatibilní se specifikací CLS.

Typ kompatibilní se specifikací CLS Popis
Bajt 8bitové celé číslo bez znaménka
Int16 16bitové celé číslo se signedm
Int32 32bitové celé číslo se signedm
Int64 64bitové celé číslo se signedm
Polovina Hodnota s plovoucí desetinnou čárkou s poloviční přesností
Jednoho Hodnota s plovoucí desetinnou čárkou s jednoduchou přesností
Dvojité desetinné číslo Hodnota s plovoucí desetinnou čárkou s dvojitou přesností
Logická hodnota true nebo false value type
Char Jednotka kódu kódování UTF-16
Desetinné číslo Desetinné číslo bez plovoucí desetinné čárky
Intptr Ukazatel nebo úchyt definované velikosti platformy
Řetězec Kolekce nulových, jednoho nebo více objektů Char

Vnitřní typy uvedené v následující tabulce nejsou kompatibilní se specifikací CLS.

Nekompatibilní typ Popis Alternativa odpovídající specifikaci CLS
Sbyte 8bitový datový typ celého čísla se signedm Int16
UInt16 16bitové celé číslo bez znaménka Int32
Uint32 32bitové celé číslo bez znaménka Int64
UInt64 64bitové celé číslo bez znaménka Int64 (může přetékat), BigInteger nebo Double
UIntPtr Ukazatel nebo úchyt bez znaménka Intptr

Knihovna tříd .NET nebo jakákoli jiná knihovna tříd může obsahovat jiné typy, které nedodržují předpisy CLS, například:

  • Typy hodnot v rámečku. Následující příklad jazyka C# vytvoří třídu, která má veřejnou vlastnost typu int* s názvem Value. int* Vzhledem k tomu, že je typ hodnoty v rámečku, kompilátor ho označí jako nekompatibilní se specifikací CLS.

    using System;
    
    [assembly:CLSCompliant(true)]
    
    public unsafe class TestClass
    {
       private int* val;
    
       public TestClass(int number)
       {
          val = (int*) number;
       }
    
       public int* Value {
          get { return val; }
       }
    }
    // The compiler generates the following output when compiling this example:
    //        warning CS3003: Type of 'TestClass.Value' is not CLS-compliant
    
  • Typové odkazy, což jsou speciální konstrukce, které obsahují odkaz na objekt a odkaz na typ. Typové odkazy jsou reprezentovány v .NET TypedReference třídou.

Pokud typ nedodržuje předpisy CLS, měli byste na něj použít CLSCompliantAttribute atribut s isCompliant hodnotou false . Další informace najdete v části Atribut CLSCompliantAttribute.

Následující příklad ukazuje problém dodržování předpisů CLS v podpisu metody a v instanci obecného typu. InvoiceItem Definuje třídu s vlastností typu UInt32, vlastnost typu Nullable<UInt32>a konstruktor s parametry typu UInt32 a Nullable<UInt32>. Při pokusu o kompilaci tohoto příkladu se zobrazí čtyři upozornění kompilátoru.

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

Chcete-li odstranit upozornění kompilátoru, nahraďte typy, které nedodržují předpisy CLS ve InvoiceItem veřejném rozhraní, typy kompatibilními s předpisy:

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

Kromě uvedených typů nejsou některé kategorie typů kompatibilní se specifikací CLS. Patří sem nespravované typy ukazatelů a typy ukazatelů funkce. Následující příklad vygeneruje upozornění kompilátoru, protože používá ukazatel na celé číslo k vytvoření pole celých čísel.

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

Pro abstraktní třídy kompatibilní se specifikací CLS (to znamená třídy označené jako abstract v jazyce C# nebo jako MustInherit v jazyce Visual Basic), musí být všechny členy třídy také kompatibilní se specifikací CLS.

Zásady vytváření názvů

Vzhledem k tomu, že některé programovací jazyky nerozlišují malá a velká písmena, musí se identifikátory (například názvy oborů názvů, typů a členů) lišit více než malá a velká písmena. Pokud jsou jejich mapování malými písmeny stejné, považují se za ekvivalentní dva identifikátory. Následující příklad jazyka C# definuje dvě veřejné třídy Person a person. Vzhledem k tomu, že se liší pouze v případě, kompilátor jazyka C# je označí jako nekompatibilní se specifikací CLS.

using System;

[assembly: CLSCompliant(true)]

public class Person : person
{
}

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

Identifikátory programovacího jazyka, jako jsou názvy oborů názvů, typů a členů, musí odpovídat standardu Unicode. To znamená, že:

  • Prvním znakem identifikátoru může být libovolné velké písmeno Unicode, malá písmena, písmeno písmena nadpisu, modifikační písmeno, jiné písmeno nebo číslo písmena. Informace o kategoriích znaků Unicode najdete v výčtu System.Globalization.UnicodeCategory .

  • Další znaky můžou být z libovolné kategorie jako první znak a můžou také obsahovat značky mezer, zkombinování zkombinování značek, desetinných míst, interpunkce spojnice a kódy formátování.

Než porovnáte identifikátory, měli byste vyfiltrovat kódy formátování a převést identifikátory na formát Unicode Normalization Form C, protože jeden znak může být reprezentován několika kódovými jednotkami kódování UTF-16. Sekvence znaků, které vytvářejí stejné jednotky kódu ve formuláři normalizace Unicode C, nejsou kompatibilní se specifikací CLS. Následující příklad definuje vlastnost s názvem , která se skládá ze znaku ANGSTROM SIGN (U+212B) a druhá vlastnost s názvem Å, která se skládá z znaku LATIN CAPITAL LETTER A WITH RING ABOVE (U+00C5). Kompilátory jazyka C# i Visual Basic označí zdrojový kód jako nekompatibilní se specifikací CLS.

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

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

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

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

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

Názvy členů v určitém oboru (například obory názvů v rámci sestavení, typy v rámci oboru názvů nebo členy v rámci typu) musí být jedinečné s výjimkou názvů, které jsou přetíženy. Tento požadavek je přísnější než systém běžných typů, který umožňuje více členům v oboru mít stejné názvy, pokud jsou různé druhy členů (například jedna je metoda a jedno je pole). Konkrétně pro členy typu:

  • Pole a vnořené typy se rozlišují samostatně podle názvu.

  • Metody, vlastnosti a události, které mají stejný název, se musí lišit více než jen návratový typ.

Následující příklad ukazuje požadavek, že názvy členů musí být jedinečné v rámci jejich oboru. Definuje třídu s názvem Converter , která obsahuje čtyři členy s názvem Conversion. Tři jsou metody a jedna je vlastnost. Metoda, která obsahuje Int64 parametr, má jedinečný název, ale dvě metody s parametrem Int32 nejsou, protože návratová hodnota není považována za součást podpisu člena. Vlastnost Conversion také porušuje tento požadavek, protože vlastnosti nemohou mít stejný název jako přetížené metody.

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

Jednotlivé jazyky obsahují jedinečná klíčová slova, takže jazyky, které cílí na modul ClR (Common Language Runtime), musí také poskytovat určitý mechanismus pro odkazování na identifikátory (například názvy typů), které se shodují s klíčovými slovy. Například case je klíčové slovo v jazyce C# i v jazyce Visual Basic. Následující příklad jazyka Visual Basic však dokáže nejednoznačit třídu pojmenovanou case z klíčového case slova pomocí levých a uzavíracích závorek. V opačném případě by se v příkladu zobrazila chybová zpráva " Klíčové slovo není platné jako identifikátor" a nepodařilo se zkompilovat.

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

Následující příklad jazyka C# umožňuje vytvořit instanci case třídy pomocí symbolu @ k nejednoznačnosti identifikátoru z klíčového slova jazyka. Bez něj by kompilátor jazyka C# zobrazil dvě chybové zprávy Typu očekávané a Neplatný výraz case.

using System;

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

Převod typu

Specifikace společného jazyka definuje dva operátory převodu:

  • op_Implicit, který se používá pro rozšiřující převody, které nemají za následek ztrátu dat nebo přesnosti. Struktura Decimal například obsahuje přetížený op_Implicit operátor pro převod hodnot integrálních typů a Char hodnot na Decimal hodnoty.

  • op_Explicit, který se používá pro zužující převody, které mohou vést ke ztrátě velikosti (hodnota je převedena na hodnotu, která má menší rozsah) nebo přesnost. Struktura například Decimal obsahuje přetížený op_Explicit operátor pro převod Double a Single hodnoty Decimal a převod Decimal hodnot na celočíselné hodnoty, Double, Singlea Char.

Ne všechny jazyky však podporují přetížení operátoru nebo definici vlastních operátorů. Pokud se rozhodnete tyto převodní operátory implementovat, měli byste také poskytnout alternativní způsob, jak provést převod. Doporučujeme, abyste zadali Frommetody Xxx a ToXxx .

Následující příklad definuje implicitní a explicitní převody kompatibilní se specifikací CLS. UDouble Vytvoří třídu, která představuje číslo s plovoucí desetinnou čárkou bez znaménka s dvojitou přesností. Poskytuje implicitní převody z UDouble do a pro explicitní převody z UDoubleSingledo , Double do UDoublea Single do UDouble.Double Také definuje metodu ToDouble jako alternativu k implicitnímu převodnímu operátoru ToSingleFromDoublea , a FromSingle metody jako alternativy k explicitním převodním operátorům.

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

Pole

Pole kompatibilní se specifikací CLS odpovídají následujícím pravidlům:

  • Všechny dimenze pole musí mít dolní mez nuly. Následující příklad vytvoří pole, které nedodržuje předpisy CLS, s dolní mezí jednoho. Navzdory přítomnosti atributu CLSCompliantAttribute kompilátor nezjistí, že pole vrácené metodou Numbers.GetTenPrimes není kompatibilní se specifikací CLS.

    [assembly: CLSCompliant(true)]
    
    public class Numbers
    {
       public static Array GetTenPrimes()
       {
          Array arr = Array.CreateInstance(typeof(Int32), new int[] {10}, new int[] {1});
          arr.SetValue(1, 1);
          arr.SetValue(2, 2);
          arr.SetValue(3, 3);
          arr.SetValue(5, 4);
          arr.SetValue(7, 5);
          arr.SetValue(11, 6);
          arr.SetValue(13, 7);
          arr.SetValue(17, 8);
          arr.SetValue(19, 9);
          arr.SetValue(23, 10);
    
          return arr;
       }
    }
    
    <Assembly: CLSCompliant(True)>
    
    Public Class Numbers
        Public Shared Function GetTenPrimes() As Array
            Dim arr As Array = Array.CreateInstance(GetType(Int32), {10}, {1})
            arr.SetValue(1, 1)
            arr.SetValue(2, 2)
            arr.SetValue(3, 3)
            arr.SetValue(5, 4)
            arr.SetValue(7, 5)
            arr.SetValue(11, 6)
            arr.SetValue(13, 7)
            arr.SetValue(17, 8)
            arr.SetValue(19, 9)
            arr.SetValue(23, 10)
    
            Return arr
        End Function
    End Class
    
  • Všechny prvky pole se musí skládat z typů kompatibilních se specifikací CLS. Následující příklad definuje dvě metody, které vracejí pole, která nedodržují předpisy CLS. První vrátí matici UInt32 hodnot. Druhá vrátí matici Object , která obsahuje Int32 a UInt32 hodnoty. Přestože kompilátor identifikuje první pole jako nevyhovující z důvodu svého UInt32 typu, nerozpozná se, že druhé pole obsahuje nekompatibilní prvky CLS.

    using System;
    
    [assembly: CLSCompliant(true)]
    
    public class Numbers
    {
       public static UInt32[] GetTenPrimes()
       {
          uint[] arr = { 1u, 2u, 3u, 5u, 7u, 11u, 13u, 17u, 19u };
          return arr;
       }
    
       public static Object[] GetFivePrimes()
       {
          Object[] arr = { 1, 2, 3, 5u, 7u };
          return arr;
       }
    }
    // Compilation produces a compiler warning like the following:
    //    Array2.cs(8,27): warning CS3002: Return type of 'Numbers.GetTenPrimes()' is not
    //            CLS-compliant
    
    <Assembly: CLSCompliant(True)>
    
    Public Class Numbers
        Public Shared Function GetTenPrimes() As UInt32()
            Return {1ui, 2ui, 3ui, 5ui, 7ui, 11ui, 13ui, 17ui, 19ui}
        End Function
    
        Public Shared Function GetFivePrimes() As Object()
            Dim arr() As Object = {1, 2, 3, 5ui, 7ui}
            Return arr
        End Function
    End Class
    ' Compilation produces a compiler warning like the following:
    '    warning BC40027: Return type of function 'GetTenPrimes' is not CLS-compliant.
    '    
    '       Public Shared Function GetTenPrimes() As UInt32()
    '                              ~~~~~~~~~~~~
    
  • Rozlišení přetížení pro metody, které mají parametry pole, je založeno na skutečnosti, že jsou pole a jejich typ prvku. Z tohoto důvodu je následující definice přetížené GetSquares metody kompatibilní se specifikací CLS.

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

Rozhraní

Rozhraní kompatibilní se specifikací CLS mohou definovat vlastnosti, události a virtuální metody (metody bez implementace). Rozhraní kompatibilní se specifikací CLS nemůže mít následující:

  • Statické metody nebo statická pole Kompilátory jazyka C# i Visual Basic generují chyby kompilátoru, pokud v rozhraní definujete statický člen.

  • Pole. Kompilátory jazyka C# i Visual Basic generují chyby kompilátoru, pokud definujete pole v rozhraní.

  • Metody, které nedodržují předpisy CLS. Například následující definice rozhraní obsahuje metodu, INumber.GetUnsignedkterá je označena jako nedodržuje předpisy CLS. Tento příklad vygeneruje upozornění kompilátoru.

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

    Kvůli tomuto pravidlu nejsou typy kompatibilní se specifikací CLS nutné k implementaci členů, které nedodržují předpisy CLS. Pokud architektura kompatibilní se specifikací CLS zveřejňuje třídu, která implementuje rozhraní, které není kompatibilní se specifikací CLS, měla by také poskytovat konkrétní implementace všech členů, které nedodržují předpisy CLS.

Kompilátory jazyka kompatibilní se specifikací CLS musí také umožňovat, aby třída poskytovala samostatné implementace členů se stejným názvem a podpisem ve více rozhraních. C# i Visual Basic podporují explicitní implementace rozhraní, aby poskytovaly různé implementace identicky pojmenovaných metod. Visual Basic také podporuje Implements klíčové slovo, které umožňuje explicitně určit, které rozhraní a člen určitý člen implementuje. Následující příklad ukazuje tento scénář definováním Temperature třídy, která implementuje ICelsius rozhraní IFahrenheit jako explicitní implementace rozhraní.

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

Výčty

Výčty kompatibilní se specifikací CLS musí dodržovat tato pravidla:

  • Základní typ výčtu musí být vnitřní celé číslo kompatibilní se specifikací CLS (Byte, Int16, Int32nebo Int64). Například následující kód se pokusí definovat výčet, jehož základní typ je UInt32 a generuje upozornění kompilátoru.

    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
    '                ~~~~
    
  • Typ výčtu musí mít jedno pole instance s názvem Value__ označené atributem FieldAttributes.RTSpecialName . To vám umožní implicitně odkazovat na hodnotu pole.

  • Výčet obsahuje literální statická pole, jejichž typy odpovídají typu samotné výčtu. Pokud například definujete State výčet s hodnotami State.On a State.OffState.On a State.Off jsou oba literální statické pole, jejichž typ je State.

  • Existují dva druhy výčtů:

    • Výčet, který představuje sadu vzájemně se vylučujících pojmenovaných celočíselné hodnoty. Tento typ výčtu je označen absencí vlastního atributu System.FlagsAttribute .

    • Výčet, který představuje sadu bitových příznaků, které mohou kombinovat za účelem vygenerování nepojmenované hodnoty. Tento typ výčtu je označen přítomností vlastního atributu System.FlagsAttribute .

    Další informace najdete v dokumentaci ke struktuře Enum .

  • Hodnota výčtu není omezena na rozsah zadaných hodnot. Jinými slovy, rozsah hodnot ve výčtu je oblast jeho podkladové hodnoty. Metodu Enum.IsDefined můžete použít k určení, zda je zadaná hodnota členem výčtu.

Obecné typy členů

Specifikace společného jazyka vyžaduje přístup ke všem polím a metodám jako členům konkrétní třídy. Globální statická pole a metody (tj. statická pole nebo metody definované kromě typu) proto nejsou kompatibilní se specifikací CLS. Pokud se pokusíte do zdrojového kódu zahrnout globální pole nebo metodu, kompilátory jazyka C# i Visual Basic vygenerují chybu kompilátoru.

Specifikace common language podporuje pouze standardní spravovanou konvenci volání. Nepodporuje nespravované konvence volání a metody se seznamy argumentů proměnných označenými klíčovým slovem varargs . Pro seznamy argumentů proměnných, které jsou kompatibilní se standardní spravovanou konvencí volání, použijte ParamArrayAttribute atribut nebo implementaci jednotlivého jazyka, například params klíčové slovo v jazyce C# a ParamArray klíčové slovo v jazyce Visual Basic.

Přístupnost členů

Přepsání zděděného člena nemůže změnit přístupnost daného člena. Například veřejnou metodu v základní třídě nelze přepsat privátní metodou v odvozené třídě. Existuje jedna výjimka: protected internal člen (v jazyce C#) nebo Protected Friend (v jazyce Visual Basic) v jednom sestavení, který je přepsán typem v jiném sestavení. V takovém případě je Protectedpřístupnost přepsání .

Následující příklad ukazuje chybu, která je generována, když CLSCompliantAttribute atribut je nastavena na true, a Human, což je třída odvozená z Animal, se pokusí změnit přístupnost Species vlastnosti z veřejné na privátní. Příklad se úspěšně zkompiluje, pokud se jeho přístupnost změní na veřejnou.

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

Typy v podpisu člena musí být přístupné vždy, když je tento člen přístupný. To například znamená, že veřejný člen nemůže zahrnout parametr, jehož typ je soukromý, chráněný nebo interní. Následující příklad ilustruje chybu kompilátoru, která má za následek, když StringWrapper konstruktor třídy zveřejňuje interní StringOperationType hodnotu výčtu, která určuje, jak má být řetězcová hodnota zabalena.

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

Obecné typy a členy

Vnořené typy vždy mají alespoň tolik obecných parametrů jako jejich uzavřený typ. Ty odpovídají obecným parametrům v uzavřeném typu. Obecný typ může také obsahovat nové obecné parametry.

Vztah mezi parametry obecného typu obsahujícího typu a jeho vnořenými typy může být skrytý syntaxí jednotlivých jazyků. V následujícím příkladu obecný typ Outer<T> obsahuje dvě vnořené třídy Inner1A a Inner1B<U>. Volání ToString metody, kterou každá třída dědí z Object.ToString(), ukazuje, že každá vnořená třída obsahuje parametry typu své obsahující třídy.

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]

Obecné názvy typů jsou kódovány v názvu formuláře n, kde název je název typu, ' je literál znaku a n je počet parametrů deklarovaných v typu, nebo pro vnořené obecné typy počet nově zavedených parametrů typu. Toto kódování obecných názvů typů je primárně zajímavé pro vývojáře, kteří používají reflexi pro přístup k obecným typům stížností CLS v knihovně.

Pokud se omezení použijí na obecný typ, musí být všechny typy používané jako omezení také kompatibilní se specifikací CLS. Následující příklad definuje třídu s názvem BaseClass , která není kompatibilní se specifikací CLS, a obecnou třídu s názvem BaseCollection , jejíž typ parametr musí být odvozen z BaseClass. Protože BaseClass ale není kompatibilní se specifikací CLS, kompilátor vygeneruje upozornění.

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

Pokud je obecný typ odvozen z obecného základního typu, musí předefinovat všechna omezení, aby bylo možné zaručit, že jsou splněna také omezení základního typu. Následující příklad definuje Number<T> , který může představovat libovolný číselný typ. Definuje také FloatingPoint<T> třídu, která představuje hodnotu s plovoucí desetinou čárkou. Zdrojový kód se však nepodaří zkompilovat, protože nevztahuje omezení Number<T> (to T musí být typ hodnoty) na 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)
'                                                          ~

Příklad se úspěšně zkompiluje, pokud je omezení přidáno do FloatingPoint<T> třídy.

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

Specifikace společného jazyka ukládá konzervativní model pro vytvoření instance pro vnořené typy a chráněné členy. Otevřené obecné typy nemohou vystavit pole nebo členy s podpisy, které obsahují určitou instanci vnořeného chráněného obecného typu. Ne generické typy, které rozšiřují určitou instanci obecné základní třídy nebo rozhraní, nemohou zveřejnit pole nebo členy s podpisy, které obsahují jinou instanci vnořeného chráněného obecného typu.

Následující příklad definuje obecný typ C1<T> (nebo C1(Of T) v jazyce Visual Basic) a chráněnou třídu C1<T>.N (nebo C1(Of T).N v jazyce Visual Basic). C1<T> má dvě metody a M1M2. Není však kompatibilní se specifikací CLS, M1 protože se pokouší vrátit C1<int>.N (nebo C1(Of Integer).N) objekt z C1<T> (nebo C1(Of T)). Druhá třída, C2je odvozena od C1<long> (nebo C1(Of Long)). Má dvě metody a M3M4. M3 není kompatibilní se specifikací CLS, protože se pokouší vrátit C1<int>.N (nebo C1(Of Integer).N) objekt z podtřídy C1<long>. Kompilátory jazyka můžou být ještě více omezující. V tomto příkladu Visual Basic zobrazí chybu při pokusu o kompilaci 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)  
'                             ~~~~~~~~~~~~~

Konstruktory

Konstruktory v třídách a strukturách kompatibilních se specifikací CLS musí dodržovat tato pravidla:

  • Konstruktor odvozené třídy musí volat konstruktor instance jeho základní třídy před přístupem k zděděným datům instance. Tento požadavek je způsoben tím, že konstruktory základní třídy nejsou zděděné jejich odvozené třídy. Toto pravidlo se nevztahuje na struktury, které nepodporují přímou dědičnost.

    Kompilátory obvykle vynucují toto pravidlo nezávisle na dodržování předpisů CLS, jak ukazuje následující příklad. Doctor Vytvoří třídu, která je odvozena z Person třídy, ale Doctor třída nemůže volat Person konstruktor třídy inicializovat zděděná pole instance.

    using System;
    
    [assembly: CLSCompliant(true)]
    
    public class Person
    {
       private string fName, lName, _id;
    
       public Person(string firstName, string lastName, string id)
       {
          if (String.IsNullOrEmpty(firstName + lastName))
             throw new ArgumentNullException("Either a first name or a last name must be provided.");
    
          fName = firstName;
          lName = lastName;
          _id = id;
       }
    
       public string FirstName
       {
          get { return fName; }
       }
    
       public string LastName
       {
          get { return lName; }
       }
    
       public string Id
       {
          get { return _id; }
       }
    
       public override string ToString()
       {
          return String.Format("{0}{1}{2}", fName,
                               String.IsNullOrEmpty(fName) ?  "" : " ",
                               lName);
       }
    }
    
    public class Doctor : Person
    {
       public Doctor(string firstName, string lastName, string id)
       {
       }
    
       public override string ToString()
       {
          return "Dr. " + base.ToString();
       }
    }
    // Attempting to compile the example displays output like the following:
    //    ctor1.cs(45,11): error CS1729: 'Person' does not contain a constructor that takes 0
    //            arguments
    //    ctor1.cs(10,11): (Location of symbol related to previous error)
    
    <Assembly: CLSCompliant(True)>
    
    Public Class Person
        Private fName, lName, _id As String
    
        Public Sub New(firstName As String, lastName As String, id As String)
            If String.IsNullOrEmpty(firstName + lastName) Then
                Throw New ArgumentNullException("Either a first name or a last name must be provided.")
            End If
    
            fName = firstName
            lName = lastName
            _id = id
        End Sub
    
        Public ReadOnly Property FirstName As String
            Get
                Return fName
            End Get
        End Property
    
        Public ReadOnly Property LastName As String
            Get
                Return lName
            End Get
        End Property
    
        Public ReadOnly Property Id As String
            Get
                Return _id
            End Get
        End Property
    
        Public Overrides Function ToString() As String
            Return String.Format("{0}{1}{2}", fName,
                                 If(String.IsNullOrEmpty(fName), "", " "),
                                 lName)
        End Function
    End Class
    
    Public Class Doctor : Inherits Person
        Public Sub New(firstName As String, lastName As String, id As String)
        End Sub
    
        Public Overrides Function ToString() As String
            Return "Dr. " + MyBase.ToString()
        End Function
    End Class
    ' Attempting to compile the example displays output like the following:
    '    Ctor1.vb(46) : error BC30148: First statement of this 'Sub New' must be a call 
    '    to 'MyBase.New' or 'MyClass.New' because base class 'Person' of 'Doctor' does 
    '    not have an accessible 'Sub New' that can be called with no arguments.
    '    
    '       Public Sub New()
    '                  ~~~
    
  • Konstruktor objektů nelze volat s výjimkou vytvoření objektu. Objekt navíc nelze inicializovat dvakrát. To například znamená, že Object.MemberwiseClone a deserializační metody nesmí volat konstruktory.

Vlastnosti

Vlastnosti v typech kompatibilních se specifikací CLS musí dodržovat tato pravidla:

  • Vlastnost musí mít setter, getter nebo obojí. V sestavení jsou implementovány jako speciální metody, což znamená, že se zobrazí jako samostatné metody (getter je název get_vlastnosti a setter jeset_ propertyname) označené jako SpecialName v metadatech sestavení. Kompilátory jazyka C# a Visual Basic vynucují toto pravidlo automaticky bez nutnosti použít CLSCompliantAttribute atribut.

  • Typ vlastnosti je návratový typ vlastnosti getter a poslední argument setter. Tyto typy musí být kompatibilní se specifikací CLS a argumenty nelze k vlastnosti přiřadit odkazem (to znamená, že nelze spravovat ukazatele).

  • Pokud má vlastnost getter i setter, musí být obě virtuální, obě statické nebo obě instance. Kompilátory jazyka C# a Visual Basic automaticky vynucují toto pravidlo prostřednictvím syntaxe definice vlastnosti.

Událost

Událost je definována jeho názvem a jeho typem. Typ události je delegát, který slouží k označení události. Například AppDomain.AssemblyResolve událost je typu ResolveEventHandler. Kromě samotné události tři metody s názvy na základě názvu události poskytují implementaci události a jsou označené jako SpecialName v metadatech sestavení:

  • Metoda přidání obslužné rutiny události s názvem add_EventName. Například metoda odběru AppDomain.AssemblyResolve události pro událost je pojmenována add_AssemblyResolve.

  • Metoda pro odebrání obslužné rutiny události s názvem remove_EventName. Například metoda odebrání události AppDomain.AssemblyResolve má název remove_AssemblyResolve.

  • Metoda označující, že došlo k události s názvem raise_EventName.

Poznámka:

Většina pravidel specifikace společného jazyka týkající se událostí jsou implementována kompilátory jazyka a jsou transparentní pro vývojáře komponent.

Metody přidání, odebrání a vyvolání události musí mít stejnou přístupnost. Všechny musí být také statické, instance nebo virtuální. Metody pro přidání a odebrání události mají jeden parametr, jehož typ je typ delegáta události. Metody přidání a odebrání musí být přítomny nebo obě chybí.

Následující příklad definuje třídu kompatibilní se specifikací CLS s názvem Temperature , která vyvolá TemperatureChanged událost, pokud se změna teploty mezi dvěma čteními rovná nebo překročí prahovou hodnotu. Třída Temperature explicitně definuje metodu raise_TemperatureChanged , aby mohl selektivně spouštět obslužné rutiny událostí.

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

Přetížení

Specifikace běžného jazyka ukládá na přetížené členy následující požadavky:

  • Členy lze přetížit na základě počtu parametrů a typu libovolného parametru. Konvence volání, návratový typ, vlastní modifikátory použité pro metodu nebo jeho parametr a zda jsou parametry předány hodnotou nebo odkazem, nejsou při odlišování mezi přetíženími považovány. Podívejte se například na kód požadavku, který musí být jedinečný v rámci oboru v části Zásady vytváření názvů .

  • Přetížit lze pouze vlastnosti a metody. Pole a události nelze přetížit.

  • Obecné metody lze přetížit na základě počtu jejich obecných parametrů.

Poznámka:

Operátory op_Explicit jsou op_Implicit výjimky pravidla, které návratová hodnota není považována za součást podpisu metody pro rozlišení přetížení. Tyto dva operátory mohou být přetíženy na základě jejich parametrů a jejich návratové hodnoty.

Výjimky

Objekty výjimky musí být odvozeny z System.Exception nebo z jiného typu odvozeného z System.Exception. Následující příklad znázorňuje chybu kompilátoru, která má za následek použití vlastní třídy s názvem ErrorClass pro zpracování výjimek.

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

Chcete-li tuto chybu opravit, ErrorClass třída musí dědit z System.Exception. Kromě toho Message musí být vlastnost přepsána. Následující příklad opraví tyto chyby a definuje ErrorClass třídu, která je kompatibilní se specifikací CLS.

using System;

[assembly: CLSCompliant(true)]

public class ErrorClass : Exception
{
   string msg;

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

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

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

<Assembly: CLSCompliant(True)>

Public Class ErrorClass : Inherits Exception
    Dim msg As String

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

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

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

Atributy

V sestaveních .NET poskytují vlastní atributy rozšiřitelný mechanismus pro ukládání vlastních atributů a načítání metadat o programovacích objektech, jako jsou sestavení, typy, členy a parametry metody. Vlastní atributy musí být odvozeny ze System.Attribute nebo z typu odvozeného z System.Attribute.

Následující příklad porušuje toto pravidlo. NumericAttribute Definuje třídu, která není odvozena z System.Attribute. Chyba kompilátoru se zobrazí pouze v případě, že je použit atribut, který nedodržuje předpisy CLS, a ne při definování třídy.

using System;

[assembly: CLSCompliant(true)]

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

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

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

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

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

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

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

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

Konstruktor nebo vlastnosti atributu kompatibilního se specifikací CLS mohou vystavit pouze následující typy:

Následující příklad definuje DescriptionAttribute třídu, která je odvozena z Attribute. Konstruktor třídy má parametr typu Descriptor, takže třída není kompatibilní se specifikací CLS. Kompilátor jazyka C# vygeneruje upozornění, ale úspěšně se zkompiluje, zatímco kompilátor jazyka Visual Basic nevygeneruje upozornění ani chybu.

using System;

[assembly:CLSCompliantAttribute(true)]

public enum DescriptorType { type, member };

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

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

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

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

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

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

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

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

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

Atribut CLSCompliantAttribute

Atribut CLSCompliantAttribute slouží k označení, zda prvek programu vyhovuje specifikaci common language. Konstruktor CLSCompliantAttribute(Boolean) obsahuje jeden povinný parametr isCompliant, který označuje, zda je prvek programu kompatibilní se specifikací CLS.

V době kompilace kompilátor zjistí nekompatibilní prvky, u nichž se předpokládá, že jsou kompatibilní se specifikací CLS, a vygeneruje upozornění. Kompilátor nevysílají upozornění pro typy nebo členy, které jsou explicitně deklarovány jako nevyhovující.

Vývojáři komponent mohou atribut používat CLSCompliantAttribute dvěma způsoby:

  • Chcete-li definovat části veřejného rozhraní vystavené komponentou, která je kompatibilní se specifikací CLS, a části, které nejsou kompatibilní se specifikací CLS. Pokud se atribut používá k označení konkrétních prvků programu jako kompatibilních se specifikací CLS, jeho použití zaručuje, že tyto prvky jsou přístupné ze všech jazyků a nástrojů, které cílí na .NET.

  • Aby bylo zajištěno, že veřejné rozhraní knihovny komponent zveřejňuje pouze prvky programu, které jsou kompatibilní se specifikací CLS. Pokud prvky nedodržují předpisy CLS, kompilátory obvykle zobrazí upozornění.

Upozorňující

V některých případech kompilátory jazyka vynucují pravidla kompatibilní se specifikací CLS bez ohledu na to, jestli CLSCompliantAttribute se atribut používá. Například definování statického členu v rozhraní porušuje pravidlo CLS. Pokud v tomto ohledu definujete static (v jazyce C#) nebo Shared (v jazyce Visual Basic) člena v rozhraní, zobrazí se v kompilátorech jazyka C# i Visual Basic chybová zpráva a kompilace aplikace se nezdaří.

Atribut CLSCompliantAttribute je označen atributem AttributeUsageAttribute , který má hodnotu AttributeTargets.All. Tato hodnota umožňuje použít CLSCompliantAttribute atribut na libovolný prvek programu, včetně sestavení, modulů, typů (tříd, struktur, výčtů, rozhraní a delegátů), členů typu (konstruktory, metody, vlastnosti, pole a události), parametrů, obecných parametrů a návratových hodnot. V praxi byste však měli použít atribut pouze na sestavení, typy a členy typu. V opačném případě kompilátory atribut ignorují a budou dál generovat upozornění kompilátoru vždy, když ve veřejném rozhraní knihovny narazí na nevyhovující parametr, obecný parametr nebo návratovou hodnotu.

Hodnota atributu CLSCompliantAttribute je zděděna obsaženými prvky programu. Pokud je například sestavení označeno jako kompatibilní se specifikací CLS, jeho typy jsou také kompatibilní se specifikací CLS. Pokud je typ označený jako kompatibilní se specifikací CLS, jeho vnořené typy a členy jsou také kompatibilní se specifikací CLS.

Zděděné dodržování předpisů můžete explicitně přepsat použitím atributu CLSCompliantAttribute na obsažený prvek programu. Můžete například použít CLSCompliantAttribute atribut s isCompliant hodnotou definovat false nekompatibilní typ v kompatibilním sestavení a můžete použít atribut s isCompliant hodnotou true k definování kompatibilního typu v nekompatibilním sestavení. Členy, které nedodržují předpisy, můžete také definovat v typu vyhovujícím předpisům. Nekompatibilní typ však nemůže obsahovat členy, které splňují předpisy, takže atribut s isCompliant hodnotou true přepsání dědičnosti z nekompatibilního typu nelze použít.

Při vývoji komponent byste vždy měli použít CLSCompliantAttribute atribut k označení, zda vaše sestavení, jeho typy a její členy jsou kompatibilní se specifikací CLS.

Vytvoření komponent kompatibilních se specifikací CLS:

  1. CLSCompliantAttribute Označte sestavení jako kompatibilní se specifikací CLS.

  2. Označte všechny veřejně vystavené typy v sestavení, které nedodržují předpisy CLS jako nevyhovující předpisům.

  3. Označte všechny veřejně zveřejněné členy v typech kompatibilních se specifikací CLS jako nekompatibilní.

  4. Poskytněte alternativu kompatibilní se specifikací CLS pro členy, které nedodržují předpisy CLS.

Pokud jste úspěšně označili všechny typy a členy nedodržující předpisy, kompilátor by neměl generovat žádná upozornění, která nedodržují předpisy. Měli byste ale určit, kteří členové nedodržují předpisy CLS, a uvést jejich alternativy kompatibilní se specifikací CLS v dokumentaci k produktu.

Následující příklad používá CLSCompliantAttribute atribut k definování sestavení kompatibilního se specifikací CLS a typu, CharacterUtilitieskterý má dva nekompatibilní členy CLS. Vzhledem k tomu, že oba členy jsou označeny atributem CLSCompliant(false) , kompilátor nevygeneruje žádná upozornění. Třída také poskytuje alternativu kompatibilní se specifikací CLS pro obě metody. Obvykle bychom do metody přidali dvě přetížení, abychom poskytli alternativy ToUTF16 kompatibilní se specifikací CLS. Vzhledem k tomu, že metody nelze přetížit na základě návratové hodnoty, názvy metod kompatibilních se specifikací CLS se liší od názvů nekompatibilních metod.

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

Pokud vyvíjíte aplikaci místo knihovny (to znamená, že nepoužíváte typy nebo členy, které můžou využívat jiní vývojáři aplikací), je dodržování předpisů CLS prvků programu, které vaše aplikace využívá, zajímavé jenom v případě, že je váš jazyk nepodporuje. V takovém případě kompilátor jazyka vygeneruje chybu při pokusu o použití nekompatibilního elementu CLS.

Interoperabilita mezi jazyky

Nezávislost jazyka má několik možných významů. Jedním významem je bezproblémové využívání typů napsaných v jednom jazyce z aplikace napsané v jiném jazyce. Druhý význam, který je zaměřen na tento článek, zahrnuje kombinování kódu napsaného v několika jazycích do jednoho sestavení .NET.

Následující příklad znázorňuje interoperabilitu mezi jazyky vytvořením knihovny tříd s názvem Utilities.dll, která obsahuje dvě třídy NumericLib a StringLib. Třída NumericLib je napsána v jazyce C# a třída StringLib je napsána v jazyce Visual Basic. Tady je zdrojový kód pro StringUtil.vb, který zahrnuje jeden člen , ToTitleCaseve své StringLib třídě.

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

Zde je zdrojový kód pro NumberUtil.cs, který definuje třídu NumericLib se dvěma členy IsEven a NearZero.

using System;

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

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

Chcete-li zabalit dvě třídy do jednoho sestavení, musíte je zkompilovat do modulů. Pro kompilaci zdrojového kódu jazyka Visual Basic do modulu použijte tento příkaz:

vbc /t:module StringUtil.vb

Další informace o syntaxi příkazového řádku kompilátoru jazyka Visual Basic naleznete v tématu Sestavení z příkazového řádku.

Pro kompilaci zdrojového kódu jazyka C# do modulu použijte tento příkaz:

csc /t:module NumberUtil.cs

Pak pomocí možností Linkeru zkompilujete tyto dva moduly do sestavení:

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

Následující příklad následně volá metody NumericLib.NearZero a StringLib.ToTitleCase. Kód jazyka Visual Basic i kód jazyka C# mají přístup k metodám v obou třídách.

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

Pro kompilaci kódu jazyka Visual Basic použijte tento příkaz:

vbc example.vb /r:UtilityLib.dll

Chcete-li kompilovat pomocí jazyka C#, změňte název kompilátoru z vbc na csca změňte příponu souboru z .vb na .cs:

csc example.cs /r:UtilityLib.dll