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

O C# tem muitos tipos de referência internos. Eles têm palavras-chave ou operadores que são sinônimos para um tipo na biblioteca .NET.

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. Você pode atribuir valores de qualquer tipo a variáveis do tipo object. Qualquer object variável pode ser atribuída ao seu valor padrão usando o literal null. Quando uma variável de um tipo de valor é convertida em objeto, diz-se que ela está encaixotada. Quando uma variável de tipo object é convertida em um tipo de valor, diz-se que ela é desencaixotada. 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. string é um alias para System.String no .NET.

Embora string seja um tipo de referência, os operadores == de igualdade e != são definidos para comparar os valores de string objetos, não referências. A igualdade baseada em valor torna o teste de igualdade de cadeia de caracteres 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 exibe "True" e, em seguida, "False" porque o conteúdo das cadeias de caracteres é equivalente, mas a não b se refere à mesma ocorrência de cadeia de caracteres.

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 de caracteres são imutáveis - o conteúdo de um objeto de cadeia de caracteres não pode ser alterado depois que o objeto é criado. Por exemplo, quando você escreve esse código, o compilador realmente cria um novo objeto string para conter a nova sequência de caracteres, e esse novo objeto é atribuído a b. A memória que foi alocada para b (quando continha a cadeia de caracteres "h") é então elegível para coleta de lixo.

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

O []operador pode ser usado para acesso somente leitura a caracteres individuais de uma cadeia de caracteres. 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';

Da mesma forma, o [] operador também pode ser usado para iterar sobre cada caractere em uma cadeia de caracteres:

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 de cadeia de caracteres são do tipo string e podem ser escritos em três formas, crua, citada e textual.

Literais de cadeia de caracteres brutos estão disponíveis a partir de C# 11. Literais de cadeia de caracteres brutos podem conter texto arbitrário sem exigir 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
    }
    """;

O compilador emite um erro se qualquer uma das linhas de texto se estender para a esquerda da sequência de cotação de fechamento. 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 cadeias de caracteres literais é que as sequências de escape não são processadas, o que facilita a escrita. 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

Strings no .NET são armazenadas usando codificação UTF-16. UTF-8 é o padrão para protocolos Web e outras bibliotecas importantes. A partir de C# 11, você pode adicionar o sufixo u8 a um literal de cadeia de caracteres para especificar a codificação UTF-8. Os literais UTF-8 são armazenados como ReadOnlySpan<byte> objetos. O tipo natural de um literal de cadeia UTF-8 é ReadOnlySpan<byte>. 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 cadeia de caracteres UTF-8 como uma matriz requer o uso de ReadOnlySpan<T>.ToArray() para copiar os bytes que contêm o literal para a matriz mutável:

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

Literais de cadeia UTF-8 não são constantes de tempo de compilação; são constantes de tempo de execução. Portanto, eles não podem ser usados como o valor padrão para um parâmetro opcional. Literais de cadeia UTF-8 não podem ser combinados com interpolação de cadeia de caracteres. 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 que pode ser usado 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. Um delegado pode ser instanciado associando-o a um método nomeado ou anônimo.

O delegado deve ser instanciado com um método ou expressão lambda que tenha um tipo de retorno compatível e parâmetros de entrada. 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, o delegado e o código a ser associado a ele são declarados juntos.

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 may 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 o uso da variável e as referências a seus membros ignoram a verificação de tipo 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 as operações que contêm expressões de tipo dynamic não são resolvidas ou tipo verificado pelo compilador. 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 a seguir contrasta uma variável de tipo dynamic com uma variável de tipo object. Para verificar o tipo de cada variável em tempo de compilação, coloque o WriteLine ponteiro do mouse sobre dyn ou obj 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