Arrays

Um array reúne sob o mesmo identificador muitas variáveis do mesmo tipo, acessadas por um índice.

Todos os arrays em C# são criados dinamicamente, ou seja, em tempo de execução. O tamanho é sempre definido em tempo de execução.

A declaração de um array sempre tem “[]” depois do tipo da variável. Devemos sempre chamar new para inicializar a array. Note que o primeiro índice do array é sempre zero. Veja no exemplo abaixo um array de uma dimensão:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System;
 
public class Class1
{
      const int Max = 5;
 
      public static void Main()
      {
            double[] Notas = new double[Max];
            Notas[0] = 6;
            Notas[1] = 9.5;
            Notas[2] = 4.2;
            Notas[3] = 7.2;
            Notas[4] = 5.4;
            double Soma = 0;
            foreach (double Nota in Notas)
            {
                  Soma = Soma + Nota;
            }
            Console.WriteLine(Soma / Max);
      }
}

Um array de várias dimensões pode ser declarado colocando vírgulas dentro dos colchetes. Você pode definir um array “dentado”, um array de arrays. A diferença no array “dentado” é que cada linha pode ter um tamanho diferente. Veja alguns exemplos:

1
2
3
4
5
6
7
...
int[] al;   // array de inteiro de uma dimensão
int[,] a2;  // array de inteiro de duas dimensões
int[,,] a3; // array de inteiro de três dimensões
int[][] j2; // array "dentado" (array de array)
int[][][] j3;     // array de array de array
...

Todo array deve ser inicializado. Você pode simplesmente especificar o tamanho ou ainda atribuir valores:

1
2
3
4
5
6
7
8
9
10
11
...
int[] al = new int[] {1, 2, 3};
int[,] a2 = new int[,] {{1, 2, 3}, {4, 5, 6}};
int[,,] a3 = new int[10, 20, 30];
int[] [] j2 = new int[3] [];
j2[0] = new int[] {1, 2, 3};
j2[1] = new int[] {1, 2, 3, 4, 5, 6};
j2[2] = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9};
int[] al = new int[] {1, 2, 3};
int[] al = {1, 2, 3};
...

A faixa dos arrays é verificada em tempo de execução. Se você indexar o array com um valor fora de faixa, uma exception é gerada.

Interface

Tipo definido pelo usuário. Uma interface é uma espécie de classe, mas contém apenas os “protótipos” dos métodos, sem a sua implementação. Corresponde no C++ a uma classe abstrata com todos os métodos “virtuais puros”. Uma classe, além de ser derivada de outra, pode implementar várias interfaces.

Abaixo pode-se ver um exemplo de declaração de interface e sua aplicação em uma classe:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
interface IMinhaInterface
{
    void MeuMetodo();
}
 
class MinhaClasse : IMinhaInterface
{
    // Implementação explícita do membro da interface herdada: 
    void IMinhaInterface.Metodo()
    {
        // Corpo do método.
    }
 
    static void Main()
    {
        // Declara uma instância da classe baseada na interface.
        IMinhaInterface obj = new MinhaClasse();
 
        // Invoca o método.
        obj.MeuMetodo();
    }
}

Veja maiores detalhes no capítulo sobre Orientação a Objetos.

Class (por referência)

Permite declarar classes, de maneira semelhante ao C++. As principais diferenças são:

  • Não é preciso seguir uma ordem de declaração, podendo declarar variáveis por exemplo, em qualquer parte do corpo da classe
  • Não é permitida a herança múltipla de classes

Um exemplo simples de uma classe em C# pode ser visto abaixo:

1
2
3
4
class MinhaClasse
{
    // Corpo da classe (Métodos, Variáveis, Propriedades, sub classes)
}

Veja maiores detalhes no capítulo sobre Orientação a Objetos que será abordado posteriormente neste curso.

Valor null (por referência)

Todos os tipos por referências podem ter o valor null atribuídos a eles. É um erro usar uma variável que contenha null.

A palavra-chave null é uma literal que representa uma referência nula, que não faz referência a qualquer objeto. null é o valor padrão das variáveis do tipo de referência. Os tipos de valor comum ou primitivos não podem ser nulos. No entanto, o C# 2.0 introduziu tipos de valor anulável.

O exemplo a seguir demonstra alguns comportamentos do uso da palavra-chave null:

class Program
{
    class MyClass
    {
        public void MyMethod() { }
    }
 
    static void Main(string[] args)
    {
        // Variável declarada possui o valor null.
        // Porém, o compilador considera como "unassigned".
        // ou sem valor atribuido. Um erro será gerado
        // ao tentar usar a variável neste momento
        MyClass mc;
 
        // Agora a variável pode ser utilizada, mas...
        mc = null;
 
        // A chamada de um método de um objeto nulo
        // gera um erro de execução - NullReferenceException.
        // Decomente esta linha para ver o resultado.
        // mc.MyMethod();
 
        // Agora, a objeto mc possui um valor, ou referência.
        mc = new MyClass();
 
        // E seu método pode ser chamado.
        mc.MyMethod();
 
        // Definir o objeto para null novamente fará com que seu conteúdo ou
        // referência seja perdido ou descartado, podendo porém receber novos valores.
        mc = null;
 
        // Lembrando que uma string vazia é diferente de uma string nula.
        string s = null;
        string t = String.Empty; // O mesmo que ""
 
        // O método Equals aplicado em objetos nulos sempre retornará false.
        bool b = (t.Equals(s));
        Console.WriteLine(b);
 
        // Operador de igualdade também retorna false ao comparar com um
        // objeto é nulo
        Console.WriteLine("Empty string {0} null string", s == t ? "igual": "diferente");
 
        // Retorna true.
        Console.WriteLine("null == null is {0}", null == null);
 
        // Tipos por valor ou primitivos não podem receber o valor null
       // int i = null; // Erro de compilação!
 
        // O C# 2.0 introduziu os tipos anuláveis, promovendo maior flexibilidade
        int? i = null;
 
        // Mantém a janela do console aberta.
        System.Console.WriteLine("Pressiona qualquer tecla para sair.");
        System.Console.ReadKey();
 
    }
}

Object (por referência)

O tipo de object é um alias para Object no .NET Framework. No sistema de tipo unificado do C#, Todos os tipos, pré-definidos e definidos pelo usuário, tipos de referência e tipos de valor, herdam direta ou indiretamente da Object. Você pode atribuir valores de qualquer tipo a variáveis de tipo object. Quando uma variável de um tipo valor é convertido em objeto, é um processo chamado de Boxed. Quando uma variável do tipo object é convertida em um tipo de valor, é um processo chamado unboxed. Ambos serão explicados adiante.

O exemplo a seguir mostra como variáveis do tipo object podem aceitar valores de qualquer tipo de dados e como variáveis do tipo object podem usar métodos em Object do .NET Framework.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class ObjectTest
{
   public int i = 10;
}
 
class MainClass2
{
   static void Main()
   {
      object a;
      a = 1;   // exemplo de boxing
      Console.WriteLine(a);
      Console.WriteLine(a.GetType());
      Console.WriteLine(a.ToString());
 
      a = new ObjectTest();
      ObjectTest classRef;
      classRef = (ObjectTest)a;
      Console.WriteLine(classRef.i);
   }
}
/* saída no console
    1
    System.Int32
    1
 * 10
*/

String (por referência)

Tipo string

As strings são tecnicamente um tipo por referência, mas possuem algumas características especiais:

  • Não precisam ser inicializadas com o operador new.
  • A atribuição de uma variável a outra funciona como se copiasse o conteúdo, criando efetivamente outra cópia da variável.
  • Uma string contendo o valor null é uma string vazia; não é um erro usá-la.
  • Você não pode criar uma classe derivada de string.

As strings contém caracteres Unicode e podem ter até 1G de comprimento. Veja um exemplo:

1
2
3
4
5
6
7
8
9
10
...
// Declara e inicializa uma string
string Nome = “Maria”
// Copia para outra string. Se alterarmos uma delas, a outra manterá o seu valor
string NovoNome = Nome;
// Atribui à string antiga
Nome = “John”;
// Exibe “John – Mary”
System.Console.WriteLine(Nome +-+ NovoNome);
...

Existem diversas operações definidas para strings. As principais são:

  • [] para ler um caractere específico;
  • == para comparar valores;
  • + para concatenar;
Tipo lmplementação
string String Unicode com até 1 giga caracteres.

As strings são consideradas imutáveis e não podem ser alteradas depois de criadas. Quando você efetua uma operação qualquer, como por exemplo, concatenar um caractere, você na verdade está criando outra string e descartando a anterior. Caso você queira alterar uma string, use “StringBuilder”. Em algumas situações, a performance com “StringBuilder” pode ser bem melhor.

Tipos no C#

O C# tem duas grandes categorias de tipos: por valor e por referência. Os tipos por valor são gerenciados diretamente. Os tipos por referência são sempre acessados através de ponteiros.

Tipos por valor

  • Os tipos por valor têm as seguintes características principais:
  • São alocados diretamente na pilha.
  • Não precisam ser inicializados com o operador new.
  • A variável armazena o valor diretamente.
  • A atribuição de uma variável a outra copia o conteúdo, criando efetivamente outra cópia da variável.
  • Normalmente são usados com tipos de pequeno tamanho (menos que 16 bytes), onde o uso de referências traria um custo muito grande.
  • Podem ser automaticamente convertidos para referências em um processo chamado “boxing”, descrito na segunda parte deste artigo.
  • Não podem conter o valor null.

Tipos por referência

Os tipos por referência têm as seguintes características principais:

  • São alocados em um heap e sujeitos à “coleta de lixo” (“garbage collection”) quando não forem mais usados.
  • Devem ser inicializados com o operador new.
  • A variável armazena uma “referência”, uma espécie de ponteiro; o conteúdo em si fica no heap.
  • A atribuição de uma variável à outra copia a referência; podemos ter muitas variáveis referindo-se ao mesmo valor.
  • Normalmente usados com tipos de grande tamanho (mais que 16 bytes), onde o custo da alocação dinâmica é relativamente pequeno frente a sua flexibilidade.
  • Podem conter o valor null, embora se usarmos uma variável com o valor null a exception NullReferenceException será gerada.

Tipos ponteiro

  • São ponteiros que poderão ser utilizados em código “inseguro”(unsafe) para acessar funções de código externo (dll win32).

Este último são herança da flexibilidade que este tipo de dados dá ao C e C++. Será tratado com mais detalhes no decorrer do curso.