Partilhar via


Independência linguística e componentes independentes da língua

O .NET é independente do idioma. Isso significa que, como desenvolvedor, você pode desenvolver em uma das muitas linguagens que visam implementações .NET, como C#, F# e Visual Basic. Você pode acessar os tipos e membros de bibliotecas de classe desenvolvidas para implementações .NET sem ter que saber a linguagem na qual eles foram originalmente escritos e sem ter que seguir qualquer uma das convenções do idioma original. Se você for um desenvolvedor de componentes, seu componente poderá ser acessado por qualquer aplicativo .NET, independentemente de seu idioma.

Nota

Esta primeira parte deste artigo discute a criação de componentes independentes de idioma, ou seja, componentes que podem ser consumidos por aplicativos escritos em qualquer idioma. Você também pode criar um único componente ou aplicativo a partir do código-fonte escrito em vários idiomas; consulte Interoperabilidade entre idiomas na segunda parte deste artigo.

Para interagir totalmente com outros objetos escritos em qualquer idioma, os objetos devem expor aos chamadores apenas os recursos que são comuns a todos os idiomas. Esse conjunto comum de recursos é definido pela Common Language Specification (CLS), que é um conjunto de regras que se aplicam aos assemblies gerados. A Common Language Specification é definida na Partição I, Cláusulas 7 a 11 da Norma ECMA-335: Common Language Infrastructure.

Se o componente estiver em conformidade com a Common Language Specification, é garantido que ele seja compatível com CLS e pode ser acessado a partir de código em assemblies escritos em qualquer linguagem de programação que suporte o CLS. Você pode determinar se seu componente está em conformidade com a Common Language Specification em tempo de compilação aplicando o atributo CLSCompliantAttribute ao seu código-fonte. Para obter mais informações, consulte O atributo CLSCompliantAttribute.

Regras de conformidade CLS

Esta seção discute as regras para criar um componente compatível com CLS. Para obter uma lista completa de regras, consulte Partição I, Cláusula 11 da Norma ECMA-335: Common Language Infrastructure.

Nota

A Common Language Specification discute cada regra para conformidade com CLS como ela se aplica a consumidores (desenvolvedores que estão acessando programaticamente um componente compatível com CLS), frameworks (desenvolvedores que estão usando um compilador de linguagem para criar bibliotecas compatíveis com CLS) e extensores (desenvolvedores que estão criando uma ferramenta como um compilador de linguagem ou um analisador de código que cria componentes compatíveis com CLS). Este artigo centra-se nas regras aplicáveis aos enquadramentos. Observe, no entanto, que algumas das regras que se aplicam a extensores também podem se aplicar a assemblies criados usando Reflection.Emit.

Para projetar um componente independente de idioma, você só precisa aplicar as regras de conformidade CLS à interface pública do componente. Sua implementação privada não precisa estar em conformidade com a especificação.

Importante

As regras de conformidade CLS aplicam-se apenas à interface pública de um componente, não à sua implementação privada.

Por exemplo, inteiros não assinados diferentes não Byte são compatíveis com CLS. Como a Person classe no exemplo a seguir expõe uma Age propriedade do tipo UInt16, o código a seguir exibe um aviso do compilador.

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

Você pode tornar a Person classe compatível com CLS alterando o tipo da Age propriedade de UInt16 para Int16, que é um inteiro assinado de 16 bits compatível com CLS. Não é necessário alterar o tipo de campo privado personAge .

using System;

[assembly: CLSCompliant(true)]

public class Person
{
   private Int16 personAge = 0;

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

Public Class Person
    Private personAge As UInt16

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

A interface pública de uma biblioteca consiste no seguinte:

  • Definições de classes públicas.

  • Definições dos membros públicos de classes públicas e definições de membros acessíveis a classes derivadas (ou seja, membros protegidos).

  • Parâmetros e tipos de retorno de métodos públicos de classes públicas e parâmetros e tipos de retorno de métodos acessíveis a classes derivadas.

As regras para conformidade com CLS estão listadas na tabela a seguir. O texto das regras é retirado literalmente da Norma ECMA-335: Common Language Infrastructure, que é Copyright 2012 pela Ecma International. Informações mais detalhadas sobre essas regras podem ser encontradas nas seções a seguir.

Categoria Consulte Regra Número da regra
Acessibilidade Acessibilidade para membros A acessibilidade não deve ser alterada quando se sobrepõem métodos herdados, exceto quando se substitui um método herdado de um conjunto diferente por acessibilidade family-or-assembly. Neste caso, a neutralização deve ter acessibilidade family. 10
Acessibilidade Acessibilidade para membros A visibilidade e a acessibilidade dos tipos e membros devem ser tais que os tipos na assinatura de qualquer membro sejam visíveis e acessíveis sempre que o próprio membro seja visível e acessível. Por exemplo, um método público que é visível fora de seu assembly não deve ter um argumento cujo tipo é visível apenas dentro do assembly. A visibilidade e a acessibilidade dos tipos que compõem um tipo genérico instanciado utilizado na assinatura de qualquer membro devem ser visíveis e acessíveis sempre que o próprio membro seja visível e acessível. Por exemplo, um tipo genérico instanciado presente na assinatura de um membro que é visível fora de seu assembly não deve ter um argumento genérico cujo tipo é visível apenas dentro do assembly. 12
Matrizes Matrizes As matrizes devem ter elementos com um tipo compatível com CLS, e todas as dimensões da matriz devem ter limites inferiores de zero. Apenas o facto de um elemento ser uma matriz e o tipo de elemento da matriz devem ser obrigados a distinguir entre sobrecargas. Quando a sobrecarga se basear em dois ou mais tipos de matrizes, os tipos de elementos devem ser denominados tipos. 16
Atributos Atributos Os atributos devem ser do tipo System.Attribute, ou um tipo herdado dele. 41
Atributos Atributos O CLS só permite um subconjunto das codificações de atributos personalizados. Os únicos tipos que devem aparecer nessas codificações são (consulte Partição IV): System.Type, System.String, System.Char, System.Boolean, System.Byte, System.Int16, System.Int32, System.Int64, System.Single, e System.Doublequalquer tipo de enumeração baseado em um tipo inteiro de base compatível com CLS. 34
Atributos Atributos O CLS não permite modificadores necessários publicamente visíveis (modreq, consulte Partição II), mas permite modificadores opcionais (modopt, consulte Partição II) que ele não entende. 35
Construtores Construtores Um construtor de objeto deve chamar algum construtor de instância de sua classe base antes que ocorra qualquer acesso aos dados de instância herdados. (Isso não se aplica a tipos de valor, que não precisam ter construtores.) 21
Construtores Construtores Um construtor de objetos não deve ser chamado, exceto como parte da criação de um objeto, e um objeto não deve ser inicializado duas vezes. 22
Enumerações Enumerações O tipo subjacente de um enum deve ser um número inteiro CLS incorporado, o nome do campo deve ser "value__" e esse campo deve ser marcado RTSpecialName. 7
Enumerações Enumerações Existem dois tipos distintos de enums, indicados pela presença ou ausência do atributo personalizado (consulte Biblioteca de System.FlagsAttribute Partição IV). Um representa valores inteiros nomeados; o outro representa sinalizadores de bit nomeados que podem ser combinados para gerar um valor sem nome. O valor de an enum não está limitado aos valores especificados. 8
Enumerações Enumerações Os campos estáticos literais de um enum devem ter o tipo do próprio eno. 9
evento Eventos Os métodos que executam um evento devem ser marcados SpecialName nos metadados. 29
evento Eventos A acessibilidade de um evento e dos seus acedouros deve ser idêntica. 30
evento Eventos Os add métodos e remove métodos de um evento devem estar presentes ou ausentes. 31
evento Eventos remove Cada um dos add e métodos para um evento deve ter um parâmetro cujo tipo define o tipo do evento e que deve ser derivado de System.Delegate. 32
evento Eventos Os eventos devem seguir um padrão de nomenclatura específico. O atributo SpecialName referido na regra 29 do CLS deve ser ignorado nas comparações de nomes adequadas e deve respeitar as regras de identificação. 33
Exceções Exceções Os objetos lançados devem ser do tipo System.Exception ou de um tipo herdado dele. No entanto, os métodos compatíveis com CLS não são necessários para bloquear a propagação de outros tipos de exceções. 40
Geral Regras de conformidade CLS As regras CLS aplicam-se apenas às partes de um tipo que são acessíveis ou visíveis fora do assembly definidor. 1
Geral Regras de conformidade CLS Os membros de tipos não conformes com CLS não devem ser marcados como conformes com CLS. 2
Genéricos Tipos e membros genéricos Os tipos aninhados devem ter pelo menos tantos parâmetros genéricos como o tipo anexo. Os parâmetros genéricos em um tipo aninhado correspondem por posição aos parâmetros genéricos em seu tipo anexo. 42
Genéricos Tipos e membros genéricos O nome de um tipo genérico deve codificar o número de parâmetros de tipo declarados no tipo não aninhado, ou recentemente introduzidos no tipo se aninhado, de acordo com as regras definidas acima. 43
Genéricos Tipos e membros genéricos Um tipo genérico deve redeclarar restrições suficientes para garantir que quaisquer restrições relativas ao tipo de base ou às interfaces seriam satisfeitas pelas restrições genéricas de tipo. 44
Genéricos Tipos e membros genéricos Os tipos utilizados como restrições aos parâmetros genéricos devem, eles próprios, ser conformes com o CLS. 45
Genéricos Tipos e membros genéricos A visibilidade e a acessibilidade dos membros (incluindo tipos aninhados) em um tipo genérico instanciado devem ser consideradas como tendo como escopo a instanciação específica e não a declaração genérica de tipo como um todo. Partindo deste pressuposto, continuam a aplicar-se as regras de visibilidade e acessibilidade da regra 12 do CLS. 46
Genéricos Tipos e membros genéricos Para cada método abstrato ou genérico virtual, deve haver uma implementação concreta (não abstrata) por defeito 47
Interfaces Interfaces As interfaces compatíveis com CLS não devem exigir a definição de métodos não conformes com CLS para os implementar. 18
Interfaces Interfaces As interfaces compatíveis com CLS não devem definir métodos estáticos, nem campos. 19
Membros Digite membros em geral Os campos e métodos estáticos globais não são compatíveis com CLS. 36
Membros -- O valor de uma estática literal é especificado usando metadados de inicialização de campo. Um literal compatível com CLS deve ter um valor especificado em metadados de inicialização de campo que seja exatamente do mesmo tipo que o literal (ou do tipo subjacente, se esse literal for um enum). 13
Membros Digite membros em geral A restrição vararg não faz parte do CLS, e a única convenção de chamada suportada pelo CLS é a convenção de chamada gerenciada padrão. 15
Convenções de nomenclatura Convenções de nomenclatura As montagens devem seguir o Anexo 7 do Relatório Técnico 15 da Norma Unicode 3.0 que rege o conjunto de caracteres permitidos para iniciar e serem incluídos nos identificadores, disponíveis on-line em Formulários de Normalização Unicode. Os identificadores devem estar no formato canônico definido pelo Formulário de Normalização Unicode C. Para fins de CLS, dois identificadores são os mesmos se seus mapeamentos em minúsculas (conforme especificado pelos mapeamentos de minúsculas Unicode sem distinção de localidade, um-para-um) forem os mesmos. Ou seja, para que dois identificadores sejam considerados diferentes de acordo com o CLS, eles devem diferir em mais do que simplesmente o seu caso. No entanto, para substituir uma definição herdada, a CLI requer que a codificação precisa da declaração original seja usada. 4
Sobrecarga Convenções de nomenclatura Todas as denominações introduzidas num âmbito de aplicação conforme com o CLS devem ser distintas, independentes da espécie, exceto se forem idênticas e resolvidas por excesso de carga. Ou seja, enquanto o CTS permite que um único tipo use o mesmo nome para um método e um campo, o CLS não. 5
Sobrecarga Convenções de nomenclatura Os campos e os tipos aninhados devem ser distintos apenas pela comparação de identificadores, embora a ETC permita distinguir assinaturas distintas. Os métodos, propriedades e eventos que têm o mesmo nome (por comparação de identificador) devem diferir em mais do que apenas o tipo de retorno, exceto conforme especificado na Regra 39 do CLS 6
Sobrecarga Sobrecargas Apenas propriedades e métodos podem ser sobrecarregados. 37
Sobrecarga Sobrecargas Propriedades e métodos podem ser sobrecarregados com base apenas no número e tipos de seus parâmetros, exceto os operadores de conversão nomeados op_Implicit e op_Explicit, que também podem ser sobrecarregados com base em seu tipo de retorno. 38
Sobrecarga -- Se dois ou mais métodos compatíveis com CLS declarados em um tipo tiverem o mesmo nome e, para um conjunto específico de instanciações de tipo, tiverem o mesmo parâmetro e tipos de retorno, todos esses métodos devem ser semanticamente equivalentes nessas instanciações de tipo. 48
Propriedades Propriedades Os métodos que implementam os métodos getter e setter de uma propriedade devem ser marcados SpecialName nos metadados. 24
Propriedades Propriedades Os acessadores de uma propriedade devem ser todos estáticos, todos virtuais ou todos ser instância. 26
Propriedades Propriedades O tipo de propriedade deve ser o tipo de retorno do getter e o tipo do último argumento do setter. Os tipos de parâmetros da propriedade devem ser os tipos dos parâmetros para o getter e os tipos de todos, exceto o parâmetro final do setter. Todos estes tipos devem estar em conformidade com o CLS e não devem ser ponteiros geridos (ou seja, não devem ser passados por referência). 27
Propriedades Propriedades As propriedades devem obedecer a um padrão de nomenclatura específico. O SpecialName atributo referido na regra 24 do CLS deve ser ignorado nas comparações de nomes adequadas e deve respeitar as regras de identificação. Uma propriedade deve ter um método getter, um método setter ou ambos. 28
Conversão de tipo Conversão de tipo Se for prevista op_Implicit ou op_Explicit, deve ser previsto um meio alternativo de coerção. 39
Tipos Tipos e assinaturas de membros do tipo Os tipos de valores in a box não são compatíveis com CLS. 3
Tipos Tipos e assinaturas de membros do tipo Todos os tipos que aparecem numa assinatura devem estar em conformidade com o CLS. Todos os tipos que compõem um tipo genérico instanciado devem ser conformes com o CLS. 11
Tipos Tipos e assinaturas de membros do tipo As referências digitadas não são compatíveis com CLS. 14
Tipos Tipos e assinaturas de membros do tipo Os tipos de ponteiro não gerenciados não são compatíveis com CLS. 17
Tipos Tipos e assinaturas de membros do tipo Classes, tipos de valor e interfaces compatíveis com CLS não devem exigir a implementação de membros não compatíveis com CLS 20
Tipos Tipos e assinaturas de membros do tipo System.Object é compatível com CLS. Qualquer outra classe compatível com CLS deve herdar de uma classe compatível com CLS. 23

Índice das subsecções:

Tipos e assinaturas de membros do tipo

O tipo System.Object é compatível com CLS e é o tipo base de todos os tipos de objeto no sistema de tipo .NET. A herança no .NET é implícita (por exemplo, a classe String herda implicitamente da Object classe) ou explícita (por exemplo, a classe CultureNotFoundException herda explicitamente da classe ArgumentException, que herda explicitamente da classe Exception. Para que um tipo derivado seja compatível com CLS, seu tipo base também deve ser compatível com CLS.

O exemplo a seguir mostra um tipo derivado cujo tipo base não é compatível com CLS. Ele define uma classe base Counter que usa um inteiro de 32 bits não assinado como um contador. Como a classe fornece funcionalidade de contador encapsulando um inteiro não assinado, a classe é marcada como não compatível com CLS. Como resultado, uma classe derivada, NonZeroCounter, também não é compatível com CLS.

using System;

[assembly: CLSCompliant(true)]

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

   public Counter()
   {
      ctr = 0;
   }

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

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

   public UInt32 Value
   {
      get { return ctr; }
   }

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

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

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

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

    Public Sub New
        ctr = 0
    End Sub

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

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

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

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

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

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

Todos os tipos que aparecem nas assinaturas de membro, incluindo o tipo de retorno de um método ou um tipo de propriedade, devem ser compatíveis com CLS. Além disso, para os tipos genéricos:

  • Todos os tipos que compõem um tipo genérico instanciado devem ser compatíveis com CLS.

  • Todos os tipos usados como restrições em parâmetros genéricos devem ser compatíveis com CLS.

O sistema de tipo comum .NET inclui muitos tipos internos que são suportados diretamente pelo Common Language Runtime e são especialmente codificados nos metadados de um assembly. Desses tipos intrínsecos, os tipos listados na tabela a seguir são compatíveis com CLS.

Tipo compatível com CLS Description
Byte Inteiro não assinado de 8 bits
Int16 Inteiro assinado de 16 bits
Int32 Inteiro assinado de 32 bits
Int64 Inteiro assinado de 64 bits
Metade Valor de ponto flutuante de meia precisão
Solteiro Valor de ponto flutuante de precisão única
Duplo Valor de ponto flutuante de precisão dupla
Booleano Tipo de valor True ou False
Char Unidade de código codificada UTF-16
Decimal Número decimal de vírgula não flutuante
IntPtr Ponteiro ou identificador de um tamanho definido pela plataforma
Cadeia Coleção de zero, um ou mais objetos Char

Os tipos intrínsecos listados na tabela a seguir não são compatíveis com CLS.

Tipo não conforme Description Alternativa em conformidade com CLS
SByte Tipo de dados inteiro assinado de 8 bits Int16
UInt16 Inteiro não assinado de 16 bits Int32
UInt32 Inteiro não assinado de 32 bits Int64
UInt64 Inteiro não assinado de 64 bits Int64 (pode transbordar), BigInteger ou Double
UIntPtr Ponteiro ou identificador não assinado IntPtr

A biblioteca de classes .NET ou qualquer outra biblioteca de classes pode incluir outros tipos que não são compatíveis com CLS, por exemplo:

  • Tipos de valores in a box. O exemplo C# a seguir cria uma classe que tem uma propriedade pública do tipo int* chamada Value. Como an int* é um tipo de valor in a box, o compilador o sinaliza como não compatível com 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
    
  • Referências tipadas, que são construções especiais que contêm uma referência a um objeto e uma referência a um tipo. As referências digitadas são representadas no .NET pela TypedReference classe.

Se um tipo não for compatível com CLS, você deverá aplicar o CLSCompliantAttribute atributo com um isCompliant valor de false a ele. Para obter mais informações, consulte a seção do atributo CLSCompliantAttribute.

O exemplo a seguir ilustra o problema de conformidade CLS em uma assinatura de método e em instanciação de tipo genérico. Ele define uma InvoiceItem classe com uma propriedade de type UInt32, uma propriedade de type Nullable<UInt32>e um construtor com parâmetros de type UInt32 e Nullable<UInt32>. Você recebe quatro avisos do compilador quando tenta compilar este exemplo.

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

Para eliminar os avisos do compilador, substitua os tipos não compatíveis com CLS na InvoiceItem interface pública por tipos compatíveis:

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

Além dos tipos específicos listados, algumas categorias de tipos não são compatíveis com CLS. Isso inclui tipos de ponteiro não gerenciados e tipos de ponteiro de função. O exemplo a seguir gera um aviso do compilador porque usa um ponteiro para um inteiro para criar uma matriz de inteiros.

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

Para classes abstratas compatíveis com CLS (ou seja, classes marcadas como abstract em C# ou como MustInherit em Visual Basic), todos os membros da classe também devem ser compatíveis com CLS.

Convenções de nomenclatura

Como algumas linguagens de programação não diferenciam maiúsculas de minúsculas, os identificadores (como os nomes de namespaces, tipos e membros) devem diferir em mais do que maiúsculas e minúsculas. Dois identificadores são considerados equivalentes se seus mapeamentos minúsculos forem os mesmos. O exemplo C# a seguir define duas classes Person públicas e person. Como eles diferem apenas por maiúsculas e minúsculas, o compilador C# os sinaliza como não compatíveis com 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)

Os identificadores de linguagem de programação, como os nomes de namespaces, tipos e membros, devem estar em conformidade com o padrão Unicode. Isto significa que:

  • O primeiro caractere de um identificador pode ser qualquer letra Unicode maiúscula, letra minúscula, letra maiúscula do título, letra modificadora, outra letra ou número de letra. Para obter informações sobre categorias de caracteres Unicode, consulte a System.Globalization.UnicodeCategory enumeração.

  • Os caracteres subsequentes podem ser de qualquer uma das categorias como o primeiro caractere e também podem incluir marcas sem espaçamento, espaçamento combinando marcas, números decimais, pontuação do conector e códigos de formatação.

Antes de comparar identificadores, você deve filtrar códigos de formatação e converter os identificadores para Unicode Normalization Form C, porque um único caractere pode ser representado por várias unidades de código codificadas em UTF-16. As sequências de caracteres que produzem as mesmas unidades de código no Formulário C de Normalização Unicode não são compatíveis com CLS. O exemplo a seguir define uma propriedade chamada , que consiste no caractere ANGSTROM SIGN (U+212B), e uma segunda propriedade chamada Å, que consiste no caractere LATIN MAIÚSCULA LETRA A COM ANEL ACIMA (U+00C5). Os compiladores C# e Visual Basic sinalizam o código-fonte como não compatível com 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
'                       ~

Os nomes de membros dentro de um escopo específico (como os namespaces dentro de um assembly, os tipos dentro de um namespace ou os membros dentro de um tipo) devem ser exclusivos, exceto para nomes que são resolvidos por sobrecarga. Este requisito é mais rigoroso do que o do sistema de tipo comum, que permite que vários membros dentro de um âmbito tenham nomes idênticos, desde que sejam tipos diferentes de membros (por exemplo, um é um método e outro é um campo). Em especial, para os membros do tipo:

  • Os campos e os tipos aninhados são distinguidos apenas pelo nome.

  • Métodos, propriedades e eventos que têm o mesmo nome devem diferir por mais do que apenas o tipo de retorno.

O exemplo a seguir ilustra o requisito de que os nomes dos membros devem ser exclusivos dentro de seu escopo. Ele define uma classe nomeada Converter que inclui quatro membros chamados Conversion. Três são métodos e um é uma propriedade. O método que inclui um Int64 parâmetro é nomeado exclusivamente, mas os dois métodos com um Int32 parâmetro não são, porque o valor de retorno não é considerado uma parte da assinatura de um membro. A Conversion propriedade também viola esse requisito, porque as propriedades não podem ter o mesmo nome que métodos sobrecarregados.

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

Os idiomas individuais incluem palavras-chave exclusivas, portanto, os idiomas destinados ao Common Language Runtime também devem fornecer algum mecanismo para referenciar identificadores (como nomes de tipo) que coincidam com palavras-chave. Por exemplo, case é uma palavra-chave em C# e Visual Basic. No entanto, o exemplo do Visual Basic a seguir é capaz de desambiguar uma classe nomeada case da case palavra-chave usando chaves de abertura e fechamento. Caso contrário, o exemplo produziria a mensagem de erro "A palavra-chave não é válida como identificador" e falharia na compilação.

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

O exemplo C# a seguir é capaz de instanciar a case classe usando o @ símbolo para desambiguar o identificador da palavra-chave language. Sem ele, o compilador C# exibiria duas mensagens de erro, "Type expected" e "Invalid expression term 'case'".

using System;

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

Conversão de tipo

A Common Language Specification define dois operadores de conversão:

  • op_Implicit, que é usado para ampliar conversões que não resultam em perda de dados ou precisão. Por exemplo, a Decimal estrutura inclui um operador sobrecarregado op_Implicit para converter valores de tipos integrais e Char valores em Decimal valores.

  • op_Explicit, que é usado para estreitar conversões que podem resultar em perda de magnitude (um valor é convertido em um valor que tem um intervalo menor) ou precisão. Por exemplo, a Decimal estrutura inclui um operador sobrecarregado op_Explicit para converter Double e Single valores para Decimal e converter Decimal valores em valores integrais, Double, Singlee Char.

No entanto, nem todos os idiomas suportam a sobrecarga do operador ou a definição de operadores personalizados. Se você optar por implementar esses operadores de conversão, também deverá fornecer uma maneira alternativa de executar a conversão. Recomendamos que você forneça Fromos métodos Xxx e ToXxx .

O exemplo a seguir define conversões implícitas e explícitas compatíveis com CLS. Ele cria uma UDouble classe que representa um número de ponto flutuante não assinado, de precisão dupla. Prevê conversões implícitas de UDouble para Double e para conversões explícitas de UDouble para Single, Double para UDouble, e Single para UDouble. Também define um ToDouble método como uma alternativa ao operador de conversão implícito e ao ToSingle, FromDoublee FromSingle métodos como alternativas aos operadores de conversão explícitos.

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

Matrizes

Os arrays compatíveis com CLS estão em conformidade com as seguintes regras:

  • Todas as dimensões de uma matriz devem ter um limite inferior de zero. O exemplo a seguir cria uma matriz não compatível com CLS com um limite inferior de um. Apesar da presença do atributo, o compilador não deteta que a Numbers.GetTenPrimes matriz retornada CLSCompliantAttribute pelo método não é compatível com 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
    
  • Todos os elementos de matriz devem consistir em tipos compatíveis com CLS. O exemplo a seguir define dois métodos que retornam matrizes não compatíveis com CLS. O primeiro retorna uma matriz de UInt32 valores. O segundo retorna uma Object matriz que inclui Int32 e UInt32 valores. Embora o compilador identifique a primeira matriz como não compatível devido ao seu UInt32 tipo, ele não reconhece que a segunda matriz inclui elementos não compatíveis com 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()
    '                              ~~~~~~~~~~~~
    
  • A resolução de sobrecarga para métodos que têm parâmetros de matriz é baseada no fato de que eles são matrizes e em seu tipo de elemento. Por esse motivo, a seguinte definição de um método sobrecarregado GetSquares é compatível com 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
    

Interfaces

As interfaces compatíveis com CLS podem definir propriedades, eventos e métodos virtuais (métodos sem implementação). Uma interface compatível com CLS não pode ter qualquer um dos seguintes:

  • Métodos estáticos ou campos estáticos. Os compiladores C# e Visual Basic geram erros de compilador se você definir um membro estático em uma interface.

  • Campos. Os compiladores C# e Visual Basic geram erros de compilador se você definir um campo em uma interface.

  • Métodos que não são compatíveis com CLS. Por exemplo, a definição de interface a seguir inclui um método, INumber.GetUnsigned, que é marcado como não compatível com CLS. Este exemplo gera um aviso do compilador.

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

    Devido a essa regra, os tipos compatíveis com CLS não são necessários para implementar membros não compatíveis com CLS. Se uma estrutura compatível com CLS expõe uma classe que implementa uma interface não compatível com CLS, ela também deve fornecer implementações concretas de todos os membros não compatíveis com CLS.

Os compiladores de linguagem compatíveis com CLS também devem permitir que uma classe forneça implementações separadas de membros que têm o mesmo nome e assinatura em várias interfaces. Tanto o C# quanto o Visual Basic oferecem suporte a implementações de interface explícitas para fornecer implementações diferentes de métodos nomeados de forma idêntica. Visual Basic também suporta a Implements palavra-chave, que permite que você designe explicitamente qual interface e membro um membro específico implementa. O exemplo a seguir ilustra esse cenário definindo uma Temperature classe que implementa as ICelsius interfaces e IFahrenheit como implementações de interface explícitas.

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

Enumerações

As enumerações compatíveis com CLS devem seguir estas regras:

  • O tipo subjacente da enumeração deve ser um inteiro intrínseco compatível com CLS (Byte, Int16, Int32, ou Int64). Por exemplo, o código a seguir tenta definir uma enumeração cujo tipo subjacente é UInt32 e gera um aviso do compilador.

    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
    '                ~~~~
    
  • Um tipo de enumeração deve ter um único campo de instância chamado Value__ que está marcado com o FieldAttributes.RTSpecialName atributo. Isso permite que você faça referência ao valor do campo implicitamente.

  • Uma enumeração inclui campos estáticos literais cujos tipos correspondem ao tipo da própria enumeração. Por exemplo, se você definir uma State enumeração com valores de State.On e State.Off, State.On e State.Off ambos forem campos estáticos literais cujo tipo é State.

  • Existem dois tipos de enumerações:

    • Uma enumeração que representa um conjunto de valores inteiros nomeados mutuamente exclusivos. Esse tipo de enumeração é indicado pela ausência do System.FlagsAttribute atributo personalizado.

    • Uma enumeração que representa um conjunto de sinalizadores de bits que podem ser combinados para gerar um valor sem nome. Esse tipo de enumeração é indicado pela presença do System.FlagsAttribute atributo personalizado.

    Para obter mais informações, consulte a documentação da Enum estrutura.

  • O valor de uma enumeração não está limitado ao intervalo de seus valores especificados. Em outras palavras, o intervalo de valores em uma enumeração é o intervalo de seu valor subjacente. Você pode usar o Enum.IsDefined método para determinar se um valor especificado é um membro de uma enumeração.

Digite membros em geral

A Common Language Specification requer que todos os campos e métodos sejam acessados como membros de uma classe específica. Portanto, campos estáticos globais e métodos (ou seja, campos estáticos ou métodos que são definidos separadamente de um tipo) não são compatíveis com CLS. Se você tentar incluir um campo global ou método em seu código-fonte, os compiladores C# e Visual Basic geram um erro de compilador.

A Common Language Specification suporta apenas a convenção de chamada gerenciada padrão. Ele não suporta convenções de chamada não gerenciadas e métodos com listas de argumentos variáveis marcadas com a palavra-chave varargs . Para listas de argumentos variáveis que são compatíveis com a convenção de chamada gerenciada padrão, use o ParamArrayAttribute atributo ou a implementação da linguagem individual, como a params palavra-chave em C# e a ParamArray palavra-chave no Visual Basic.

Acessibilidade para membros

Substituir um membro herdado não pode alterar a acessibilidade desse membro. Por exemplo, um método público em uma classe base não pode ser substituído por um método privado em uma classe derivada. Há uma exceção: um protected internal membro (em C#) ou Protected Friend (em Visual Basic) em um assembly que é substituído por um tipo em um assembly diferente. Nesse caso, a acessibilidade da substituição é Protected.

O exemplo a seguir ilustra o erro gerado quando o atributo CLSCompliantAttribute é definido como true, e Human, que é uma classe derivada de Animal, tenta alterar a Species acessibilidade da propriedade de pública para privada. O exemplo é compilado com êxito se sua acessibilidade for alterada para público.

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

Os tipos na assinatura de um membro devem estar acessíveis sempre que esse membro estiver acessível. Por exemplo, isso significa que um membro público não pode incluir um parâmetro cujo tipo é privado, protegido ou interno. O exemplo a seguir ilustra o erro do compilador que resulta quando um StringWrapper construtor de classe expõe um valor de enumeração interno StringOperationType que determina como um valor de cadeia de caracteres deve ser encapsulado.

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

Tipos e membros genéricos

Os tipos aninhados sempre têm pelo menos tantos parâmetros genéricos quanto seu tipo de inclusão. Estes correspondem, por posição, aos parâmetros genéricos do tipo anexo. O tipo genérico também pode incluir novos parâmetros genéricos.

A relação entre os parâmetros de tipo genéricos de um tipo que contém e seus tipos aninhados pode ser ocultada pela sintaxe de linguagens individuais. No exemplo a seguir, um tipo Outer<T> genérico contém duas classes aninhadas e Inner1B<U>. Inner1A As chamadas para o ToString método, do qual cada classe herda , Object.ToString()mostram que cada classe aninhada inclui os parâmetros de tipo de sua classe que o contém.

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]

Os nomes de tipo genéricos são codificados no formato name'n, onde name é o nome do tipo, ' é um literal de caractere e n é o número de parâmetros declarados no tipo, ou, para tipos genéricos aninhados, o número de parâmetros de tipo recém-introduzidos. Essa codificação de nomes de tipo genéricos é de interesse principalmente para desenvolvedores que usam reflexão para acessar tipos genéricos de reclamação CLS em uma biblioteca.

Se as restrições forem aplicadas a um tipo genérico, todos os tipos usados como restrições também deverão ser compatíveis com CLS. O exemplo a seguir define uma classe nomeada BaseClass que não é compatível com CLS e uma classe genérica nomeada BaseCollection cujo parâmetro type deve derivar de BaseClass. Mas como BaseClass não é compatível com CLS, o compilador emite um aviso.

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

Se um tipo genérico é derivado de um tipo de base genérico, ele deve redeclarar quaisquer restrições para que possa garantir que as restrições no tipo base também sejam satisfeitas. O exemplo a seguir define um Number<T> que pode representar qualquer tipo numérico. Ele também define uma FloatingPoint<T> classe que representa um valor de ponto flutuante. No entanto, o código-fonte falha ao compilar, porque ele não aplica a restrição em Number<T> (que T deve ser um tipo de valor) para 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)
'                                                          ~

O exemplo compila com êxito se a restrição for adicionada à FloatingPoint<T> classe.

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

A Common Language Specification impõe um modelo conservador por instanciação para tipos aninhados e membros protegidos. Os tipos genéricos abertos não podem expor campos ou membros com assinaturas que contenham uma instanciação específica de um tipo genérico aninhado e protegido. Tipos não genéricos que estendem uma instanciação específica de uma classe base genérica ou interface não podem expor campos ou membros com assinaturas que contenham uma instanciação diferente de um tipo genérico aninhado e protegido.

O exemplo a seguir define um tipo C1<T> genérico (ou C1(Of T) no Visual Basic) e uma classe C1<T>.N protegida (ou C1(Of T).N no Visual Basic). C1<T> tem dois métodos, M1 e M2. No entanto, M1 não é compatível com CLS porque tenta retornar um C1<int>.N (ou C1(Of Integer).N) objeto de C1<T> (ou C1(Of T)). Uma segunda classe, C2, é derivada de C1<long> (ou C1(Of Long)). Tem dois métodos, M3 e M4. M3 não é compatível com CLS porque tenta retornar um C1<int>.N objeto (ou C1(Of Integer).N) de uma subclasse de C1<long>. Os compiladores de linguagem podem ser ainda mais restritivos. Neste exemplo, o Visual Basic exibe um erro quando tenta compilar M4o .

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

Construtores

Os construtores em classes e estruturas compatíveis com CLS devem seguir estas regras:

  • Um construtor de uma classe derivada deve chamar o construtor de instância de sua classe base antes de acessar dados de instância herdados. Esse requisito ocorre porque os construtores de classe base não são herdados por suas classes derivadas. Esta regra não se aplica a estruturas que não suportam herança direta.

    Normalmente, os compiladores impõem essa regra independentemente da conformidade CLS, como mostra o exemplo a seguir. Ele cria uma Doctor classe que é derivada de uma Person classe, mas a Doctor classe falha ao chamar o Person construtor de classe para inicializar campos de instância herdados.

    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()
    '                  ~~~
    
  • Um construtor de objeto não pode ser chamado, exceto para criar um objeto. Além disso, um objeto não pode ser inicializado duas vezes. Por exemplo, isso significa que Object.MemberwiseClone e métodos de desserialização não devem chamar construtores.

Propriedades

As propriedades em tipos compatíveis com CLS devem seguir estas regras:

  • Uma propriedade deve ter um setter, um getter, ou ambos. Em um assembly, eles são implementados como métodos especiais, o que significa que eles aparecerão como métodos separados (o getter é chamado get_propertyname e o setter éset_ propertyname) marcados como SpecialName nos metadados do assembly. Os compiladores C# e Visual Basic impõem essa regra automaticamente sem a necessidade de aplicar o CLSCompliantAttribute atributo.

  • O tipo de uma propriedade é o tipo de retorno do getter da propriedade e o último argumento do setter. Esses tipos devem ser compatíveis com CLS e os argumentos não podem ser atribuídos à propriedade por referência (ou seja, não podem ser ponteiros gerenciados).

  • Se uma propriedade tem um getter e um setter, ambos devem ser virtuais, ambos estáticos ou ambas as instâncias. Os compiladores C# e Visual Basic impõem automaticamente essa regra por meio de sua sintaxe de definição de propriedade.

Evento

Um evento é definido pelo seu nome e tipo. O tipo de evento é um delegado que é usado para indicar o evento. Por exemplo, o AppDomain.AssemblyResolve evento é do tipo ResolveEventHandler. Além do evento em si, três métodos com nomes baseados no nome do evento fornecem a implementação do evento e são marcados como SpecialName nos metadados do assembly:

  • Um método para adicionar um manipulador de eventos, chamado add_EventName. Por exemplo, o método de assinatura de evento para o AppDomain.AssemblyResolve evento é chamado add_AssemblyResolve.

  • Um método para remover um manipulador de eventos, chamado remove_EventName. Por exemplo, o método de remoção para o AppDomain.AssemblyResolve evento é chamado remove_AssemblyResolve.

  • Um método para indicar que o evento ocorreu, chamado raise_EventName.

Nota

A maioria das regras da Common Language Specification em relação a eventos são implementadas por compiladores de linguagem e são transparentes para os desenvolvedores de componentes.

Os métodos para adicionar, remover e elevar o evento devem ter a mesma acessibilidade. Todos eles também devem ser estáticos, instância ou virtuais. Os métodos para adicionar e remover um evento têm um parâmetro cujo tipo é o tipo de delegado de evento. Os métodos add e remove devem estar presentes ou ambos ausentes.

O exemplo a seguir define uma classe compatível com CLS chamada Temperature que gera um TemperatureChanged evento se a mudança de temperatura entre duas leituras for igual ou superior a um valor limite. A Temperature classe define explicitamente um raise_TemperatureChanged método para que ele possa executar seletivamente manipuladores de eventos.

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

Sobrecargas

A Common Language Specification impõe os seguintes requisitos aos membros sobrecarregados:

  • Os membros podem ser sobrecarregados com base no número de parâmetros e no tipo de qualquer parâmetro. Convenção de chamada, tipo de retorno, modificadores personalizados aplicados ao método ou seu parâmetro e se os parâmetros são passados por valor ou por referência não são considerados ao diferenciar entre sobrecargas. Para obter um exemplo, consulte o código para o requisito de que os nomes devem ser exclusivos dentro de um escopo na seção Convenções de nomenclatura.

  • Apenas propriedades e métodos podem ser sobrecarregados. Campos e eventos não podem ser sobrecarregados.

  • Os métodos genéricos podem ser sobrecarregados com base no número de seus parâmetros genéricos.

Nota

Os op_Explicit operadores e op_Implicit são exceções à regra de que o valor de retorno não é considerado parte de uma assinatura de método para resolução de sobrecarga. Esses dois operadores podem ser sobrecarregados com base em seus parâmetros e seu valor de retorno.

Exceções

Os objetos de exceção devem derivar de System.Exception ou de outro tipo derivado de System.Exception. O exemplo a seguir ilustra o erro do compilador que resulta quando uma classe personalizada nomeada ErrorClass é usada para tratamento de exceções.

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

Para corrigir esse erro, a ErrorClass classe deve herdar de System.Exception. Além disso, a Message propriedade deve ser substituída. O exemplo a seguir corrige esses erros para definir uma ErrorClass classe que é compatível com 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

Atributos

Em assemblies .NET, os atributos personalizados fornecem um mecanismo extensível para armazenar atributos personalizados e recuperar metadados sobre objetos de programação, como assemblies, tipos, membros e parâmetros de método. Os atributos personalizados devem derivar de System.Attribute ou de um tipo derivado de System.Attribute.

O exemplo a seguir viola essa regra. Ele define uma NumericAttribute classe que não deriva de System.Attribute. Um erro de compilador resulta somente quando o atributo não compatível com CLS é aplicado, não quando a classe é definida.

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

O construtor ou as propriedades de um atributo compatível com CLS podem expor apenas os seguintes tipos:

O exemplo a seguir define uma DescriptionAttribute classe que deriva de Attribute. O construtor de classe tem um parâmetro do tipo Descriptor, portanto, a classe não é compatível com CLS. O compilador C# emite um aviso, mas compila com êxito, enquanto o compilador do Visual Basic não emite um aviso ou um erro.

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

O atributo CLSCompliantAttribute

O atributo CLSCompliantAttribute é usado para indicar se um elemento de programa está em conformidade com a Common Language Specification. O CLSCompliantAttribute(Boolean) construtor inclui um único parâmetro necessário, isCompliant, que indica se o elemento do programa é compatível com CLS.

Em tempo de compilação, o compilador deteta elementos não compatíveis que se presume serem compatíveis com CLS e emite um aviso. O compilador não emite avisos para tipos ou membros que são explicitamente declarados não compatíveis.

Os desenvolvedores de componentes podem usar o atributo de CLSCompliantAttribute duas maneiras:

  • Para definir as partes da interface pública expostas por um componente que são compatíveis com CLS e as partes que não são compatíveis com CLS. Quando o atributo é usado para marcar elementos específicos do programa como compatíveis com CLS, seu uso garante que esses elementos sejam acessíveis a partir de todas as linguagens e ferramentas destinadas ao .NET.

  • Para garantir que a interface pública da biblioteca de componentes exponha apenas elementos de programa compatíveis com CLS. Se os elementos não forem compatíveis com CLS, os compiladores geralmente emitirão um aviso.

Aviso

Em alguns casos, os compiladores de linguagem impõem regras compatíveis com CLS, independentemente de o CLSCompliantAttribute atributo ser usado. Por exemplo, definir um membro estático em uma interface viola uma regra CLS. A esse respeito, se você definir um static membro (em C#) ou Shared (em Visual Basic) em uma interface, os compiladores C# e Visual Basic exibirão uma mensagem de erro e falharão ao compilar o aplicativo.

O CLSCompliantAttribute atributo é marcado com um AttributeUsageAttribute atributo que tem um valor de AttributeTargets.All. Esse valor permite que você aplique o atributo a qualquer elemento do CLSCompliantAttribute programa, incluindo assemblies, módulos, tipos (classes, estruturas, enumerações, interfaces e delegados), membros do tipo (construtores, métodos, propriedades, campos e eventos), parâmetros, parâmetros genéricos e valores de retorno. No entanto, na prática, você deve aplicar o atributo somente a assemblies, tipos e membros de tipo. Caso contrário, os compiladores ignoram o atributo e continuam a gerar avisos do compilador sempre que encontrarem um parâmetro não compatível, um parâmetro genérico ou um valor de retorno na interface pública da biblioteca.

O valor do atributo é herdado CLSCompliantAttribute por elementos de programa contidos. Por exemplo, se um assembly estiver marcado como compatível com CLS, seus tipos também serão compatíveis com CLS. Se um tipo estiver marcado como compatível com CLS, seus tipos e membros aninhados também serão compatíveis com CLS.

Você pode substituir explicitamente a conformidade herdada aplicando o CLSCompliantAttribute atributo a um elemento de programa contido. Por exemplo, você pode usar o CLSCompliantAttribute atributo com um isCompliant valor de para definir um tipo não false compatível em um assembly compatível, e você pode usar o atributo com um isCompliant valor de true para definir um tipo compatível em um assembly não compatível. Você também pode definir membros não compatíveis em um tipo compatível. No entanto, um tipo não compatível não pode ter membros compatíveis, portanto, você não pode usar o atributo com um isCompliant valor de para substituir a herança de true um tipo não compatível.

Ao desenvolver componentes, você sempre deve usar o CLSCompliantAttribute atributo para indicar se seu assembly, seus tipos e seus membros são compatíveis com CLS.

Para criar componentes compatíveis com CLS:

  1. Use o CLSCompliantAttribute para marcar seu assembly como compatível com CLS.

  2. Marque todos os tipos expostos publicamente no assembly que não são compatíveis com CLS como não compatíveis.

  3. Marque todos os membros expostos publicamente em tipos compatíveis com CLS como não compatíveis.

  4. Forneça uma alternativa compatível com CLS para membros não compatíveis com CLS.

Se você marcou com êxito todos os tipos e membros não compatíveis, seu compilador não deve emitir nenhum aviso de não conformidade. No entanto, você deve indicar quais membros não são compatíveis com CLS e listar suas alternativas compatíveis com CLS na documentação do produto.

O exemplo a seguir usa o CLSCompliantAttribute atributo para definir um assembly compatível com CLS e um tipo, CharacterUtilities, que tem dois membros não compatíveis com CLS. Como ambos os membros são marcados com o CLSCompliant(false) atributo, o compilador não produz avisos. A classe também fornece uma alternativa compatível com CLS para ambos os métodos. Normalmente, apenas adicionamos duas sobrecargas ao ToUTF16 método para fornecer alternativas compatíveis com CLS. No entanto, como os métodos não podem ser sobrecarregados com base no valor de retorno, os nomes dos métodos compatíveis com CLS são diferentes dos nomes dos métodos não compatíveis.

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

Se você estiver desenvolvendo um aplicativo em vez de uma biblioteca (ou seja, se não estiver expondo tipos ou membros que podem ser consumidos por outros desenvolvedores de aplicativos), a conformidade CLS dos elementos do programa que seu aplicativo consome será de interesse somente se seu idioma não oferecer suporte a eles. Nesse caso, o compilador de linguagem gerará um erro quando você tentar usar um elemento não compatível com CLS.

Interoperabilidade entre idiomas

A independência linguística tem alguns significados possíveis. Um significado envolve o consumo contínuo de tipos escritos em um idioma de um aplicativo escrito em outro idioma. Um segundo significado, que é o foco deste artigo, envolve a combinação de código escrito em vários idiomas em um único assembly .NET.

O exemplo a seguir ilustra a interoperabilidade entre idiomas criando uma biblioteca de classes chamada Utilities.dll que inclui duas classes NumericLib e StringLib. A NumericLib classe é escrita em C# e a StringLib classe é escrita em Visual Basic. Aqui está o código-fonte do StringUtil.vb, que inclui um único membro, ToTitleCase, em sua StringLib classe.

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

Aqui está o código-fonte para NumberUtil.cs, que define uma NumericLib classe que tem dois membros IsEven e 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;
   }
}

Para empacotar as duas classes em um único assembly, você deve compilá-las em módulos. Para compilar o arquivo de código-fonte do Visual Basic em um módulo, use este comando:

vbc /t:module StringUtil.vb

Para obter mais informações sobre a sintaxe de linha de comando do compilador do Visual Basic, consulte Criando a partir da linha de comando.

Para compilar o arquivo de código-fonte C# em um módulo, use este comando:

csc /t:module NumberUtil.cs

Em seguida, use as opções do Linker para compilar os dois módulos em um assembly:

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

O exemplo a seguir chama os NumericLib.NearZero métodos e StringLib.ToTitleCase . O código do Visual Basic e o código C# são capazes de acessar os métodos em ambas as classes.

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

Para compilar o código do Visual Basic, use este comando:

vbc example.vb /r:UtilityLib.dll

Para compilar com C#, altere o nome do compilador de vbc para csce altere a extensão de arquivo de .vb para .cs:

csc example.cs /r:UtilityLib.dll