Apostila Entrando no tom de C# - UEMA

Page 1


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


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.


 

 

     

  

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.


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;

string nome;

float real;

bool logico;

char caractere;

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).


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];


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 = char[] charArray = new bool[] boolArray = new string[] stringArray =

new double[5]; char[5]; bool[2]; 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" };


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.


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;


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.


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:


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);


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 */ }


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.


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;


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++; }


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); }


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


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.


É 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)


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


* 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


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.


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


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());


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.


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;


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; };


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


*/ 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();


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:


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.


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.


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)


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(); } }


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); }


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 ();


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();


//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++; }


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


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 else if (tipo == '1') { if (this.hr >= 10) System.Console.Write(hr); else System.Console.Write("0{0}", System.Console.Write(':'); if (this.min >= 10) System.Console.Write(min); else System.Console.Write("0{0}", System.Console.Write(':'); if (this.seg >= 10) System.Console.Write(seg); else System.Console.Write("0{0}", } else if (tipo == '2') { if (this.hr >= 10) System.Console.Write(hr); else System.Console.Write("0{0}", System.Console.Write(':'); if (this.min >= 10) System.Console.Write(min);

molde padrão�);

this.hr);

this.min);

this.seg);

this.hr);


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.


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”.


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


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/ptbr/library/0yd65esw(v=vs.80).aspx

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


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.



Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.