Partilhar via


Tipos de referência internos (referência C#)

O C# tem muitos tipos de referência internos. Estes tipos incluem palavras-chave ou operadores que são sinónimos de um tipo na biblioteca .NET.

A referência da linguagem C# documenta a versão mais recentemente lançada da linguagem C#. Contém também documentação inicial para funcionalidades em pré-visualizações públicas para o próximo lançamento linguístico.

A documentação identifica qualquer funcionalidade introduzida pela primeira vez nas últimas três versões da língua ou em pré-visualizações públicas atuais.

Sugestão

Para saber quando uma funcionalidade foi introduzida pela primeira vez em C#, consulte o artigo sobre o histórico de versões da linguagem C#.

O tipo de objeto

O object tipo é um alias para System.Object no .NET. No sistema de tipos unificado de C#, todos os tipos, predefinidos e definidos pelo usuário, tipos de referência e tipos de valor, herdam direta ou indiretamente do System.Object. Atribuir valores de qualquer tipo (exceto ref struct, ver ref struct) a variáveis do tipo object. Pode atribuir o literal null a qualquer object variável como valor predefinido. Quando converte uma variável de tipo de valor para object, o valor é encaixado. Quando convertes uma variável do tipo object num tipo de valor, o valor é desencaixado. Para obter mais informações, consulte Boxe e Unboxing.

O tipo de cadeia de caracteres

O string tipo representa uma sequência de zero ou mais caracteres Unicode. Em .NET, string é um alias para System.String.

Embora string seja um tipo de referência, os operadores == de igualdade e != comparam os valores dos string objetos, não referências. A igualdade baseada em valor torna o teste da igualdade de cadeias mais intuitivo. Por exemplo:

string a = "hello";
string b = "h";
// Append to contents of 'b'.
b += "ello";
Console.WriteLine(a == b);
Console.WriteLine(object.ReferenceEquals(a, b));

O exemplo anterior mostra "Verdadeiro" e depois "Falso" porque o conteúdo das cadeias é equivalente, mas a e b não se referem à mesma instância de cadeia.

O operador + concatena cadeias de caracteres:

string a = "good " + "morning";

O código anterior cria um objeto string que contém "bom dia".

As cadeias são imutáveis – não podes alterar o conteúdo de um objeto string depois de o criares. Por exemplo, ao escrever este código, o compilador cria na verdade um novo objeto string para conter a nova sequência de caracteres, e atribui esse novo objeto a b. A memória alocada para b (quando continha a cadeia de caracteres "h") é então elegível para coleta de lixo.

string b = "h";
b += "ello";

Pode usar o []operador para acesso apenas de leitura a caracteres individuais de uma cadeia. Os valores de índice válidos começam em 0 e devem ser menores que o comprimento da cadeia de caracteres:

string str = "test";
char x = str[2];  // x = 's';

De forma semelhante, pode usar o [] operador para iterar sobre cada carácter numa cadeia:

string str = "test";

for (int i = 0; i < str.Length; i++)
{
  Console.Write(str[i] + " ");
}
// Output: t e s t

Literais de cadeias

Os literais string são do tipo string e apresentam três formas: cru, citado e literal.

Os literais de strings brutos contêm texto arbitrário sem necessidade de sequências de escape. Os literais de cadeia de caracteres brutos podem incluir espaços em branco e novas linhas, aspas incorporadas e outros caracteres especiais. Os literais de cadeia de caracteres brutos são colocados em um mínimo de três aspas duplas ("""):

"""
This is a multi-line
    string literal with the second line indented.
"""

Você pode até incluir uma sequência de três (ou mais) caracteres de aspas duplas. Se o texto exigir uma sequência incorporada de aspas, inicie e termine a cadeia de caracteres bruta literal com mais aspas, conforme necessário:

"""""
This raw string literal has four """", count them: """" four!
embedded quote characters in a sequence. That's why it starts and ends
with five double quotes.

You could extend this example with as many embedded quotes as needed for your text.
"""""

Os literais de cadeia de caracteres brutos normalmente têm as sequências de aspas inicial e final em linhas separadas do texto incorporado. Os literais de cadeia de caracteres brutos de várias linhas suportam cadeias de caracteres que são elas próprias cadeias de caracteres entre aspas:

var message = """
"This is a very important message."
""";
Console.WriteLine(message);
// output: "This is a very important message."

Quando as aspas inicial e final estão em linhas separadas, as novas linhas após a cotação de abertura e precedendo a cotação final não são incluídas no conteúdo final. A sequência de aspas de fechamento dita a coluna mais à esquerda para o literal da cadeia de caracteres. Você pode recuar um literal de cadeia de caracteres bruto para corresponder ao formato de código geral:

var message = """
    "This is a very important message."
    """;
Console.WriteLine(message);
// output: "This is a very important message."
// The leftmost whitespace is not part of the raw string literal

As colunas à direita da sequência de aspas finais são preservadas. Esse comportamento habilita cadeias de caracteres brutas para formatos de dados como JSON, YAML ou XML, conforme mostrado no exemplo a seguir:

var json= """
    {
        "prop": 0
    }
    """;

Sugestão

O Visual Studio e o C# Dev Kit fornecem alguma validação e realce de sintaxe quando literais de cadeia de caracteres bruta contêm dados JSON ou expressões regulares.

As ferramentas analisam o texto. Se as ferramentas tiverem confiança de que o texto representa JSON ou uma expressão regular, o editor fornecerá coloração de sintaxe.

Você pode melhorar essa experiência adicionando um comentário acima da declaração indicando o formato:

  • // lang=json indica que o literal da cadeia de caracteres bruta representa dados JSON.
  • // lang=regex Indica que o literal de cadeia de caracteres bruto representa uma expressão regular.

Quando um literal de cadeia de caracteres bruto é usado como um argumento em que o parâmetro usa o System.Diagnostics.CodeAnalysis.StringSyntaxAttribute para indicar um formato, essas ferramentas validam o literal de cadeia de caracteres bruto para alguns dos tipos de formato. JSON e regex são suportados.

Para alguns formatos, o comentário ou o atributo habilita sugestões de código oferecem correções para literais de cadeia de caracteres com base no formato.

O compilador devolve um erro se alguma das linhas de texto se estender para a esquerda da sequência de aspas de encerramento. As sequências de aspas de abertura e fechamento podem estar na mesma linha, desde que o literal da cadeia de caracteres não comece nem termine com um caractere de aspas:

var shortText = """He said "hello!" this morning.""";

Você pode combinar literais de cadeia de caracteres brutos com interpolação de cadeia de caracteres para incluir caracteres e chaves de aspas na cadeia de caracteres de saída.

Os literais de cadeia de caracteres entre aspas são colocados entre aspas duplas ("):

"good morning"  // a string literal

Os literais de cadeia de caracteres podem conter qualquer literal de caracteres. Sequências de fuga estão incluídas. O exemplo a seguir usa a sequência \\ de escape para barra invertida, \u0066 para a letra f e \n para newline.

string a = "\\\u0066\n F";
Console.WriteLine(a);
// Output:
// \f
//  F

Nota

O código \udddd de escape (onde dddd é um número de quatro dígitos) representa o caractere Unicode U+dddd. Códigos de escape Unicode de oito dígitos também são reconhecidos: \Udddddddd.

Os literais de cadeia de caracteres verbatim começam com @ e também são colocados entre aspas duplas. Por exemplo:

@"good morning"  // a string literal

A vantagem das strings verbatim é que as sequências de escape não são processadas, o que as torna fáceis de escrever. Por exemplo, o texto a seguir corresponde a um nome de arquivo do Windows totalmente qualificado:

@"c:\Docs\Source\a.txt"  // rather than "c:\\Docs\\Source\\a.txt"

Para incluir aspas duplas em uma cadeia de caracteres @-quotas, duplique-a:

@"""Ahoy!"" cried the captain." // "Ahoy!" cried the captain.

Literais de cadeia UTF-8

.NET armazena strings usando codificação UTF-16. UTF-8 é o padrão para protocolos Web e outras bibliotecas importantes. Podes adicionar o sufixo u8 a um literal string para especificar a codificação UTF-8. O compilador armazena literais UTF-8 como ReadOnlySpan<byte> objetos. Usar um literal de cadeia de caracteres UTF-8 cria uma declaração mais clara do que declarar o equivalente System.ReadOnlySpan<T>, conforme mostrado no código a seguir:

ReadOnlySpan<byte> AuthWithTrailingSpace = new byte[] { 0x41, 0x55, 0x54, 0x48, 0x20 };
ReadOnlySpan<byte> AuthStringLiteral = "AUTH "u8;

Para armazenar um literal de string UTF-8 como um array, use ReadOnlySpan<T>.ToArray() para copiar os bytes que contêm o literal para o array mutável:

byte[] AuthStringLiteral = "AUTH "u8.ToArray();

Os literais de cadeia UTF-8 não são constantes em tempo de compilação; São constantes de execução. Portanto, eles não podem ser usados como o valor padrão para um parâmetro opcional. Não podes combinar literais de cadeias UTF-8 com interpolação de cadeias. Não é possível usar o $ token e o sufixo u8 na mesma expressão de cadeia de caracteres.

O tipo de delegado

A declaração de um tipo de delegado é semelhante a uma assinatura de método. Tem um valor de retorno e qualquer número de parâmetros de qualquer tipo:

public delegate void MessageDelegate(string message);
public delegate int AnotherDelegate(MyType m, long num);

No .NET, System.Action e System.Func tipos fornecem definições genéricas para muitos delegados comuns. Você provavelmente não precisa definir novos tipos de delegados personalizados. Em vez disso, você pode criar instanciações dos tipos genéricos fornecidos.

A delegate é um tipo de referência incorporado que pode usar para encapsular um método nomeado ou anónimo. Os delegados são semelhantes aos ponteiros de função em C++; no entanto, os delegados são seguros para tipos e protegidos. Para aplicativos de delegados, consulte Delegados e delegados genéricos. Os delegados são a base para os Eventos. Instancias um delegado associando-o a um método nomeado ou anónimo.

Deve instanciar o delegado com um método ou expressão lambda que tenha um tipo de retorno e parâmetros de entrada compatíveis. Para obter mais informações sobre o grau de variância permitido na assinatura do método, consulte Variância em delegados. Para uso com métodos anónimos, declare o delegado e o código como associados a ele em conjunto.

A combinação ou remoção de delegados falha com uma exceção de tempo de execução quando os tipos de delegados envolvidos em tempo de execução são diferentes devido à conversão de variantes. O exemplo a seguir demonstra uma situação que falha:

Action<string> stringAction = str => {};
Action<object> objectAction = obj => {};

// Valid due to implicit reference conversion of
// objectAction to Action<string>, but might fail
// at run time.
Action<string> combination = stringAction + objectAction;

Você pode criar um delegado com o tipo de tempo de execução correto criando um novo objeto delegado. O exemplo a seguir demonstra como essa solução alternativa pode ser aplicada ao exemplo anterior.

Action<string> stringAction = str => {};
Action<object> objectAction = obj => {};

// Creates a new delegate instance with a runtime type of Action<string>.
Action<string> wrappedObjectAction = new Action<string>(objectAction);

// The two Action<string> delegate instances can now be combined.
Action<string> combination = stringAction + wrappedObjectAction;

Você pode declarar ponteiros de função, que usam sintaxe semelhante. Um ponteiro de função usa a calli instrução em vez de instanciar um tipo de delegado e chamar o método virtual Invoke .

O tipo dinâmico

O dynamic tipo indica que a variável e as referências aos seus membros contornam a verificação de tipos em tempo de compilação. Em vez disso, essas operações são resolvidas em tempo de execução. O dynamic tipo simplifica o acesso a APIs COM, como as APIs de automação do Office, a APIs dinâmicas, como bibliotecas IronPython, e ao DOM (Document Object Model) HTML.

O tipo dynamic se comporta como o tipo object na maioria das circunstâncias. Em particular, qualquer expressão não nula pode ser convertida para o dynamic tipo. O dynamic tipo difere de object que o compilador não resolve nem faz operações de verificação de tipos que contenham expressões do tipo dynamic. O compilador empacota informações sobre a operação, e essas informações são usadas posteriormente para avaliar a operação em tempo de execução. Como parte do processo, as variáveis do tipo dynamic são compiladas em variáveis do tipo object. Portanto, o tipo dynamic existe apenas em tempo de compilação, não em tempo de execução.

O exemplo seguinte contrasta uma variável do tipo dynamic com uma variável do tipo object. Para verificar o tipo de cada variável em tempo de compilação, coloque o dyn ponteiro do mouse sobre obj ou WriteLine nas instruções. Copie o código a seguir para um editor onde o IntelliSense está disponível. O IntelliSense mostra dinâmica para dyn e objeto para obj.

class Program
{
    static void Main(string[] args)
    {
        dynamic dyn = 1;
        object obj = 1;

        // Rest the mouse pointer over dyn and obj to see their
        // types at compile time.
        System.Console.WriteLine(dyn.GetType());
        System.Console.WriteLine(obj.GetType());
    }
}

As WriteLine instruções exibem os tipos de tempo de execução de dyn e obj. Nesse ponto, ambos têm o mesmo tipo, inteiro. A seguinte saída é produzida:

System.Int32
System.Int32

Para ver a diferença entre dyn e obj em tempo de compilação, adicione as duas linhas a seguir entre as declarações e as WriteLine instruções no exemplo anterior.

dyn = dyn + 3;
obj = obj + 3;

Um erro de compilador é relatado para a tentativa de adição de um inteiro e um objeto na expressão obj + 3. No entanto, nenhum erro é relatado para dyn + 3. A expressão que contém dyn não é verificada em tempo de compilação porque o tipo de dyn é dynamic.

O exemplo a seguir usa dynamic em várias declarações. O Main método também contrasta a verificação de tipo em tempo de compilação com a verificação de tipo em tempo de execução.

using System;

namespace DynamicExamples
{
    class Program
    {
        static void Main(string[] args)
        {
            ExampleClass ec = new ExampleClass();
            Console.WriteLine(ec.ExampleMethod(10));
            Console.WriteLine(ec.ExampleMethod("value"));

            // The following line causes a compiler error because ExampleMethod
            // takes only one argument.
            //Console.WriteLine(ec.ExampleMethod(10, 4));

            dynamic dynamic_ec = new ExampleClass();
            Console.WriteLine(dynamic_ec.ExampleMethod(10));

            // Because dynamic_ec is dynamic, the following call to ExampleMethod
            // with two arguments does not produce an error at compile time.
            // However, it does cause a run-time error.
            //Console.WriteLine(dynamic_ec.ExampleMethod(10, 4));
        }
    }

    class ExampleClass
    {
        static dynamic _field;
        dynamic Prop { get; set; }

        public dynamic ExampleMethod(dynamic d)
        {
            dynamic local = "Local variable";
            int two = 2;

            if (d is int)
            {
                return local;
            }
            else
            {
                return two;
            }
        }
    }
}
// Results:
// Local variable
// 2
// Local variable

Especificação da linguagem C#

Para obter mais informações, consulte as seguintes seções da especificação da linguagem C#:

Consulte também