Apostila Entrando no tom de C# - UEMA

47

description

Apostila feita por alunos do Curso de Engenharia da Computação sobre essa Linguagem de programação

Transcript of Apostila Entrando no tom de C# - UEMA

Page 1: Apostila Entrando no tom de C# - UEMA
Page 2: Apostila Entrando no tom de C# - UEMA

Os alunos do Curso de Engenharia da Computação da Universidade Estadual do Maranhão

– UEMA Período 2012.2, na matéria Paradigmas da Programação, misistrada pelo professor

Diógenes Aquino foram incentivados a criar uma apostila referente à Linguagem de

programação escolhida para ser estudada por eles. No nosso caso, a linguagem escolhida

foi C#. Esperamos que você tire suas dúvidas e se empolgue com essa linguagem que

também nos empolgou. :D

Obrigado.

O que é a linguagem

Tipos de dados

Vetores

Input/Output

Comentários e sua importância

Condicionais, Operadores Lógicos, Matemáticos e Ternário.

Laços de Repetição.

Funções

Parâmetros

Ponteiros

Recursividade

Using e Namespace

Conversão de Dados

Programação orientada a objeto

Operador New

Modificadores de acesso C#.

Tratamento de Erros

Framework.Net

Page 3: Apostila Entrando no tom de C# - UEMA

C# (Lê-se C Sharp) é uma linguagem de programação multi-paradigma, assim como C++ e

Object Pascal. Uma linguagem multi-paradigma pode ser puramente procedural,

puramente orientada a objetos ou pode conter elementos de ambos paradigmas, como

nessa linguagem.

O nome C Sharp foi inspirado em notação musical, onde um sustenido indica que a nota

escrita deve ser feita um semitom mais agudo. Este é semelhante ao nome da língua de

C++, onde "++" indica que a variável deve ser incrementado por 1.

Na notação de cifras, C# significa a nota Dó Sustenido.

Devido a limitações técnicas do display (fontes padrão, navegadores, etc) e o fato de que o

símbolo sustenido (U+266F ♯ SÍMBOLO MUSICAL (HTML: &♯9839;)) não está presente no

teclado padrão, o sinal de número (U+0023 # JOGO DA VELHA (HTML: #)) foi escolhido para

representar o símbolo do sustenido no nome escrito da linguagem de programação [8] Esta

convenção é refletida no ECMA-334 C # Language Specification [6] no entanto, quando ele

é.. prática para fazê-lo (por exemplo, na publicidade ou na caixa de arte [9]), a Microsoft

usa o símbolo musical pretendido.

Essa linguagem foi desenvolvida pela Microsoft no âmbito da sua iniciativa .NET e

posteriormente aprovada como padrão pela Ecma (ECMA-334) e ISO (ISO/IEC 23270:2006).

C# é uma das linguagens de programação projetadas para a Common Language

Infrastructure (CLI).

CLI é uma especificação aberta desenvolvido pela Microsoft e padronizada pela ISO e ECMA

que descreve o código executável e ambiente de execução runtime que formam o núcleo

da Microsoft .NET Framework e implementações Mono e Portable .NET, que são se código

livre e aberto. A especificação define um ambiente que permite que várias linguagens de

alto nível possam ser usadas em diferentes plataformas de computador sem ser reescrito

para arquiteturas específicas.

Desde 2002, C# vem melhorando ao decorrer do tempo, hoje essa linguagem já está na sua

versão 5.0 e a Microsoft não vê ainda razão para descontinuá-la.

Os principios de design de padronização da ECMA para C# foram:

A linguagem C# é destinada a ser uma linguagem simples, moderna, de uso geral e

com programação orientada a objetos.

A linguagem e suas implementações, devem fornecer suporte para os princípios de

engenharia de software, tais como verificação de tipo forte, verificação de limites

de matriz, detecção de tentativas de utilização de variáveis não inicializadas, e

coleta de lixo automática. Software de robustez, durabilidade e produtividade do

programador são importantes.

Page 4: Apostila Entrando no tom de C# - UEMA

A linguagem deve ser utilizada no desenvolvimento de componentes de software

adequados para a implantação em ambientes distribuídos.

Portabilidade do código fonte é muito importante, assim como a portabilidade do

programador, especialmente para os programadores já familiarizados com C e C +

+.

Apoio à internacionalização é muito importante.

C# pretende ser adequada para a criação de aplicativos para ambos os sistemas:

hospedados e incorporados, que vão desde o muito grande, que usa sistemas

operacionais sofisticados, até os muito pequenos com funções dedicadas.

Embora as aplicações C# pretendem ser econômicas em relação aos requisitos de

energia, memória e processamento, a língua não tem a intenção de competir

diretamente no desempenho e tamanho, com C ou linguagem de montagem.

Simplicidade: os projetistas de C# costumam dizer que essa linguagem é tão

poderosa quanto o C++ e tão simples quanto o Visual Basic;

Completamente orientada a objetos: em C#, qualquer variável tem de fazer parte

de uma classe;

Fortemente tipada: isso ajudará a evitar erros por manipulação imprópria de tipos

e atribuições incorretas;

Gera código gerenciado: assim como o ambiente .NET é gerenciado, assim também

o é C#;

Tudo é um objeto: System.Object é a classe base de todo o sistema de tipos de C#;

Controle de versões: cada assembly gerado, seja como EXE ou DLL, tem informação

sobre a versão do código, permitindo a coexistência de dois assemblies homônimos,

mas de versões diferentes no mesmo ambiente;

Suporte a código legado: o C# pode interagir com código legado de objetos COM e

DLLs escritas em uma linguagem não-gerenciada;

Flexibilidade: se o desenvolvedor precisar usar ponteiros, o C# permite, mas ao

custo de desenvolver código não-gerenciado, chamado “unsafe”;

Linguagem gerenciada: os programas desenvolvidos em C# executam num

ambiente gerenciado, o que significa que todo o gerenciamento de memória é feito

pelo runtime via o GC (Garbage Collector).

C# pode ser usada para todos os tipos de aplicações que vão desde jogos de computador,

serviços públicos, sistemas operacionais e compiladores. Há também aplicativos baseados

na web que rodam na plataforma asp.net.

Page 5: Apostila Entrando no tom de C# - UEMA

Em C#, assim como em e Java, os principais tipos de dados são: inteiro, real, caractere,

logico e string (Um vetor de caracteres). Esses tipos de dados naturais da linguagem, são

utilizados quando se quer armazenar um valor qualquer. A linguagem C# (diferentemente

do ANSI C), aceita a declaração de varáveis em qualquer parte do código.

Para se declarar uma variável, basta que declaremos o tipo dela e o nome que deve

começar com uma letra ou com um “_”, o nome da variável somente pode conter letras,

números ou “_”.

Exemplo:

int inteiro;

float real;

char caractere;

string nome;

bool logico;

Há ainda outros tipos de dados, como podemos ver na seguinte tabela:

Referencia: site da Microsoft studio (http://msdn.microsoft.com/pt-br/library/ms228360(v=vs.80).aspx).

Page 6: Apostila Entrando no tom de C# - UEMA

Vetores podem ser divididos nas quatro seguintes categorias.

Vetor de uma dimensão

Vetor de multi-dimensão (Matriz)

Vetor irregular

Vetor mixto

Em C#, o índice começa em zero. Isso significa que o primeiro item de um vetor

começa na posição 0. A posição do último item em um vetor terá um total de número

de itens - 1. Então, se um vetor tem 10 itens, o 10º item está na 9ª Posição.

Em C#, vetores podem ser declarados em tamanho fixo ou dinâmico.

Um vetor de comprimento fixo pode armazenar um número predeterminado de itens.

Um vetor dinâmico não tem um tamanho predefinido. O tamanho de um vetor

dinâmico aumenta à medida que você adiciona novos itens ao vetor. Você pode

declarar um vetor de comprimento fixo ou dinâmico. Você pode mesmo mudar um

dinâmico para um estático depois que ele é definido.

Vamos dar uma olhada em simples declarações de vetores em C#. O seguinte trecho

de código define um simples vetor dinâmico de tipos inteiros que não tem um

tamanho fixo.

int[] intArray;

Como você pode ver a partir do trecho de código acima, a declaração de um vetor

começa com um tipo de vetor seguido de um colchete ([]) eo nome do vetor.

O seguinte trecho de código declara um vetor que pode armazenar 5 itens somente a

partir de índice de 0 a 4.

int[] intArray; intArray = new int[5];

O seguinte trecho de código declara um vetor que pode armazenar 100 itens a partir

de índice de 0 a 99.

int[] intArray; intArray = new int[100];

Page 7: Apostila Entrando no tom de C# - UEMA

No trecho de código anterior, vimos como definir um vetor simples do tipo inteiro. Da

mesma forma, podemos definir vetores de qualquer tipo, tais como double, char e

string.

Em C#, vetores são objetos. Isso significa que declarar um vetor não cria um vetor.

Depois de declarar um vetor, você precisa instanciar um vetor usando o operador

"new".

O seguinte trecho de código define vetores de tipos de dados double, char, bool e

string.

double[] doubleArray = new double[5]; char[] charArray = new char[5]; bool[] boolArray = new bool[2]; string[] stringArray = new string[10];

Uma vez que o vetor é declarado, o próximo passo é inicializar um vetor. O processo

de inicialização de um vetor inclui a adição de dados reais para o mesmo.

O seguinte trecho de código cria um vetor de 3 itens e os valores desses itens são

adicionados quando o vetor é inicializado.

// Inicializando um vetor estático int[] staticIntArray = new int[3] {1, 3, 5};

Como alternativa, também é possível adicionar itens do vetor um de cada vez,

conforme listado na seguinte trecho de código.

// Inicializando um vetor estático um item por vez int[] staticIntArray = new int[3]; staticIntArray[0] = 1; staticIntArray[1] = 3; staticIntArray[2] = 5;

O seguinte trecho de código declara um vetor dinâmico com valores de string.

// Inicializando um vetor dinâmico com items durante a declaração string[] strArray = new string[] { "Mahesh Chand", "Mike Gold", "Raj Beniwal", "Praveen Kumar", "Dinesh Beniwal" };

Page 8: Apostila Entrando no tom de C# - UEMA

Podemos acessar um item do vetor, passando o índice do item pelo vetor. O seguinte

trecho de código cria um vetor de três itens e exibe os itens no console.

// Inicializando um vetor estático um item por vez int[] staticIntArray = new int[3]; staticIntArray[0] = 1; staticIntArray[1] = 3; staticIntArray[2] = 5; // Lendo itens do vetor um item por vez Console.WriteLine(staticIntArray[0]); Console.WriteLine(staticIntArray[1]); Console.WriteLine(staticIntArray[2]);

Este método é útil quando você sabe o item que você deseja acessar a partir de um

vetor. Se você tentar passar um item de índice maior do que os itens de série, você

receberá um alerta de erro.

A declaração de controle foreach (laço) é usado para percorrer os itens de um vetor.

Por exemplo, o código a seguir usa o laço foreach para ler todos os itens de um vetor

de strings.

// Initialize a dynamic array items during declaration string[] strArray = new string[] { "Mahesh Chand", "Mike Gold", "Raj Beniwal", "Praveen Kumar", "Dinesh Beniwal" }; // Read array items using foreach loop foreach (string str in strArray) { Console.WriteLine(str); }

Esta abordagem é usada quando você não sabe o índice exato de um item em um

vetor e precisa percorrer todos os itens.

Page 9: Apostila Entrando no tom de C# - UEMA

Um vetor de multi-dimensão é declarado da seguinte forma:

string[,] mutliDimStringArray;

Um vetor multi-dimensional pode ser de tamanho fixo ou dinâmico.

O seguinte trecho de código é um exemplo de matrizes de tamanho fixo que definem 2

matrizes de 3x2 e 2x2. A primeira matriz pode armazenar seis itens e segunda pode

armazenar quatro. Ambas as matrizes são inicializadas durante a declaração.

int[,] numbers = new int[3, 2] { { 1, 2 }, { 3, 4 }, { 5, 6 } }; string[,] names = new string[2, 2] { { "Rosy", "Amy" }, { "Peter", "Albert" } };

Agora vamos ver exemplos de matrizes dinâmicas, onde você não tem certeza do

número de itens da matriz. O seguinte trecho de código cria duas matrizes sem limite.

int[,] numbers = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 } };

string[,] names = new string[,] { { "Rosy", "Amy" }, { "Peter", "Albert" } };

Você também pode omitir o operador new como fizemos em vetores unidimensionais.

Você pode atribuir esses valores diretamente sem usar o novo operador. Por exemplo:

int[,] numbers = { { 1, 2 }, { 3, 4 }, { 5, 6 } };

string[,] names = { { "Rosy", "Amy" }, { "Peter", "Albert" } };

Também é possível inicializar os itens da matriz um item de cada vez. O seguinte

trecho de código é um exemplo de inicializar os itens do array um de cada vez.

int[,] numbers = new int[3, 2];

numbers[0, 0] = 1; numbers[1, 0] = 2;

numbers[2, 0] = 3; numbers[0, 1] = 4; numbers[1, 1] = 5;

numbers[2, 1] = 6;

Page 10: Apostila Entrando no tom de C# - UEMA

Os itens da matriz são representados em um formato de matriz e para acessá-los, é

preciso especificar a dimensão da matriz. Por exemplo, o item (1,2) representa um

item vetor na matriz, na segunda linha e terceira coluna.

O seguinte trecho de código mostra como acessar varios números definidos no código

acima.

Console.WriteLine(numbers[0,0]);

Console.WriteLine(numbers[0, 1]); Console.WriteLine(numbers[1, 0]);

Console.WriteLine(numbers[1, 1]); Console.WriteLine(numbers[2, 0]); Console.WriteLine(numbers[2, 2]);

Para se entrar com um dado, é necessário que se crie uma variável, onde o

dado será armazenado, e se use das funções Read() ou ReadKey ou ReadLine(). Cada

uma tem uma utilização distinta e estão no namespace System.IO (será explicado mais

sobre classes no decorrer da apostila).

Essa função serve para ler o próximo caractere de fluxo de entrada padrão. Sua

sintaxe é:

public override int Read()

Seus valores de retorno são do tipo System.Int32 (um inteiro).

O próximo caractere de fluxo de entrada representado como um objeto de

Int32 -1, ou se nenhum caractere mais está disponível.

Essa função aguarda até que o usuário pressione uma tecla. Esta função é muito utilizada quando utilizamos aplicações console queremos fazer com que o programa pare em um determinado ponto para que possamos analisar os dados que estão sendo impressos. Sua sintaxe é:

public static ConsoleKeyInfo ReadKey(bool intercept)

O parâmetro intercept, é utilizado para dizer se é ou não para ser impresso na tela a tecla pressionado, basta colocar true para imprimir e false para não imprimir.

Page 11: Apostila Entrando no tom de C# - UEMA

O valor de retorno dessa função é um System.ConsoleKeyInfo que é uma serie de definições de constantes que permitem o programador identificar qual tecla foi pressionada. Para ler a lista, basta acessar: http://msdn.microsoft.com/pt-br/library/system.consolekey(v=vs.80).aspx

A função ler uma linha que o usuário digitar (uma sequencia de caracteres) e retorna para uma variável que esteja sendo igualada a ela. Por exemplo:

string nome = ReadLine();

Sua sintaxe é:

public override string ReadLine ();

Seu retorno é uma string com todos os caracteres digitados pelo usuário antes de pressionar ENTER.

Pode ter surgido agora a duvida: “Como que eu leio um inteiro?” ou até mesmo, “Como que eu leio um caractere?”. Muito simples, utilizamos conversão de tipos. >>Para se ler um inteiro, se utilizaria o seguinte formato: /* Lendo um numero inteiro*/

int number; number = Convert.ToInt32(Console.ReadLine());

>>Para se ler um real, se utilizaria o seguinte formato: /* Lendo um numero real*/

double number; number = Convert.ToDouble(Console.ReadLine());

E assim para todos os outros tipos de dados.

A saída padrão do C# é o monitor do computador. As funções utilizadas com tal finalidade (em projetos Console), são: System.Console.Write() e System.Console.Writeline(). A diferença entre as funções é a quebra de linha, que ocorre na segunda e na primeira não ocorre. Vejamos o exemplo abaixo:

Page 12: Apostila Entrando no tom de C# - UEMA

Usando em ambos: System.Console.Write()

Usando no “Olá Mundo!”: System.Console.WriteLine()

Ambos os métodos são métodos sobrecarregados (característica da orientação a objeto). Metodos sobrecarregados, são aqueles que possuem mais de uma sintaxe padrão e o compilador interpreta qual a melhor opção que se encaixa com determinada utilização. Perceba que pode-se declarar duas variáveis de tipos distintos e passa-las como parâmetro para qualquer uma dessas funções.

public static void WriteLine();

public static void Write();

Para analisar todas as sobrecargas do método WriteLine acesse: http://msdn.microsoft.com/pt-br/library/system.console.writeline(v=VS.80).aspx

Para imprimir um numero ou uma variável, basta fazer uma das sintaxes abaixo:

//Declarando variáveis. string nome = "Usuario";

int idade = 10; /*

Imprimindo variáveis e testando sobrecarga do método Console.Write();

*/ Console.WriteLine(nome);

Console.WriteLine(idade); Console.WriteLine("Meu nome é: {0}",nome); Console.WriteLine("Meu nome é: "+nome);

Page 13: Apostila Entrando no tom de C# - UEMA

Comentários tem a função de deixar seu código mais legível. Quando se escreve

um algoritmo hoje, a probabilidade de você não se lembrar, daqui há um mês, o que

um trecho do seu código faz é muito grande. Há também o fato de tornar seu código

mais legível para as pessoas que iram lê-lo, seu chefe ou mesmo um outro

programador que esteja trabalhando na mesma equipe que você. Sem comentários,

poderia ser necessário ler todo o código para que se entenda somente uma linha e

mesmo assim, pode ser que o próprio desenvolvedor não entenda-a. Para prevenir

tais incômodos, utiliza-se comentários.

Existem 2 métodos para se comentar algo:

//Utilizado para se fazer comentários de somente uma linha

ou /* Utilizado para comentários de mais de uma linha */

ou

/** Utilizado para comentários no .NET */

Os condicionais em C# são utilizados para delimitar uma certa condição para

que se execute um trecho de código ou não. Os condicionais em C#, são os mesmos do

C++ e do Java.

Estruturalmente, se tem:

if (condicao) { /* trecho de codigo */ }

Page 14: Apostila Entrando no tom de C# - UEMA

Podem ser comparações ou ate mesmo variáveis do tipo bool (variável logica). Veja os exemplos: bool condicao = true; int numero = 10;

if (condicao) { Console.WriteLine("Primeira Condicao"); if (numero == 2) Console.Write("Segunda Condicao"); }

Os operadores lógicos do C# são: == (igual), != (diference), >(maior que), < (menor que), >= (maior ou igual que) e <=(menor ou igual que). Pode-se ainda conjugar duas condições. Fica assim:

bool condicao = true; int numero = 10;

if (condição && numero == 10) Console.WriteLine("Primeira Condicao");

Perceba que para condicionais que só utilizam-se de uma linha de comando,

não é necessário o uso das chaves {}.

Os principais operadores que permitem unir duas conjunções são o &&

(Conjunção), o || (Disjunção) e o ! (Negação).

A conjunção serve como o ‘E’ na língua portuguesa, ele serve para unir duas

conjunções e somente torna toda a expressão verdadeira se ambos os termos da

expressão forem verdadeiros.

A disjunção se assemelha ao ‘OU’ da língua portuguesa. Serve para unir duas

conjunções e torna-a verdadeira se qualquer um dos dois forem verdadeiros.

A negação, como o próprio nome sugere, serve para negar uma conjunção. Se

ele era falsa, se torna verdadeira e se verdadeira se torna falsa.

Page 15: Apostila Entrando no tom de C# - UEMA

Os operadores matemáticos, são + (soma), - (subtracao), / (divisão), * (multiplicação) e

% (resto) e Podem ser utilizados em qualquer situação.

O operador resto, retorna o resto da divisão de um numero por outro. 5 % 3 = 2.

Há ainda os operadores do tipo ++, --, += e -=. Estes operadores funcionam da seguinte

forma:

a++; é a mesma coisa de a = a + 1;

a += b; é a mesma coisa de a = a + b;

Page 16: Apostila Entrando no tom de C# - UEMA

O operador ternário serve para realizar uma comparação e uma atribuição,

dependendo do resultado da comparação. Sua estrutura é a seguinte:

a = (condicao) ? retornoVerdadeiro : retornoFalso;

Exemplo:

//declaração de variáveis int maioridade = 0; int idade1 = 20; int idade2 = 30; //operador ternario para atribuição maiorIdade = (idade1 > idade2) ? idade1 : idade2;

Laços de repetição servem para realizar um mesmo trecho de código enquanto uma

condição for valida.

Este laço, realiza uma repetição num bloco de comandos (um trecho de código, entre

chaves) enquanto uma condição for verdadeira. Sua sintaxe é a seguinte:

while(condição){}

Veja o exemplo:

/* * * Este algoritmo vai imprimir todos os * valores pares entre 0 e 10 * */ //declaração de variáveis int i = 0; //laço de repetição while (i <= 10) {

if (i % 2 == 0) {/* * se o resto da divisão por 2 for igual a 0, ou seja, se o * numero for par, então imprima o número. */ Console.WriteLine(i); } i++; }

Page 17: Apostila Entrando no tom de C# - UEMA

O do-while funciona semelhantemente ao while. A única diferença é que no while a

comparação é feita logo no início e no do-while a comparação é feita somente no final. O que

isso significa? Simplesmente significa que no while não é feito nenhum laço caso a condição

não seja satisfeita e já no do-while é feito no mínimo um laço antes que a condição seja

satisfeita. Faça dois programas com condições falsa, um com while e um com do-while e

verifique os resultados.

Sintaxe:

do{}while(condicao);

Este laço é mais utilizado quando se sabe o numero de interações que se pretende

fazer. Por exemplo para contar de 0 até 10, é mais aconselhável usar este laço do que o while

ou o do-while.

Sintaxe:

for (inicialização; condição; incremento)

Exemplo:

int i; for (i = 0; i < 10; i++) { Console.WriteLine(i); }

Page 18: Apostila Entrando no tom de C# - UEMA

Laço derivado do for, que serve para percorrer todos os elementos de uma matriz ou

coleção de objetos. Utiliza-se da seguinte forma:

int[,] matriz = new int[3, 2] { { 1, 2 }, { 3, 4 }, { 5, 6 } }; int elemento; foreach (int element in matriz) { Console.WriteLine(element); }

Saida no console:

Funções são blocos de códigos que servem para fazer uma coisa específica. Por

exemplo, a função System.Console.Write() serve para escrever algo na tela.

Funções tem duas utilidades:

1- Facilitar a leitura do código.

2- Facilitar a escrita do código.

Uma Função pode receber como parâmetros dados de outra função ou até

mesmo de outros programas, dependendo do caso. Um método (sinônimo) só é bem

implementado se for chamado no mínimo 3 vezes, caso contrário, certamente o

programador só deu mais trabalho para si mesmo.

Implementar uma função significa fazer um trecho de código que faça uma

determinada ação.

Segue abaixo o modelo de função

Page 19: Apostila Entrando no tom de C# - UEMA

modificador tipo_retorno nome (parâmetros) {

//código

return retorno;

}

Vejamos a implementação de uma função soma:

public int soma (int numero1, int numero2) { //declaração de váriaveis

int resultado; //somando os dois numeros passados por parâmetro

resultado = numero1 + numero2;

//retorno da função return resultado; }

Serve para dizer quais classes podem acessar ou não a função. Isso será

explanado com mais detalhes mais há frente. O importante agora é saber que esse

modificador dá ou não visibilidade dentro de um objeto ou uma classe sobre a função.

É o tipo da variável que a função irá retornar para quem a chamou. No caso

anterior o tipo da função era inteiro por que o retorno foi a variável resultado que

também é um inteiro.

Os tipos de retorno podem ser qualquer tipo de variável, nativa ou não da linguagem. Um tipo de retorno que merece destaque aqui, é o void. Este tipo de retorno é o que declara que a função não retorna nenhuma variável, funções com esse tipo de retorno são chamadas procedimentos. Geralmente a impressão dos dados é feita na própria função.

Page 20: Apostila Entrando no tom de C# - UEMA

É o nome pelo qual a função irá ser chamada, dentro dos outros métodos

(outro nome para função).

É o algoritmo que será executado toda vez que a função for chamada.

São valores que são passados para a função que é chamada. São as variáveis

que ficam dentro do parênteses. Existem dois tipos de parâmetros (por valor e por

referência). Passar um valor por valor significa passar o seu valor e passar um

parâmetro por referência significa passar o endereço de memoria da variável.

Isso implica que passando um parâmetro por valor, ele quando é alterado é

alterado somente dentro da função que o utiliza. Já na passagem por referência ele

altera os valores dentro da função e em todo o programa.

Quando passados por valor, as variáveis da função recebem uma cópia idêntica

do parâmetro. Caso passado um vetor, o vetor é copiado exatamente do mesmo jeito,

todos os valores do vetor que estão na função chamadora serão os mesmos que

estavam na chamada. A grande diferença é que mesmo que a função chamada

modifique o valor de uma variável, o não será modificado na função chamadora.

Vejamos o exemplo:

class ParametroValor { /** * Definição: Função soma que retorna o resultado da soma de dois * números inteiros. * * @parâmetros: dois números que serão somados. * @retorno: a soma dos dois números passados por parâmetro. * */ public static int soma(int numero1, int numero2) { //variável que armazenará o resultado

int resultado = 0; //operando a soma dos dois números inteiros

resultado = numero1 + numero2; //retorno da função (variável local)

Page 21: Apostila Entrando no tom de C# - UEMA

return resultado; } //função principal que será a chamadora da função int soma(int int); static void Main(string[] args) { //Declaração das variáveis int numero1;

int numero2; int resultado;

//Entrada de dados

Console.WriteLine("Entre com o primeiro numero: "); numero1 = Convert.ToInt32(Console.ReadLine()); Console.WriteLine("Entre com o segundo numero: "); numero2 = Convert.ToInt32(Console.ReadLine());

//Processamento

resultado = soma(numero1, numero2); //Saída Console.WriteLine("{0} + {1} = {2}.", numero1, numero2, resultado); Console.ReadKey(); } }

Quando passados por referência, as variáveis da função recebem o endereço de memória que a variável na função chamadora tem. Quando modificado o valor de uma variável na função chamada ele também é modificado na função chamadora. Para passar um parâmetro como referência, é necessário que se adicione somente o ref na frente do parâmetro.

Veja:

class Programa { /** * Definição: Função soma que por referencia armazena o resultado de * soma. * * @parâmetros: dois números que serão somados passados por valor e

Page 22: Apostila Entrando no tom de C# - UEMA

* parâmetro pro referencia que é um inteiro que armazena * o resultado. * @retorno: a soma dos dois números passados por parâmetro. * */ public static void soma(int numero1, int numero2,ref int resultado) { //operando a soma dos dois números inteiros resultado = numero1 + numero2; } /* * função principal que será a chamadora * da função void soma(int int, ref int); */ static void Main(string[] args) { //Declaração das variáveis int numero1; int numero2; int resultado = 0; //Entrada de dados Console.WriteLine("Entre com o primeiro numero: "); numero1 = Convert.ToInt32(Console.ReadLine()); Console.WriteLine("Entre com o segundo numero: "); numero2 = Convert.ToInt32(Console.ReadLine()); //Processamento soma(numero1, numero2,ref resultado); //Saída Console.WriteLine("{0} + {1} = {2}.", numero1, numero2, resultado); Console.ReadKey(); } }

Rode os dois programas e perceba a diferença.

Nos dias em C que era a principal linguagem de programação, os ponteiros

significavam programação e vice-versa. Agora, em linguagens mais sofisticadas como

C#, os ponteiros são apenas um recurso fornecido, mas não é realmente incentivado.

Em uma linguagem moderna, o argumento é que você nunca precisará descer ao nível

de ponteiros, porque isto simplesmente mostra que você está pensando em um nível

muito primitivo e pode por em perigo a união com o hardware. Embora isto seja

verdade, ainda há momentos quando em que você tem que interagir com o hardware

de uma forma que só pode ser alcançada usando ponteiros. Além disso, há todos

Page 23: Apostila Entrando no tom de C# - UEMA

aqueles programas em C/C++ que usam ponteiros e precisam ser convertido para algo

mais polido e seguro. Em resumo, você certamente deve saber sobre ponteiros mesmo

esperando que você nunca irá realmente usa-los em um programa real. Se você acha

que os ponteiros são essenciais para algo de baixo nível que você está tentando

implementar, em seguida, também é importante que você saiba como implementá-los

em um possível método seguro.

Primeiro precisamos esclarecer algumas confusões que existe entre os três termos

Referência, ponteiro e endereço. Começando como o mais básico endereço: é o

número do endereço de algo que está armazenado. A maioria dos hardwares do

computador atribui endereços usando um esquema simples de incremento a partir de

algum valor e continuam até outro valor, por exemplo, 0-1000, e cada endereço

correspondem a uma posição de memória capaz de armazenar alguns dados com um

determinado número de bits. Esta ideia simples tornou-se cada vez mais complicada

com a evolução do hardware e a introdução do mapeamento de memória de

hardware. Agora o endereço que os dados são armazenados é mudado sem que eles

sejam movidos, devido à utilização de hardware de conversão de endereços. Mesmo

que o hardware não possa mudar o endereço, ele pode ser mudado quando o

aplicativo for executado, pelo sistema operacional ou o sistema de coleta de lixo, que

normalmente efetua algumas mudanças para o sistema ficar mais eficiente. O ponto é

que embora uma vez que o endereço de memória era uma forma fixa e confiável de

acesso, hoje é cercado por uma serie de problemas.

O ponteiro é a abstração de um endereço. Na sua forma mais básica, um ponteiro é

simplesmente uma variável que pode ser usada para armazenar o endereço de alguma

coisa. Você pode usar um ponteiro para acessar o índice do dado que ele está

apontando – um processo chamado desreferenciamento. Você também pode utilizar o

ponteiro para fazer operações aritméticas, que podem mudar sua localização no

armazenamento ou memória. Ou seja, se na memória os objetos são armazenados um

após o outro, você pode executar a “aritmética de ponteiro” para alterar o objeto que

está sendo apontado. Aritméticas de ponteiro é a razão pela qual, muitos

programadores gostam de ponteiros, mas também é a razão pela qual os ponteiros são

perigosos. Um erro no cálculo pode resultar em que o ponteiro aponte para um lugar

onde ele não deve estar apontando e todo o sistema pode falhar com o resultado. Não

há realmente nenhuma razão para que um ponteiro não deva ser basicamente a

abstração de um endereço, mas na maioria dos casos, os ponteiros são apenas

invólucros para endereços da máquina e isto também levanta a questão sobre o que

acontece se o sistema fizer algo que mude o endereço de um objeto. Veremos mais

sobre isso posteriormente.

Page 24: Apostila Entrando no tom de C# - UEMA

Finalmente chegamos ao ponto mais alto da abstração de endereço na forma de uma

referência: uma referência é apenas isso – uma referência a um item de dados ou um

objeto. Se isso soa como um ponteiro, há um sentindo, já que isto é verdade, mas a

ideia fundamental é que você não pode manipular uma referência diretamente. Ou

seja, enquanto há certamente uma aritmética de ponteiros, não pode haver uma

aritmética de referência. Tudo o que você pode fazer com uma referência é passá-la

para outro usuário ou referenciá-lo e acessar os dados que ele faz referência. Como no

caso dos ponteiros, não há razão para que uma referência, não deve ser uma

abstração do endereço da máquina subjacente, mas na maioria dos casos, e em C#, em

particular, a referência é apenas um invólucro para um endereço. Em futuras

implementações, contudo, uma referencia pode ser implementada como um

identificador para uma tabela, em outra tabela, a um índice de recursos e assim por

diante, até que finalmente o hardware converta a referência em um endereço de

objeto dados reais. O ponto é que, enquanto sabemos que é uma referência, e para

que serve um ponteiro, em C#, eles são simplesmente um invólucro para um endereço

e este é um detalhe de implementação, e não algo que você deva se apegar.

Em C# podemos fazer uso de referências o tempo todo, sob a forma de variáveis que

são atribuídas qualquer tipo de referência. Por exemplo, se você criar uma instância de

uma classe, a variável de instância não é um objeto, mas uma referência a um objeto.

Isto é:

MyClass MyObject = new MyClass();

Então MyObject é uma referência a uma instância de MyClass. Na prática, contém o

endereço da ocorrência, mas como já foi explicado, você não deve contar com essa

forma de execução. Este armazenamento de uma referencia no MyObjeto invés de um

valor é mais evidente se você criar uma outra variável que referencia o mesmo objeto

de MyObject:

MyClass MyObject2 = MyObject;

Naturalmente, agora nós não temos outra cópia completa e independente do objeto

referenciado por MyObject, ao invés de referência, temos a variável na mesma

instância. Se você fizer uma alteração na instância usando MyObject2, as mesmas

alterações serão encontradas através de MyObject. A diferença entre uma referência e

um valor, basicamente se resume a esta semântica, a referencia não cria uma copia

dos dados/objetos originais. Se isso acontecer, então temos um valor semântico, se

não temos semântica de referência.

Os ponteiros são uma generalização de um modelo de referência para incluir a

aritmética de ponteiro. Ponteiros são tão perigosos que devem ser colocados em

Page 25: Apostila Entrando no tom de C# - UEMA

quarentena dentro do seu código. Primeiro, o projeto tem que ser marcado como não

seguro (usafe) usando as propriedades do projeto para definir o Build como, Allow

Usafe Code. Então, qualquer uso de ponteiros tem que ser incluído no bloco usafe{}

para marca-lo ainda mais claramente. Ainda mais restritivo é o fato de que você não

pode usar a memória para fazer uso do ponteiro menos complicado. Essencialmente,

você só pode criar um ponteiro para qualquer tipo de valor simples, por exemplo, int,

fload, char, enum, para outro ponteiro ou struct que não contenha outros tipos

gerenciados. Então você não pode ter um ponteiro para um objeto, ou um delegate ou

uma referência. Isto é bastante restritivo e basicamente, não permite que se criem

ponteiros para qualquer coisa criada no heap (pilha de memória) ou algum objeto que

usa uma gestão dinâmica de memória. No entanto, você pode ter um ponteiro para

uma struct que contém tipo de valores simples e você pode criar ponteiros para arrays

de tipos de valores simples. Você também pode ter um ponteiro do tipo void, ou seja,

um ponteiro para um tipo desconhecido, mas para ser de alguma utilidade, por

exemplo, para usar a aritmética de ponteiros, você precisa converter um ponteiro do

tipo void para um ponteiro de um determinado tipo.

Para declarar um ponteiro em C#, usa-se a mesma sintaxe do C++:

tipo * variavel;

O asterisco (*) é o operador de referência ou operador indireto, e geralmente é usado

em conjunto com o endereço do operador e que como o próprio nome sugere, retorna

o endereço de uma variável (sua referência). Por exemplo:

unsafe

{

int* pMyInt;

int MyInt;

pMyInt = &MyInt;

}

O código acima cria um ponteiro de inteiro, ou seja, pMyInt armazena no ponteiro o

endereço do inteiro MyInt. A primeira coisa importante, é que um ponteiro não herda

de um objeto e assim não há métodos associados a ele ou boxing e unboxing. Por

exemplo, você não pode usar um método toString() para mostrar o valor do

ponteiro.O que você pode fazer, é converter o ponteiro para um tipo simples e assim

usar os métodos associados a este tipo, como no exemplo abaixo:

MessageBox.Show(((int)pMyInt).ToString());

Page 26: Apostila Entrando no tom de C# - UEMA

Claro que isto pressupõe que no int caberá o ponteiro, ou seja, um endereço de

memória.

O operador de referencia, também retorna os valores armazenados no endereço que o

ponteiro está apontando. Por exemplo:

MessageBox.Show((*pMyInt).ToString());

O código acima mostra o conteúdo de MyInt. Um ponteiro pode ser nulo e se neste caso for

aplicado o operador de referencia (*), gera uma exceção. Obviamente não faz sentido querer

exibir o conteúdo de um ponteiro nulo, mas você sempre pode converter um ponteiro void e

então evitar o engano. Em alguns casos, você pode produzir um “erro” de converter

incorretamente um ponteiro. Por exemplo:

void* pMyData = &MyInt;

MessageBox.Show((*(double*)pMyData).ToString());

No código acima é definido um ponteiro nulo para apontar para um inteiro, ou seja,

um número inteiro de 32 bits, em seguida, ele é convertido para um ponteiro de

double, ou seja, double *, e finalmente é aplicado o operador de referência para

retornar o valor apontado. Se você tentar fazer isso, verá que ele funciona, mas gera

um resultado inesperado, porque o int original era de apenas 4 bytes de

armazenamento e do double é de 8 bytes. De onde é que será adicionado os 4 bytes? A

resposta é de um local de memória vizinho, um que você não seria capaz de acessar só

apontando para o inteiro. Ler uma posição de memória que você não conhecida até

pode ser seguro, mas quem sabe que efeitos isto pode trazer para o seu código ou

sistema operacional. Por exemplo:

int MyInt2 = 0;

int MyInt=1234;

void* pMyData = &MyInt;

*(double*)pMyData = 123456.789;

MessageBox.Show(MyInt2.ToString());

Você pode se surpreender ao descobrir que o valor de MyInt2 mudou e já não é mais

zero, embora não seja atribuído um novo valor para ela dentro do programa. A

explicação é simples, o armazenamento do MyInt2 é alocado ao lado de MyInt e

quando vamos atribuir um valor de 8 bytes para MyInt os 4bytes extras substituem de

MyInt2. Isto é claramente perigoso, inesperado e indesejado, geralmente por este tipo

de comportamento que o código é considerado inseguro.

Page 27: Apostila Entrando no tom de C# - UEMA

Um uso muito comum de ponteiros é para manipular a estrutura de um tipo de dado.

Por exemplo, suponha que você deseje recuperar os quatro bytes que compõem um

número inteiro de 32 bits:

int MyInt = 123456789;

Sempre podemos usar um ponteiro nulo para obter o endereço de qualquer variável:

void* MyPointer;

MyPointer = &MyInt;

Então podemos atribuí-lo a qualquer ponteiro de qualquer um dos tipos padrão e usar

aritmética de ponteiro, como no exemplo abaixo, onde um byte, é convertido para um

char:

byte* pMyByte = (byte*)MyPointer;

char MyChar = (char)*(pMyByte + 3);

MessageBox.Show(MyChar.ToString());

A razão porque não atribuímos diretamente para um ponteiro de char é que um char

tem dois bytes de tamanho e estamos convertendo um inteiro ASCII de 4 bytes para

caracteres Unicode.

Na maioria dos casos, são existem maneiras de ter acesso à estrutura interna dos tipos

de dados comuns usando as classes Converter ou BitConvertor. No caso do

BitConvertor o método GetBytes pode ser usado para converter o int para um array de

bytes e então qualquer um dos bytes pode ser convertida para um caractere usando a

classe Convert:

Byte[] Bytes = BitConverter.GetBytes(MyInt);

MyChar = Convert.ToChar(Bytes[3]);

Se você entender como funciona um ponteiro, vai entender como lidar com ponteiro

de ponteiro, e ponteiro de ponteiro de ponteiro, e assim por diante. Em teoria, isto é

fácil, na prática não tão complicada como você deve pensar. Por exemplo:

int** ppMyInt;

int* pMyInt;

int MyInt=1234;

pMyInt = &MyInt;

Page 28: Apostila Entrando no tom de C# - UEMA

ppMyInt = &pMyInt;

MessageBox.Show((**ppMyInt).ToString());

No código acima, declaramos um ponteiro de ponteiro, ou seja, ppMyInt e usamos ele

para apontar para pMyInt. Para exibir o valor apontado por pMyInt, ou seja, o valor de

MyInt, usamos dois níveis de referência, **ppMyInt. Neste caso a referência dupla é

fácil de entender, mas em casos reais, ela pode se tornar difícil de trabalhar, quando

você precisar de um ponteiro de ponteiro de ponteiro e assim por diante.

Bem como trabalhar com tipo de valor que você pode criar seus próprios tipos de

dados primitivos usando a pilha. A declaração stackalloc tipo[n] aloca n bytes para o

tipo e retorna um ponteiro. Você não precisa se preocupar com o armazenamento, a

pilha não é modificada, ou a coleta de lixo, enquanto as variáveis estão no escopo de

execução. Você também não precisa se lembrar de desalocar a memória, porque a

pilha é automaticamente limpa quando as variáveis saem do âmbito da aplicação,

geralmente quando o método que as declarou retorna.

Por exemplo:

int* pMyArray = stackalloc int[100];

pMyArray[10] = 34;

MessageBox.Show(pMyArray[10].ToString());

Aloca 100 números inteiros, ou seja, 400 bytes, na pilha e usa o ponteiro para

armazenar 34 em 4 bytes começando no byte 40 e exibe este valor.

Observe que usamos a indexação do array, para fazer a atribuição e para exibir o valor,

exatamente com em um array padrão. No entanto, o bloco de memória é apenas um

bloco de memória que você pode fazer o que quiser com ele. Por exemplo:

public struct MyStructType

{

public int x;

public int y;

};

Page 29: Apostila Entrando no tom de C# - UEMA

MyStructType* pMyDataBlock = stackalloc MyStructType[1];

pMyDataBlock->x = 34;

Aloca uma strutc na pilha, mas isso é, aloca o tamanho do struct que possui dois

números inteiros, ou seja, 8 bytes.

Podemos usar o ponteiro para a struct de forma habitual para definir e acessar um

campo. Ou seja, podemos usar o bloco como se fosse uma estrutura, mas se quisermos

podemos simplesmente tratá-lo como um bloco de 8 bytes e utilizá-lo como outras

estruturas de dados. Por exemplo, se você quiser tratar os dados como um array de

int, você pode:

*((int*)pMyDataBlock) = 36;

Indo por partes:

int* pMyArray = (int*)pMyDataBlock;

pMyArray[0] = 36;

Cria um ponteiro para inteiro, e usa indexação de array para acessar o primeiro

elemento do array que é o mesmo do campo x da estrutura.

Observe que essa forma depende de como a estrutura é organizada em memória e

atualmente, o compilador do C# armazena os campos na ordem em que eles são

declarados. Uma serie de atributos podem ser usados para pedir ao compilador para

usar layouts especiais de memória especial para uma struct.

Recursividade é a técnica de programação na qual uma função chama a si

mesmo até chegar em uma solução trivial. Pode-se utilizar recursividade para resolver

problemas de labirinto, calcular a sequencia de Fibonacci, fatorial, dizer se uma palavra

é um palíndromo ou não. Muitos problemas quando resolvidos com recursividade

podem se resumir a 10 linhas e de outra forma, se tornaria um código imenso.

class Recursividade { /** * Definição: função recursiva que determina * se uma palavra é um palíndromo ou não * * @parâmetros: uma string contendo a palavra, um * índice para o final da palavra e um * índice para o inicio da palavra.

* @sem retorno

Page 30: Apostila Entrando no tom de C# - UEMA

*/ static void palindromo(string palavra, int fim, int indiceInicio) { //solução trivial, se a palavra tem uma só letra. if (fim == 1) Console.Write("Palavra é um palíndromo"); /* * Checando se o primeiro e ultimo termo da palavra são iguais. * Se sim, chamar recursivamente a função senão imprimir uma * mensagem. */ else if (palavra[fim - 1] == palavra[indiceInicio]) palindroma(palavra, fim - 1, indiceInicio + 1); else Console.WriteLine("Palavra não é um palíndromo"); } static void Main(string[] args) { //Var string palavra; //input Console.WriteLine("Entre com uma palavra: "); palavra = Console.ReadLine(); //processing palindromo(palavra, palavra.Length,0); Console.ReadKey(); } }

Um namespace é um organizador. O namespace mais utilizado até agora foi o System, que referencia a classe Console (utilizada em quase todos os métodos até agora). Namespaces servem como referencia para um conjunto de classes, este artifício nos permite chamar uma classe ou um método de uma classe que esteja dentro de um namespace em qualquer parte do nosso programa. O using serve para chamar esse namespace, de modo que não precisemos referenciar toda vez que uma função é de um determinado namespace. Por exemplo: Se não utilizássemos o “using System;”, teríamos que escrever toda vez:

System.Console.WriteLine(); System.Console.Write();

System.Console.Read();

Page 31: Apostila Entrando no tom de C# - UEMA

Existe na teoria de conversão de dados dois tipos: a implícita e a explicita.

Assim como o nome já diz, a conversão é feita de forma implícita, sem que seja

percebido. Converter um dado implicitamente significa, por exemplo atribuir um int para um

float.

namespace ConversaoImplicita { class Program { static void Main(string[] args) { /* * convertendo implicitamente os * dados logo na declaração. * */ sbyte sb = 10; short b = sb; int i = b; long l = i; float f = l; double d = f; Console.Write(f); Console.Read(); } } } }

Existe uma tabela de conversão implícita disponibilizada pela Microsoft, que contém todas as formas implícitas possíveis. Vale ressaltar que esse tipo de conversão deve ser feita de forma cuidadosa, pois a tipos como Int que possuem menos pontos de precisão nos valores armazenados, o que pode acarretar em arredondamentos de valores de forma errônea.

Observe a tabela de conversão abaixo e análise as opções possíveis:

Page 32: Apostila Entrando no tom de C# - UEMA

Essa é a conversão que na qual o programador necessita utilizar o cast explícito

para converter um dado em outro. Para realizar esse tipo de conversão, basta declarar

à frente da variável da direita e entre parênteses o tipo de dado que deseja

(chamamos isso de cast). Veja o exemplo:

namespace CoversaoExplicita { class Program { static void Main(string[] args) { // Convertendo os dados explititamente sbyte a = 10; short b = (short) a; int c = (int) b; long d = (long) c; float e = (long) d; double f = (float) e; //imprimindo o valor de f Console.Write(f); Console.Read(); } } }

Perceba que o valor da variável ‘f’ ainda é um double. Para a conversão

explicita também há uma tabela de equivalência.

Page 33: Apostila Entrando no tom de C# - UEMA

Há ainda a conversão com o auxílio de classes. A classe que daremos destaque nessa apostila é Convert que esta no namespace System. Esta é uma classe que possui mais de 300 métodos próprios para conversão de dados. Os tipos da dados base suportados, são: Boolean, Char, Sbyte, Byyte, Int16, Int32, Int64, Single, Double, Decimal, DataTime (este tipo de dados da suporte para manipulação de datas e horas) e String. Uma conversão que não pode produzir um resultado significativo, produz uma exceção do tipo InvalidCastException (falaremos mais sobre tratamento de exceção posteriormente). Por hora, basta saber que um erro será produzido em transformações do tipo: Char para Boolean, Single, Double, Decimal ou DateTime.

A unidade de programação orientada a objetos no C# é a classe, tendo seu principal fundamento a herança. Os objetos são eventualmente criados dessas classes, e as funções são encapsuladas dentro dos limites das classes como métodos. Definições de classe C# não necessitam de arquivos de cabeçalho ou arquivos separados (IDL, Interface Definition Language). Também permite interfaces, um meio de fazer um contrato com uma classe para processos que a interface determina. No C#, uma classe pode herdar de apenas um objeto pai (parent), mas uma classe pode implementar múltiplas interfaces. C# proporciona também um suporte a structs (estruturas). C# proporciona suporte total de delegates (delegados): para invocação de métodos indiretamente. Delegates são referência de tipos seguros que encapsulam métodos com assinaturas e tipos de retorno específicos.

Objetos instanciados de uma determinada classe, passam a ter as características definidas na classe.

- Membros da classe: Dados (membros de armazenamento) Constantes. Campos. Eventos. Funções (membros que contém código) Construtores. Destrutor. Propriedades. Métodos. Indexadores. Operadores.

Page 34: Apostila Entrando no tom de C# - UEMA

Tipo identificador = new Tipo(...); // Declara um identificador do tipo DateTime, // cria uma instância de um objeto DateTime na pilha // e o inicializa com 25 de Dezembro de 2005. DateTime natal = new DateTime(2005, 12, 25); // Declara três identificadores do tipo int (n1, n2 e n3), // cria três instâncias de objetos int na pilha // e os inicializa com o valor 0 (zero). Int32 n1 = new Int32(); int n2 = new int(); int n3 = 0; // Declara um identificador do tipo int, // mas nenhum objeto é instanciado... // n4 não está inicializado. int n4; // Declara um identificador do tipo Horario, // cria uma instância de um objeto no heap // e o inicializa com 9 horas 10 minutos e 20 segundos. Horario horario = new Horario(9, 10, 20);

Exemplo de uma implementação ideal:

public class Horario : object { byte hora, minuto, segundo;

public Horario(byte hora, byte minuto, byte segundo) : base() { this.hora = hora; this.minuto = minuto; this.segundo = segundo; } }

A ideia por trás das propriedades é que um método ou grupo de métodos seja revestido de tal forma que pareça um campo, no que diz respeito a qualquer código cliente. As propriedades podem ser:

-read-write

Acessores

get

set

- read-only

Acessores

get (somente)

-write-only

Acessores

set (somente)

Page 35: Apostila Entrando no tom de C# - UEMA

public class Horario : object { ...

public byte Hora { get {

return hora; } set { hora = value; } } }

O compilador C# dá ênfase a segurança quando o assunto é inicialização de variáveis, ou seja, toda variável deve ser inicializada antes de ser trabalhada.

Variáveis que são campos de uma classe ou estrutura são zeradas por padrão tão logo uma instância da classe ou estrutura seja criada.

Variáveis locais a um método deverão ser explicitamente inicializadas antes de qualquer instrução na qual elas apareçam.

Escopo

Região de código onde a variável de fato existe.

Uma variável de classe (campo) existe dentro da classe.

Uma variável local existe dentro do bloco no qual foi declarada.

Uma variável declarada num laço for tem escopo somente dentro deste laço.

public class FusoHorario : Horario { string pais; public FusoHorario(string pais, byte hora, byte minuto, byte segundo) : base(hora, minuto, segundo) { this.pais = pais; } new public string ToHora12() { return pais + ", " + base.ToHora12(); } new public string ToHora24() { return pais + ", " + base.ToHora24(); } }

Page 36: Apostila Entrando no tom de C# - UEMA

Em C#, podem ser passados argumentos aos parâmetros por valor ou por referência. Passagem por referência permite que membros da função, métodos, propriedades, indexadores, operadores e construtores para alterar o valor dos parâmetros e fazer essa alteração persistirem no ambiente de chamada. Para passar um parâmetro por referência usa-se o ref ou out palavra-chave, tudo é passado por valor a não ser quando ref ou out é especificado.

static void Main(string[] args) { int i = 5; GetNext(ref i); Console.WriteLine(i); int quociente, resto; Divide(10, 3, out quociente, out resto); Console.WriteLine(quociente); Console.WriteLine(resto); } static void GetNext(ref int numero) { numero++; } static void Divide(int dividendo, int divisor, out int quociente, out int resto) { quociente = dividendo / divisor; resto = dividendo % divisor; }

static void Main(string[] args) { MostraTotal("Livros", 2, 5, 7, 1); } static void MostraTotal(string mensagem, params int[] numeros) { int tot = 0; foreach (int numero in numeros) { tot += numero; } Console.WriteLine("{0} = {1}", mensagem, tot); }

Page 37: Apostila Entrando no tom de C# - UEMA

Membros de instância pertencem ao objeto instanciado.

Cada objeto instanciado possui seu próprio conjunto de dados.

Quando não especificamos a palavra chave static, definimos um membro de instância.

Membros estáticos pertencem a classe, não importando se há ou não a existência de uma instância da classe.

Cada classe possui um e apenas um conjunto de dados estáticos.

Quando especificamos a palavra chave static, definimos um membro estático, ou seja, um membro de classe.

Um membro estático deve processar somente dados estáticos, a não ser é claro que uma referência a um objeto seja passada durante a chamada.

Numero obj1 = new Numero(5);

Numero obj2 = new Numero(5);

if (obj1 != obj2) Console.WriteLine("obj1 != obj2"); // Exibe

Numero obj3 = obj1;

if (obj3 == obj1) Console.WriteLine("obj3 == obj1"); // Exibe

string x, y;

x = "C# Language";

y = "C# Language"; // Há uma otimização do compilador neste ponto

// para a string anterior.

if (x == y) Console.WriteLine("x == y"); // Exibe

// O operador == está sobrecarregado para comparar o conteúdo

// e não o endereço dos objetos.

O operador new, serve par instanciar um novo objeto. Com o operador new, o sistema

operacional interpreta que tem que liberar uma determinada quantidade de memória

pra o programa. Perceba que também usamos esse operador para trabalhar com

vetores, matrizes, filas, pilhas, arvores e com objetos.

Instanciar um objeto, significa criar um novo objeto. Exemplos básicos de instanciação

de objetos são as declarações de variáveis pelo método:

int numero = new int(); bool flag = new bool(); double real = new double ();

Page 38: Apostila Entrando no tom de C# - UEMA

Automaticamente, essas variáveis serão inicializadas com valores padrão. Acesse o

link: http://msdn.microsoft.com/pt-br/library/vstudio/83fhsxwc.aspx para ver mais dos valores

padrões.

Prática: Compile e analise o código abaixo:

using System; class Program { static void Main(string[] args) { //declaração de variáveis (instanciando novos objetos). int myInt = new int(); bool myBool = new bool(); float myFloat = new float(); double myDouble = new double (); //imprimindo os valores das variáveis. Console.WriteLine("Inteiro: {0}",myInt); Console.WriteLine("Bool: {0}",myBool); Console.WriteLine("Float: {0}",myFloat); Console.WriteLine("Double: {0}",myDouble); //Aguardar para finalizar o programa. Console.ReadKey(); } }

Declaração de vetor e matrizes: Perceba a utilização do operador new para alocar

memoria para o vetor e a matriz.

using System; class Program { static void Main(string[] args) { //Declarando um vetor int [] vetor = new int[4] { 1, 2, 3, 4 }; //Declarando uma matriz. int[,] matriz = new int[2, 4] { {1,2,3,4}, {4,5,6,7}}; //Imprimindo os valores do vetor. Console.WriteLine("Elementos do vetor: "); foreach (int elemento in vetor) Console.Write(" {0} ", elemento); //Quebrando duas linhas Console.WriteLine(); Console.WriteLine();

Page 39: Apostila Entrando no tom de C# - UEMA

//Imprimindo os valores da matriz. Console.WriteLine("Elementos da matiriz: "); foreach(int elemento in matriz) Console.Write(" {0} ",elemento); Console.ReadKey(); } }

Instanciando objetos:

using System;

namespace ConsoleApplication1 { /* * Classe Relogio. */ public class Relogio { //declarando as variáveis. private int seg; private int min; private int hr; private char tipo; private static int quantidade = 0; //Construtores //Construtor Padrão public Relogio() { this.tipo = '0'; quantidade++; } public Relogio(int hr, int min, int seg) { this.seg = seg; this.min = min; this.hr = hr;

this.tipo = '1'; quantidade++; } public Relogio(int hr, int min) { this.min = min; this.hr = hr; this.tipo = '2'; quantidade++; }

Page 40: Apostila Entrando no tom de C# - UEMA

//metodos get e set //variável seg public int getSeg() { return this.seg; } public void setSeg(int seg) { this.seg = seg; } //variavel min

public int getMin() { return this.min; }

public void setMin(int min) { this.min = min; } //variável hr public int getHr() { return this.hr; } public void setHr(int hora) { this.hr = hora; } //tipo public char getTipo() { return this.tipo; } //variável estatica quantidade public static int getQuantidade() { return quantidade; }

//Funções que um relogio Possui: public void incremetar_hr() { if (this.tipo != '0') this.hr++; if (hr >= 24) { this.hr = 0; if (tipo == '1')

Page 41: Apostila Entrando no tom de C# - UEMA

Console.WriteLine("Mudou o dia! "); } } public void incrementar_min() { if (this.tipo != '0') this.min++; if (this.min >= 60) { this.min = 0; this.incremetar_hr(); } }

public void incrementar_seg() { if (this.tipo != '0') this.seg++;

if (this.seg >= 60) { this.seg = 0; this.incrementar_min(); } } public void verHora() { if (tipo == '0') System.Console.WriteLine("Relogio molde padrão”); else if (tipo == '1') { if (this.hr >= 10) System.Console.Write(hr); else System.Console.Write("0{0}", this.hr); System.Console.Write(':'); if (this.min >= 10) System.Console.Write(min); else System.Console.Write("0{0}", this.min); System.Console.Write(':'); if (this.seg >= 10) System.Console.Write(seg); else System.Console.Write("0{0}", this.seg); }

else if (tipo == '2') { if (this.hr >= 10) System.Console.Write(hr); else System.Console.Write("0{0}", this.hr); System.Console.Write(':'); if (this.min >= 10) System.Console.Write(min);

Page 42: Apostila Entrando no tom de C# - UEMA

else System.Console.Write("0{0}", this.min); } } //Destrutores ~Relogio() { this.seg = 0; this.min = 0; this.hr = 0; quantidade--; }

} /*

* Classe Principal. */ class Teste { static void Main(string[] args) { //Instanciando os objetos. Relogio relogio0 = new Relogio(); Relogio relogio1 = new Relogio(1,2); Relogio relogio2 = new Relogio(1,2,3); //Métodos dos objetos. relogio2.verHora(); Console.WriteLine(); Console.WriteLine(); relogio2.verHora(); Console.ReadKey(); } } }

Modificadores de acesso servem, na POO, para identificar o nível de acesso de uma variável, um método ou uma classe. Para que uma classe possa ser vista/chamada por todas as outras classes, basta que determine-se esta classe como public, por exemplo. Vejamos: public: Permite que todas as classes vejam quem for declarado desta forma.

Page 43: Apostila Entrando no tom de C# - UEMA

private: Não permite que nenhuma outra classe, além da atual, veja quem for

declarado desta forma.

protected: O acesso é permitido somente à classe e aos seus derivados

(herdeiros) da classe.

internal: O acesso é restrito somente às classes que estão no mesmo projeto.

Em java, equivale ao package.

protected internal: O acesso é restrito às classes do mesmo projeto e às classes

derivadas da atual.

Estes modificadores servem para alterar o estado, diferentemente dos anteriores que

serviam para alterar o acesso. Estes modificadores são aplicadas em funções e/ou

variáreis.

static: Fixa em uma classe um termo. Podemos perceber que sempre que a função

principal ( Main() ) é declara, utiliza-se deste modificador, uma vez que só existe uma

função principal em todo o programa.

abstract: O modificador abstract pode ser aplicado tanto em classes como em

métodos. Em classes, ele as cria de modo que não podem ser instanciadas pois são,

como o nome diz, abstratas. São classes “incompletas”.

Page 44: Apostila Entrando no tom de C# - UEMA

Métodos abstratos são forçados a não possuírem código. Servem apenas de protótipo,

uma definição que deverá ser obrigatoriamente implementada na classe filha.

interface: Interfaces são regras para a criação de outras classes, são moldes. Se uma

classe implementa uma interface ela obrigatoriamente tem que ter implementação

para tudo o que a interface tem.

As interfaces não podem ter variáveis, não podem ter implementações básicas e não

podemos definir modificadores de acesso nos métodos dela, quem deve fazer isso é a

classe que a implementa. O legal é que uma classe pode implementar várias interfaces.

Lembrando que não podemos definir modificadores de acesso nos métodos de uma

interface. Vamos ver um exemplo abaixo:

//Molde (interface) das classes. interface IObjeto { string formato(); } interface IColorido { public string cor(); } //Classe chamada 'BolaVermelha' herda as outras classes public class BolaVermelha : IObjeto, IColorido { public string formato() { return "esférico"; } public string cor() { return "vermelho"; } }

unsafe: Usado quando se usa ponteiros em C#. Toda função que usar um ponteiro

deve possuir esse modificador.

override: O modificador override simplesmente diz que o método que ele segue está

sobrescrevendo outro, ou seja, você está apagando o método antigo e colocando no

lugar este novo. Veremos exemplos mais abaixo.

volatile: O modificador volatile permite que a variável/propriedade possa ser

modificada por fatores externos, como o sistema operacional ou outros processos.

Com o modificador volatile você garante que, independente do processo que esteja

acessando a váriavel/propriedade, ele pegará o valor atual da váriavel/propriedade

Page 45: Apostila Entrando no tom de C# - UEMA

O tratamento de erros é algo muito utilizado nas linguagens orientadas a objeto. O

tratamento de erros, recorre para erros que podem ocorrer durante a execução do programa.

Erros que ocorrem com frequência é uma conversão de tipos errada, acesso de um índice de

vetor invalido, acesso de memória inválido, entre vários outros.

A estrutura do tratamento é

try { //Comandos para serem testados.

} catch (NomeErro) { //Comandos caso o erro seja encontrado. } finally { /* * Comandos que são realizados, não * importando o resultado do teste */ }

Será executado um trecho de código que esta dentro do try, caso ocorra algum

erro, o que normalmente o fecharia, o programa automaticamente passa para a

estrutura que captura o erro (catch) que recebe como parâmetro, mas não

obrigatoriamente, um erro especifico que será tratado. A estrutura finally serve para

executar comandos que sempre devem ser executados, não importando se ocorreu ou

não erro no programa. Toda estrutura try deve possuir um catch, contudo o finally é

opcional.

Para não se tratar um erro, mas identificar que este é conhecido, basta utilizar a clausula throw.

Veja o exemplo tirado do próprio site da Microsoft: http://msdn.microsoft.com/pt-

br/library/0yd65esw(v=vs.80).aspx

using System; class MainClass { static void ProcessString(string s) { if (s == null)

{

Page 46: Apostila Entrando no tom de C# - UEMA

throw new ArgumentNullException(); } }

static void Main() { try { string s = null; ProcessString(s); } catch (Exception e) { Console.WriteLine("{0} Exception caught.", e); } Console.ReadKey(); } }

.Net é o nome da plataforma criada pela Microsoft com o objetivo de fornecer ao desenvolvedor um mecanismo único para a criação e execução de aplicações. Foi criado em 2002 e, ao longo do tempo, já recebeu diversas atualizações e novas versões.

O .Net é executado sobre um ambiente livre de linguagem (Common Language Runtime - CLR). Isso significa que o programador pode usar qualquer uma das linguagens suportadas e, depois, converter seu programa para esse ambiente. Cerca de 30 linguagens são suportadas pelo .Net algumas são: C#, C++, Java, Visual Basic e Pascal.

Page 47: Apostila Entrando no tom de C# - UEMA