Baixe o app para aproveitar ainda mais
Prévia do material em texto
1 C# - Guia rápido A linguagem de Programação C# ......................................................................................................................................... 2 As características do C# ................................................................................................................................................... 2 “Olá Mundo”: A estrutura básica de uma aplicação C# (C Sharp) ................................................................................... 2 Palavras-chave e palavras reservadas da linguagem C# .............................................................................................. 3 Variáveis, tipos de dados, conversão entre tipos de dados e operadores....................................................................... 4 Variáveis e seus tipos ................................................................................................................................................... 4 Definindo Constantes .................................................................................................................................................. 4 Conversão de dados .................................................................................................................................................... 5 Principais operadores ...................................................................................................................................................... 7 Comandos condicionais ................................................................................................................................................... 8 O condicional if ......................................................................................................................................................... 8 O condicional switch ................................................................................................................................................ 9 Laços de repetição (loop) .............................................................................................................................................. 9 Tratamento de erros ...................................................................................................................................................... 12 O try ........................................................................................................................................................................... 12 O catch ....................................................................................................................................................................... 13 O finally ...................................................................................................................................................................... 14 O throw ...................................................................................................................................................................... 14 Arrays e classes de coleções (listas) ............................................................................................................................... 15 Trabalhando com Arrays............................................................................................................................................ 15 Acessando elementos do array ................................................................................................................................. 16 Redimensionando arrays ........................................................................................................................................... 16 Arrays bidimensionais (matrizes) .............................................................................................................................. 16 Classes de coleções (listas) ........................................................................................................................................ 16 Passando arrays como parâmetro ............................................................................................................................. 17 Formatando valores em C# ............................................................................................................................................ 17 Formatador padrão ................................................................................................................................................... 17 Formatador customizado .......................................................................................................................................... 18 Criando um arquivo texto .............................................................................................................................................. 18 Criando pastas no sistema de arquivos.......................................................................................................................... 19 Movendo e Copiando arquivos ...................................................................................................................................... 19 Definindo classe e criando objetos ..................................................................................................................................... 20 Utilizando Construtores ................................................................................................................................................. 23 Utilizando Destrutores ................................................................................................................................................... 24 Propriedades .................................................................................................................................................................. 24 Modificadores de acesso ............................................................................................................................................... 25 Métodos e passagem de parâmetros ............................................................................................................................ 25 Os modificadores in/out/ref .......................................................................................................................................... 27 O modificador ref ...................................................................................................................................................... 28 O modificador out...................................................................................................................................................... 28 Herança .......................................................................................................................................................................... 29 Polimorfismo .................................................................................................................................................................. 31 Conversão entre as classes ............................................................................................................................................ 33 Classes Abstratas............................................................................................................................................................ 33 Interfaces ....................................................................................................................................................................... 34 2 A linguagem de Programação C# A Microsoft define o C# como a principal linguagem de programação para uso na plataforma .NET. Por ser uma derivação da linguagem C++, sem as suaslimitações, e é uma linguagem bastante simples de se programar. As características do C# Dentre as características essenciais do C# podemos citar: • 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#, tudo é classe. System.Object é a classe base de todo o sistema de tipos de C#. • Fortemente tipada: isso ajuda a evitar erros por manipulação imprópria de tipos, atribuições incorretas etc. • Case sensitive: diferencia caracteres maiúsculos e minúsculos. • Controle de versões: cada assemblie 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), e não diretamente pelo programador, reduzindo as chances de cometer erros comuns em linguagens de programação onde o gerenciamento da memória é feito diretamente pelo programador. “Olá Mundo”: A estrutura básica de uma aplicação C# (C Sharp) O código a seguir implementa um programa em modo console (caractere) que é executado no prompt de comando. O resultado da execução é a saída “Olá Mundo”. Salve o código abaixo num arquivo texto e dê o nome de OlaMundo.cs. Observe que “OlaMundo” é o nome da classe e do arquivo, e “.cs” é a extensão do arquivo e também identifica qual linguagem o código foi implementado, neste caso o C# (CSharp). using System; //namespace public class OlaMundo //identificação da classe { public static void Main(string[] args) //identificação do método principal { //código Console.WriteLine("Olá Mundo!"); Console.ReadKey(); } 3 } Todo programa em C# é uma classe. O programa acima é bem primário e possui 4 elementos básicos, uma declaração de namespace, declaração da classe, um método principal, e duas linhas de código. Para compila-ló é necessário usar o compilador C#, o CSC.exe. Ele é encontrado no diretório de instalação do .NET Framework e deve estar referenciado no path do Windows. Para referenciar o compilador digite PATH %path%;C:\Windows\Microsoft.NET\Framework\v3.5 no prompt de comando. Neste caso a versão do .NET Framework é a 3.5. O programa pode ser compilado através do prompt de comando por meio da instrução csc OlaMundo.cs, se nenhum erro ocorrer, será gerado um assemblie chamado OlaMundo.exe, é só executar. Todo programa C# deve ter uma classe que defina o método Main(), que deve ser declarado como estático usando o modificador static, isto diz ao runtime que o método pode ser chamado sem que a classe seja instanciada. É através desse modificador que o runtime sabe qual será o ponto de entrada do programa, para poder passar o controle ao runtime .NET. O “M” maiúsculo do método Main é obrigatório, e seu valor de retorno void significa que o método não retorna nenhum valor quando é chamado. Veja o código MSIL gerado pela compilação abrindo o assemblie gerado (OlaMundo.exe) com o programa IL Disassembler encontrado junto com o compilador da linguagem. Importante observar que strings em C# utilizam são delimitadas por aspas (“ ”) enquanto caracteres utilizam apóstrofos (‘ ’). Em C# é permito 2 tipos de comentários. No exemplo acima foram utilizados os dois tipos de comentários: // e /* ... */. Sintaxe Descrição // Comentário de linha: Comentário iniciado por //, restrito a uma única linha. /* ... */ Comentário de bloco: Comentário delimitado por /* e */, para blocos de código. Palavras-chave e palavras reservadas da linguagem C# abstract event new struct as explicit null switch base extern object this bool false operator throw break finally out true byte fixed override try case float params typeof catch for private uint char foreach protected ulong checked goto public unchecked class if readonly unsafe const implicit ref ushort continue in return using 4 decimal int sbyte virtual default interface sealed volatile delegate internal short void do is sizeof while double lock stackalloc else long static enum namespace string Variáveis, tipos de dados, conversão entre tipos de dados e operadores Esta sessão introduz os operadores, variáveis e seus tipos em C# como também a conversão entre os tipos de dados. Variáveis e seus tipos A declaração de variáveis no C# não possui um comando específico como em outras linguagens de programação (Dim no Visual Basic e var no Javascript). A declaração de variáveis em C# é feita informando-se o tipo de dado a ser utilizado e em seguida o nome da variável, conforme o exemplo a seguir. string nome; int idade; //Declarando e inicializando a variável na mesma instrução string sexo = “M”; Tipos de dados Tipo .NET Framework Sintaxe C# Faixa de Dados Significado Classificação System.Boolean bool True ou false (verdadeiro ou falso) Representa valores true e false Value-types System.Byte byte 0 a 255 (8 bits) Inteiro de 8-bit sem sinal Value-types System.SByte sbyte -128 a 127 (8 bits) Value-types System.Char char 0 a 65535 (16 bits) Um caractere Value-types System.Decimal decimal ±1.0 × 10-28 a ±7.9 × 1028 (128 bits) Tipo numérico para cálculos financeiros (28 casas decimais) Value-types System.Double double ±5.0 × 10-324 a ±1.7 × 10308 (64 bits) Ponto flutuante de precisão dupla (15 casas decimais) Value-types System.Single float ±1.5 × 10-45 a ±3.4 × 1038 (32 bits) Ponto Flutuante de precisão simples (7 casas decimais) Value-types System.Int32 int -2.147.483.648 a 2.147.483.647 (32 bits) Inteiro Value-types System.UInt32 uint 0 a 4.294.967.295 (32 bits) inteiro sem sinal Value-types System.Int64 long –9.223.372.036.854.775.808 a 9.223.372.036.854.775.807 (64 bits) Inteiro Longo Value-types System.UInt64 ulong 0 a 18.446.744.073.709.551.615 (64 bits) inteiro longo sem sinal Value-types System.Int16 short -32.768 a 32.767 (16 bits) Inteiro Short Value-types System.UInt16 ushort 0 a 65.535 (16 bits) Um inteiro short sem sinal Value-types System.String string Seqüência de caracteres (16 bits por caractere) Uma sequência de caracteres Reference-Type Definindo Constantes Outra estrutura importante que pode ser construída no C# são as constantes. As constantes são valores que não alteram seu valor durante a execução do programa. Em geral são utilizados para parâmetros de configuração ou para valores fixos representados por um nome para maior clareza. 5 As constantes são declaradas através da palavra const, e assim como qualquer outra estrutura de programação deve ser declarada dentro de uma classe. O exemplo a seguir mostrar como declarar uma constante. cont int segundos = 60; Conversão de dados Quando precisamos atribuir o valor de uma variável para outra variável de um tipo diferente o C# exige a utilização de uma função de conversão. Diferente de outras linguagens, como o Visual Basic, o C# exige a conversão explicita de dados na maioria dos casos. Vamos analisar os diferentes tipos de conversões possíveis no C#. Conversão Explicita A conversão explicita pode ser feita atravésda utilização de operadores de “cast” (conversão). Um operador de cast é utilizado informando-se o tipo de dados entre parênteses antes da atribuição do valor à variável de um tipo diferente. Um exemplo de operação de cast explicito pode ser observada a seguir. int valorInteiro; double valorGrande = 10; //convertendo valorInteiro = (int)valorGrande; No exemplo acima para converter um valor do tipo double para o tipo int, é necessário informarmos um operador de conversão antes da atribuição (int). Se não informarmos a função de conversão, conforme o exemplo acima recebemos um erro de compilação. Conversão Implicita Apesar da exigência de uma função de conversão na maioria dos casos, conforme vimos no exemplo acima, existem alguns casos em que a função de conversão não se faz necessária. Quando fazemos a conversão de um tipo de dados de menor magnitude para um de maior magnitude, a função de conversão não é necessário, tendo em vista que não corremos o risco de ter perda de valores no processo de conversão, conforme o exemplo a seguir. int valorInteiro = 10; double valorGrande; //convertendo valorGrande = valorInteiro; Conversões para strings A conversão de strings funciona de uma maneira um pouco diferente do que as conversões apresentadas nos exemplos acima. As strings só podem ser convertidas através de funções especiais, capazes de realizar este tipo de conversão. 6 A conversão de uma variável qualquer para string é um processo mais simples dentro de uma aplicação .NET. Para convertermos um valor para o tipo string, basta informarmos a chamada do método ToString() após o nome da variável que possui o valor a ser convertido. Todo e qualquer tipo de dados do .NET possui a função ToString e em alguns casos esta função pode inclusive receber parâmetros. Um exemplo de conversão para o tipo string pode ser observado a seguir. string mensagem; int valor1, valor2; valor1 = 10; valor2 = 15; int result = valor1 + valor2; mensagem = "O resultado da soma de 10 + 15 é " + result.ToString(); No exemplo acima estamos somando duas variáveis do tipo inteiro e concatenando o valor convertido para string em uma variável chamada mensagem. Convertendo string para outros valores A conversão de strings para outros valores se dá através do uso de uma classe especial para conversão, a classe Convert. A classe Convert é capaz de converter uma string para qualquer tipo primitivo de dados. A função a ser utilizada deve ser To<Tipo de Dado>. Abaixo temos um exemplo de uso da classe Convert. string numero = "1000"; int valor = Convert.ToInt32(numero); short valor2 = Convert.ToInt16(numero); DateTime dt = Convert.ToDateTime("01/01/2007"); Outra opção para conversão de strings é a utilização da função Parse de um tipo específico. A maioria dos tipos de dados suporta a função Parse, que permite a conversão de string para um valor, conforme exemplo a seguir. string numero = "1000"; int valor = Int32.Parse(numero); short valor2 = Int16.Parse(numero); DateTime dt = DateTime.Parse("01/01/2007"); 7 Validando a conversão de tipos Muitas vezes precisamos testar o dado contido em uma variável antes de realizamos sua conversão para outro tipo de dado. Isso pode ser feito facilmente através de tratamentos exceções, mas não é uma boa prática de programação, pois consome recursos desnecessários para uma conversão de tipo de dados. Algumas classes de definição de tipos oferecem o método TryParse() que retorna um valor boleano que representa se a conversão ocorreu com sucesso ou não, e retorna o valor convertido no segundo parâmetro do método como out. Mesmo que o retorno seja falso, ou seja não foi possível realizar ele vai retornar o segundo parâmetro inicializado com 0 (zero). Vejamos um exemplo. string entradaDados; int resultado; Console.Write("Digite algo: "); entradaDados = Console.ReadLine(); if (int.TryParse(entradaDados, out resultado)) { Console.WriteLine("É inteiro."); } else { Console.WriteLine("NÃO É inteiro."); } Console.WriteLine("Resultado da conversão: {0} ", resultado); Console.ReadKey(); Principais operadores Em C#, os operadores podem ser classificados da seguinte forma: aritméticos, unários, lógicos, condicionais, relacionais, igualdade e de atribuição. Operadores Aritméticos Operador Uso + Soma tipos numéricos. Também é usado para concatenar strings. - Efetua a diferença de tipos numéricos. / Efetua a divisão de tipos numéricos. * Efetua o produto de tipos numéricos. % Retorna o resto da divisão. Operadores Condicionais Operador Uso && Operador lógico AND usado para comparar expressões booleanas. || Operador lógico OR usado para comparar expressões booleanas. ?: Operador ternário usado da seguinte forma: expr. a: expr. b? expr. c. Isso equivale a dizer: if (expr. a) expr. b; else expr. c; Operadores Relacionais Operador Uso < Condição “menor que” para tipos numéricos. > Condição “maior que” para tipos numéricos. >= Condição “maior ou igual que” para tipos numéricos. 8 <= Condição “menor ou igual que” para tipos numéricos. is Compara em tempo de execução se um objeto é compatível como um tipo qualquer. Esse assunto será abordado mais adiante. as Efetua mascaramento de tipos e caso este falhe, o resultado da operação será null. Esse assunto será abordado mais adiante. Operadores de Igualdade Operador Uso == Avalia a igualdade de dois tipos. != Avalia a desigualdade de dois tipos. Operadores de Atribuição Operador Uso = Atribuição simples. No caso de atribuição entre objetos, referências são atribuídas e não valores. *= Multiplicação seguida de atribuição. Exemplo: x*= 10 que é equivalente a x = x*10. /= Divisão seguida de atribuição. Exemplo: x/= 10 que é equivalente a x = x/10. %= Resíduo seguido de atribuição. Exemplo: x%= 10 que é equivalente a x = x % 10. += Soma seguida de atribuição. Exemplo: x += 10 que é equivalente a x = x + 10. -= Subtração seguida de atribuição. Exemplo: x -= 10 que é equivalente a x = x – 10. <<= Deslocamento de X à esquerda pelo número de bits indicado pela segunda variável/valor seguido de atribuição. Exemplo: x<<=5 que é equivalente a x = x << 5. >>= Deslocamento de X à direita pelo número de bits indicado pela segunda variável/valor, seguido de atribuição. Exemplo: x>>5 que é equivalente a x = x >>5. &= Operação AND seguida de atribuição. Exemplo: x &= 0x0a que é equivalente a x = x & 0x0a. ^= Operação XOR seguida de atribuição. Exemplo: x ^= 0x0a que é equivalente a x = x ^ 0x0a. |= Operação OR seguida de atribuição. Exemplo: x |= 0x0a que é equivalente a x = x | 0x0a. Tabela 1: Operadores suportados pela linguagem C#. Comandos condicionais Em C# existem dois tipos de condicionais: if e switch. O condicional if O if avalia uma expressão lógica booleana e qualquer outro tipo será acusado como erro pelo compilador. Se o resultado for verdadeiro, o bloco de código dentro do if será executado, caso contrário, o controle é passado para a próxima declaração depois do if. Os projetistas de C# optaram por aceitar unicamente expressões booleanas no if para evitar escrever código com semântica obscura e propensa a resultados inesperados. A declaração if tem três formas básicas: 1. 2. 3. if (expressão) { Declaração } if (expressão){ Declaração } else { Declaração } if (expressão) { Declaração } else if (expressão) { Declaração } else (expressão) { Declaração } 9 O condicional switch A declaração switch avalia uma expressão cujo resultado pode ser dos tipos sbyte, byte, short, ushort, int, uint, long, ulong, char, string ou enum, e este por sua vez é comparado com cada uma das seções case que constituem o switch. Vejamos a sua sintaxe: switch(expressão) { case constante1: declaração 1; break; case constante2: declaração 2; break; 57 ... [default: declarações; break; ] } Em C#, é obrigatório que cada seção case tenha uma declaração break. A seção default, é avaliada caso nenhuma das seções case forem verdadeira, equivale ao else do if. Seu uso não é obrigatório. E não pode existir mais de uma seção case com a mesma constante. Laços de repetição (loop) A linguagem C# dá suporte a quatro tipos diferentes de laços: for, foreach/in, while, e do/while. O laço for O laço for trabalha checando uma condição para executar um bloco de código até que essa condição seja verdadeira, no caso do laço for temos que em sua sintaxe declarar sua inicialização, sua condição e seu incremento, veja: for (int i = 0; i <= 10; i++) { //instruções } No código acima temos a sintaxe um laço for onde na primeira parte declaramos uma variável do tipo inteiro (int) e a inicializamos com o valor 0 (zero), na segunda parte temos a condição nesse caso verifica se a nossa variável recém criada é menor ou igual a 10 e a terceira e última parte é o incremento desta variável, sendo essas três partes separadas por ';' (ponto e virgula). O funcionamento é simples, todo código dentro do bloco for será executado dez vezes. Para abandonar o laço antes que a condição for seja falsa, usa-se a palavra reservada break. for (int i = 0; i <= 10; i++) { Console.WriteLine("Iteração número {0}", i); if (i == 3) break; } 10 A palavra reservada continue permite que o fluxo de execução da iteração corrente seja abandonado, mas não o laço, e que a iteração seguinte dê início no topo do laço, uma vez que a condição do for seja satisfeita. for(int i=0; i<=10; i++) { Console.WriteLine("Iteração número {0}", i); if (i == 3) break; } for(int i=0; i<=10; i++) { Console.WriteLine("Iteração número {0}", i); if (i == 3) continue; // a declaração a seguir não será executada quando i==3 Console.WriteLine ("Iteração número {0}", i +2 ); } O laço foreach/in O laço foreach é usado para percorrer listas. Ele opera sobre Arrays ou coleções veja sua sintaxe básica: foreach(<tipo de dado> <nome> in <lista>) { //instruções } Vejamos um exemplo pratico para facilitar o entendimento: string[] nomes = { "Maria", "João", "Sebastião", "Josefa" }; foreach (string pessoa in nomes) { Console.WriteLine("{0} ", pessoa); } Criamos um array de string e colocamos alguns elementos dentro e no nosso laço foreach, como resultado será exibido todos os elementos dentro de nosso array. As suas vantagens em relação ao laço for são as seguintes: • Não precisamos nos preocupar com a avaliação da uma condição booleana para garantir a sua execução; • Nem com a inicialização de variáveis com o seu incremento/decremento; • Nem com a forma de extração do conteúdo do array ou coleção, já que ambos possuem formas diferentes de extração dos seus valores; • Quando todos os elementos do array/coleção tiverem sido varridos, o laço foreach/in será abandonado; 11 • O uso do comando break é valido para quebrar a execução. O laço while De modo diferente do laço for (embora o objetivo seja o mesmo, ou seja, repetir a execução de um código testando uma condição) o laço while é mais simples de ser entendido, pois sua sintaxe não requer que você coloque na mesma linha a variável de inicialização, a condição e o seu incremento. No laço while apenas colocamos a condição que queremos testar, veja como fica: while (expressão booleana) { //instruções } Veja como é simples o código. Expressão booleana é uma expressão que sempre retorna falso ou verdadeiro e a instruções dentro do bloco de código do laço while só será executada enquanto essa expressão retornar verdadeiro. Veja um exemplo: int contador = 2; while (contador != 10) { Console.WriteLine(contador.ToString()); contador++; } Neste caso temos uma variável chamada contador e seu valor é 2, e no laço while testamos se a variável contador é diferente de 10, caso verdadeiro mostramos na tela o valor atual da variável contador e o incrementos em 1 e o laço while continuará até que essa condição se torne falsa. Temos de ter cuidado para não causar um loop infinito, ou seja, a condição nunca será falsa e o laço vai continuar até travar a máquina. As palavras reservadas break e continue também podem ser usadas no laço while da mesma forma do laço for. O laço do...while Se fossemos analisar com cuidado laço while, veríamos que dependendo do caso pode nunca ser executado, ou seja, se a condição do laço while retorna falsa de primeira ele nunca vai ser executado. No exemplo anterior se atribuíssemos o valor 10 a variável “contador” em sua declaração, o laço while nunca começaria. Com o do.. while o código será executado ao menos uma vez porque verificação da condição é no final da instrução, veja: do { //instruções } while (expressão booleana) 12 Podemos traduzir “do” para “faça”, ou seja, faça as instruções enquanto (while) expressão seja verdadeira. Assim garantimos que ao menos uma vez as instruções serão executadas. Exemplo: int contador = 10; do { Console.WriteLine(contador.ToString()); } while (contador != 10); Veja que mesmo “contador” sendo igual a 10 a instrução será executa ao menos uma vez porque só depois que fazemos a verificação. As palavras reservadas break e continue também podem ser usadas no laço do...while da mesma forma do laço for. Tratamento de erros Qualquer um que já foi um simples usuário de software já se deparou com uma mensagem de erro na sua frente, alguns softwares têm mensagens que só ajudam o desenvolvedor, alguns só o usuário e outros tem mensagens que não ajudão em nada. O tratamento de erros consiste em interceptar/capturar esses erros a fim de que o programa não aborte inesperadamente e informe ao usuário o que está errado. Na plataforma .NET esses erros são chamados exceções (ou exceptions) e são tratados com as instruções try, catch e finally. O C# adota um estilo relativamente comum em outras linguagens, que é o de tratar erros como objetos que encapsulam todas as informações que necessitamos para resolvê-lo. Essa ideia é válida para todo ambiente .NET e foi batizada como SEH (Structured Exception Handling). A ideia básica é mais ou menos a seguinte: todo objeto que representa uma exceção é derivado de System.Exception. Essa classe possui os seguintes membros: Propriedade Significado HelpLink Retorna uma URL para um arquivo de Help descrevendo o erro em detalhes. Message Esta propriedade é somente leitura e descreve o erro. Source Retorna o nome do objeto ou aplicação que gerou o erro. StackTrace Esta propriedade é somente leitura e contém uma string que identifica a seqüência de chamadas que disparou o erro. InnerException Pode ser usada para preservar os detalhes do erro ao longo de uma série de exceções. Para tratar as exceções definimos um bloco do código que o runtime irá "tentar" (try) executar,se algo der errado ele vai executar o que estiver num bloco especifico (catch) que contem o que fazer numa situação dessas. Nesse bloco normalmente disparamos alguma mensagem. Também é possível criar um bloco que será sempre executado dando erro ou não (finally). O try O try é o bloco de código para escopo normal. As linhas de código que você simplesmente colocaria dentro do método, passam a ficar dentro do bloco de try onde será executado uma tentativa normal. //Sem o uso do bloco try… public void TesteMetodo1(string teste) 13 { Console.Write((Convert.ToInt32(teste))+1); } //Com o uso do bloco try… public void TesteMetodo2(string teste) { try { Console.Write((Convert.ToInt32(teste))+1); } catch{} } No exemplo acima estamos convertendo um tipo string para inteiro. Isso só é possível se o valor contido na variável “teste” seja um número inteiro. Assim runtime converte o valor de string para inteiro. Caso contrário será gerada uma exceção e o escopo do programa é direcionado para o bloco catch ao qual iremos falar mais abaixo. O catch O catch é o bloco que trata o erro gerado. Dentro deste bloco pode ser vista a mensagem do erro e feita a devida manipulação que o desenvolvedor quiser, como fechar o programa, retornar algo que identifique o erro ou simplesmente mostrar a mensagem de erro que foi gerada. //Com o uso do bloco catch… public void TesteMetodo(string teste) { try { Console.Write((Convert.ToInt32(teste))+1); } catch ( FormatException e) { Console.Write(e.Message.ToString()); } } No bloco catch anteriormente mostrado, apenas será exiba a mensagem de erro a qual a exceção gerada mostra. O bloco catch pode ser repetido várias vezes para serem tratados vários tipos de exceção. public void TesteMetodo(string teste) { try { Console.Write((Convert.ToInt32(teste))+1); } catch (FormatException e) { Console.Write(e.Message.ToString()); } catch (InvalidCastException e) { Console.Write(e.Message.ToString()); } } Existe ainda um tipo de exceção padrão para todos os erros. Este tipo de exceção é chamado apenas de Exception e é usada da seguinte forma. 14 public void TesteMetodo(string teste) { try { Console.Write((Convert.ToInt32(teste))+1); } catch (Exception e) { Console.Write(e.Message.ToString()); } } O finally O finally é como nome já diz o bloco de finalização. Este bloco é executado em qualquer circunstância. Mesmo que o programa gere uma exceção o bloco finally será executado. A ideia é que existe um escopo rodando mesmo que o erro faça o programa parar, assim é possível fazer com que você possa matar conexões ou coisas do tipo, para que essas coisas não consumam carga de processamento e memória. public void testeMetodo(string teste) { string Resp = ""; try { Console.Write((Convert.ToInt32(teste))+1); Resp = "Funcionou"; } catch (Exception e) { Console.Write(e.Message.ToString()); Resp = "Erro"; } finally { Console.Write(Resp); } } No código mostrado acima, se o escopo normal for realizado dentro do bloco try, quando ele for executar o finally irá mostrar a mensagem “Funcionou”. Caso contrário, quando for executado o catch irá mudar o valor para “Erro” e assim quando for executado finally irá ser mostrada esta mensagem na tela. O throw O throw é um complemento aos blocos de código. Ele tem como função explodir uma exceção que pode ou não ser criada pelo usuário. public static void TesteMetodo(string teste) { string Resp = ""; int saida; try { if (teste == null) { throw new ArgumentNullException(); } if (!Int32.TryParse(teste, out saida)) { throw new Exception("Valor inválido."); } 15 Console.Write(saida + 1); Resp = "Funcionou"; } catch (ArgumentNullException e) { Console.Write(e.Message.ToString()); Resp = "Informe um parâmetro."; } catch (Exception e) { Console.Write(e.Message.ToString()); } finally { Console.Write(Resp); } } No exemplo mostrado é checado se a variável teste é nula. Caso a mesma esteja nula é estourada uma exceção de valor NULL, fazendo com que o escopo do programa seja direcionado para o bloco catch. É também testado se o valor é um inteiro, se não é estourada outra exceção. Arrays e classes de coleções (listas) De forma simples um array é um conjunto de elementos de um mesmo tipo de dado onde cada elemento desse conjunto é acessado através de um índice. Um array é também conhecido como vetor (quando unidimensional) ou matriz (quando bidimensional). Trabalhando com Arrays Além das variáveis normais, o C# ainda possui suporte a criação de arrays. Os arrays são declarados de forma semelhante às variáveis convencionas, mas possuem um modificador [ ] que indica que a variável é um array, conforme exemplo. // Apenas um inteiro int valor; // Um array de inteiros int[] valores; Quando declaramos um array no C#, este array encontra-se em um estado não inicializado. Para inicializar um array devemos utilizar o comando new indicando o tamanho a ser alocado para o array. O tamanho do array é informado dentro dos colchetes, conforme exemplo a seguir. // Array com 5 inteiros int[] valores = new int[5]; //Array com 10 nomes, de 0 à 9 string[] nomes = new string[10]; É possível ainda inicializar o array no seu dimensionamento, bastando para isto informar os itens do array logo após o comando new entre chaves, conforme o exemplo a seguir. 16 // Array com 5 inteiros int[] valores = new int[5] { 1, 2, 3, 4, 5 }; // Sem informar a dimensão string[] nomes = new string[] { "Nome1", "Nome2", "Nome3" }; Acessando elementos do array Para acessarmos elementos dos arrays, devemos informar entre colchetes o índice do array a ser acessado. Os arrays sempre iniciam pelo índice 0, conforme o exemplo a seguir. nomes[0] = "Nome1"; nomes[1] = "Nome2"; string var = nomes[3]; Redimensionando arrays Para redimensionarmos um array no C# devemos utilizar uma função da namespace System.Array, a função Resize. Esta função possibilita o redimensionamento do array. string[] nomes = new string[2]; nomes[0] = "Nome1"; nomes[1] = "Nome2"; System.Array.Resize(ref nomes,3); nomes[2] = "Nome3"; Arrays bidimensionais (matrizes) Vamos declarar um array de duas dimensões utilizando o modificar [,], veja: int[,] matriz = new int[2, 2]; Na sintaxe acima declaramos um array bidimensional com duas linhas e duas colunas, ou seja, temos um array com 4 posições: matriz [0, 0] = 1; matriz [0, 1] = 2; matriz [1, 0] = 3; matriz [1, 1] = 4; A ordem de acesso é sempre coluna x linha. Classes de coleções (listas) Apesar de suportar array, as operações com este tipo de estrutura em geral são complexas de se implementar. O mais comum é utilizarmos as classes de coleção. Estas classes abstraem a complexidade dos array, tornando o redimensionamento e a obtenção de itens automática. As classes de coleção fazem parte do namespace System.Collections. A seguir temos um exemplo da utilização da classe ArrayList. 17 System.Collections.ArrayList lista = new System.Collections.ArrayList(); lista.Add("Nome1"); lista.Add("Nome2"); lista.Add("Nome3"); lista.Remove("Nome2"); lista.RemoveAt(0); É possível observar que utilizando o ArrayList não há necessidade de redimensionamento quando inserimos ou removemos um item. Analogamente a classe ArrayList, a classe List do namespace System.Collections.Generic permite a criação de uma coleção para armazenamento de um conjunto de dados quaisquer. A diferença entre as duas bibliotecas é que a biblioteca genérica nos permite informar o tipo de dados o que queremos armazenar na coleção. Esta informação é indicada na declaração da coleção pelo parâmetro T, onde devemos informar o tipo de dados a ser utilizado. O exemplo a seguir cria uma lista de números inteiros e outra lista de uma classe definida pelo usuário. //Lista de valors inteiros List<int> listaInteiros = new List<int>(); listaInteiros.Add(10); int valor = listaInteiros[0]; //Lista de instância de uma classe definida pelo usuário List<MinhaClasse> listaMinhaClasse = new List<MinhaClasse>(); listaMinhaClasse.Add(new MinhaClasse()); Passando arrays como parâmetro Ao se passar um array como parâmetro por valor (in), os seus elementos se comportam como se fossem passados por referência. Isso significa que se alterarmos o valor de um elemento do vetor dentro do método, essa alteração vai ocorrer também no elemento do vetor passado como parâmetro. Formatando valores em C# Existem situações em que necessitamos formatar valores do tipo Data, Dinheiro, Inteiros ou Decimais. No C# temos dois grupos de formatadores, formatador padrão é formatador customizado. Formatador padrão A sintaxe é a seguinte: [Formato][Qtde. Casas Decimais]. O caracter que vem após os dois pontos é o formato em que o valor será exibido. Você também poderá optar por definir a quantidade de casas decimais da seguinte forma: C2. A seguir uma lista com os valores possíveis: Formatador Padrão Descrição C Exibe o valor no formato de moeda. D Exibe o valor no formato decimal. E Exibe o valor no formato científico (exponencial). F Exibe o valor no formato fixo. G Exibe o valor no formato geral. N Exibe o valor no formato numérico. P Exibe o valor no formato de porcentagem. X Exibe o valor no formato hexadecimal. 18 Os caracteres acima que especificam o formato a ser exibido, não são case-sensitive, exceto para o X, pois se ele for minúsculo os valores serão apresentados em minúsculo, do contrário, serão exibidos em maiúsculo. Formatador customizado Usado para formatar data e hora. Formatador Customizado Descrição MM/dd/yyyy Formato Mês/Dia/Ano. dd/MM/yyyy Formato Dia/Mês/Ano. HH:mm Formato Hora:Minuto. HH:mm:ss Formato Hora:Minuto:Segundo. dd/MM/yyyy hh:mm:ss Formato Dia/Mês/Ano Hora:Minuto:Segundo. Devemos nos atentar para o MM e para o mm, pois o maiúsculo significa Mês, já o minúsculo significa Minutos. Pode-se também ao invés de barras "/" utilizar o hífen "-" como separador para as Datas, ficando a string de formatação da seguinte forma: {0:dd-MM-yyyy hh:mm:ss}. Também existe diferença para o formatador que representa a hora. HH representa o formato 24 horas enquanto, hh representa o formato 12 horas. Criando um arquivo texto Para manipulação de arquivos devemos importar o namespace System.IO, vamos agora criar um arquivo texto onde escreveremos algumas informações como exemplo. Existem várias maneiras de se abrir um arquivo texto no C#. Uma maneira muito simples é utilizando as classes StreamReader e StreamWriter. Estas classes permitem a gravação e a leitura de arquivos de uma maneira simples, fornecendo ainda métodos para a gravação e leitura de Strings nestes arquivos. Para utilizarmos estas classes, basta criarmos uma instancia das mesmas informando em seu construtor o nome do arquivo a ser aberto. Através dos métodos WriteLine e ReadLine, podemos gravar e ler informações dos arquivos texto de uma maneira bem simples. No exemplo abaixo, iremos abrir um arquivo texto para gravação e gravar algumas informações dentro dele. StreamWriter file = new StreamWriter(@"C:\teste.txt"); file.WriteLine("TESTE Linha 1"); file.WriteLine("TESTE Linha 2"); file.WriteLine("TESTE Linha 3"); file.WriteLine("TESTE Linha 4"); file.Close(); Se executarmos o exemplo de gravação de arquivos apresentado acima mais de uma vez, veremos que o arquivo é sobrescrito a cada execução. Para criarmos um StreamWriter capaz de realizar uma operação de inserção ao final de um arquivo (Append) basta informarmos um parâmetro adicional no construtor do 19 mesmo. No exemplo abaixo, estamos passando o valor de True para o parâmetro de append, permitindo que as informações sejam adicionadas no arquivo. StreamWriter file = new StreamWriter(@"C:\teste.txt", true); file.WriteLine("TESTE Linha 1"); file.WriteLine("TESTE Linha 2"); file.WriteLine("TESTE Linha 3"); file.WriteLine("TESTE Linha 4"); file.Close(); Note que ao finalizarmos a gravação das informações no arquivo, utilizamos o método Close para fechar o Stream. É importante fecharmos o arquivo sempre que terminamos de realizar qualquer operação com arquivos, do contrário o arquivo ficará aberto. No exemplo abaixo, iremos criar uma instancia da classe StreamReader para ler o arquivo que acabamos de criar. Utilizaremos o método ReadLine para ler as informações do arquivo. StreamReader fileRead = new StreamReader(@"C:\teste.txt"); string linha; while (fileRead.Peek() > -1) { linha = fileRead.ReadLine(); Console.WriteLine(linha); } fileRead.Close(); Notem que utilizamos o método Peek da classe StreamReader. Este método nos informa o número de caracteres restantes existentes no arquivo. Caso não existam mais caracteres a serem lidos, o método Peek retorna o valor de -1. Criando pastas no sistema de arquivos Agora que já vimos como criar e ler arquivos, vamos criar uma nova pasta para colocar este arquivo que acabamos de criar com nosso programa de exemplo. No namespace System.IO existe uma classe chamada Directory. Esta classe possui métodos para trabalharmos com pastas do sistema operacional. Para criarmos uma novo pasta basta chamarmos o método CreateDirectory, conforme o exemplo abaixo. Directory.CreateDirectory(@"C:\NovoDir"); Movendo e Copiando arquivos A tarefa de mover arquivos e copiar arquivos também é simples. Para isto, basta utilizar os métodos compartilhados da classe File. O método MoveFile é utilizado para mover arquivos, enquanto o método CopyFile é utilizado para mover arquivos. No exemplo abaixo podemos ver um exemploda utilização dos dois comandos. File.Copy(@"C:\teste.txt", @"C:\SubDir\Teste2.txt"); File.Move(@"C:\teste.txt", @"C:\SubDir\Teste.txt"); 20 Definindo classe e criando objetos Numa aplicação desenvolvida com uma linguagem orientada a objetos o trabalho “pesado” é feito por classes. Mesmo aplicações pequenas em C# necessitam da elaboração de uma ou mais classes, cada uma com suas propriedades e métodos usados para executar as tarefas relativas ao objeto. Em C#, podemos ter dentro de uma classe os seguintes membros: atributos, construtores, destrutores, domínios, métodos, propriedades, indexadores, delegates, eventos e nested classes. A criação de classe usa basicamente a sintaxe apresentada no listagem a seguir. <using> <namespace1>; <using> <namespace2>; <public/private/protected> class <NomeDaClasse> { <public/private/protected> <tipo> <atributo1>; <public/private/protected> <tipo> <atributo2>; <public/private/protected> <tipo> <atributoN>; <public/private/protected> <tipo retorno> <propriedade1>; <public/private/protected> <tipo retorno> < propriedade2>; <public/private/protected> <tipo retorno> < propriedadeN>; <public/private/protected> <tipo retorno> <método1>; <public/private/protected> <tipo retorno> <método2>; <public/private/protected> <tipo retorno> <métodoN>; } Vamos considerar uma aplicação de exemplo que defina uma classe Pessoa que armazene dados de uma pessoa como nome, sexo, idade, peso, altura, data de nascimento e salário, e com estes atributos faça o cálculo do índice de massa corporal (IMC) e o aumente do salário da pessoa. Nosso programa pode ser qualquer tipo de aplicação .NET (WindowsForms, ASP.NET, etc). E este fará acesso a classe Pessoa. Dentro do projeto escolhido devemos criar uma classe chamada Pessoa. O listagem a seguir é o código de definição da classe Pessoa com seus atributos, propriedades, construtores, métodos e destrutores. /// <summary> /// Classe representando uma pessoa. /// </summary> public class Pessoa { private string _nome; /// <summary> /// Configura ou recupera o nome da pessoa. /// </summary> public string Nome { get { return _nome; } set { _nome = value; } } private EnumSexo _sexo; /// <summary> /// Configura ou recupera o sexo da pessoa. /// </summary> public EnumSexo Sexo { get { return _sexo; } set { _sexo = value; } 21 } private int _idade; /// <summary> /// Recupera a idade da pessoa. /// </summary> public int Idade { get { return _idade; } } private double _peso; /// <summary> /// Configura ou recupera o peso da pessoa. /// </summary> public double Peso { get { return _peso; } set { _peso = value; } } private double _altura; /// <summary> /// Configura ou recupera a altura da pessoa. /// </summary> public double Altura { get { return _altura; } set { _altura = value; } } private DateTime _dataNascto; /// <summary> /// Configura ou recupera a data de nascimento da pessoa. /// </summary> public DateTime DataNascto { get { return _dataNascto; } set { _dataNascto = value; _idade = CalcularIdade(DateTime.Now); } } private double _salario; /// <summary> /// Configura ou recupera o salário da pessoa. /// </summary> public double Salario { get { return _salario; } set { _salario = value; } } /// <summary> /// Construtor: Inicializa o objeto (Sobrecarregado) /// </summary> /// <param name="nome">Nome da pessoa</param> /// <param name="sexo">Sexo da pessoa</param> /// <param name="peso">Peso da pessoa</param> /// <param name="altura">Altura da pessoa</param> /// <param name="dataNascto">Data de nascimetno da pessoa</param> /// <param name="salario">Salário da pessoa</param>/// public Pessoa(string nome, EnumSexo sexo, double peso, double altura, DateTime dataNascto, double salario) { _nome = nome; _sexo = sexo; _peso = peso; _altura = altura; _dataNascto = dataNascto; 22 _idade = CalcularIdade(DateTime.Now); _salario = salario; } /// <summary> /// Construtor padrão (Sobrecarregado) /// </summary> public Pessoa() { _nome = ""; _sexo = EnumSexo.Masculino; _idade = 0; _peso = 0; _altura = 0; _dataNascto = DateTime.Now; _salario = 0; } /// <summary> /// Retorna o IMC da pessoa quando peso e altura são informados /// </summary> /// <returns>Valor do IMC</returns> public double IMC() { if ((Altura > 0) && (Peso > 0)) { return (Peso / System.Math.Pow(Altura, 2)); } return (0); } /// <summary> /// Retorna a classificação do peso da pessoa conforme o cálculo do IMC. /// </summary> /// <returns></returns> public string IMCClassificacao() { double imc = IMC(); string retorno = "Seu IMC é " + imc.ToString("N2") + ". "; if (imc < 18.5) retorno += "Você está abaixo do peso ideal."; else if (imc <= 24.9) retorno += "Parabéns. Você está em seu peso normal!"; else if (imc <= 29.9) retorno += "Você está acima de seu peso (sobrepeso)."; else if (imc <= 34.9) retorno += "Obesidade grau I."; else if (imc <= 39.9) retorno += "Obesidade grau II."; else retorno += "Obesidade grau III. Procure um médico."; return (retorno); } /// <summary> /// Calcula a idade da pessoa. /// </summary> /// <param name="dataReferencia">Data em que se quer saber a idade da pessoa.</param> /// <returns></returns> protected int CalcularIdade(DateTime dataReferencia) { int idade = dataReferencia.Year - this._dataNascto.Year; return (idade); } /// <summary> 23 /// Aumenta o salário conforme o percentual informado. /// </summary> /// <param name="percentualAumento">Percentual de aumento do salário.</param> /// <param name="novoSalario">Retorna o novo salário após o aumento.</param> public void AumentarSalario(double percentualAumento, out double novoSalario) { novoSalario = this._salario + (this._salario * (percentualAumento / 100)); this.Salario = novoSalario; } /// <summary> /// Aumenta o salário da pessoa conforme o valor informado. /// </summary> /// <param name="valorGratificacao">Entrada: valor do aumento de salário; Saída: novo salário com aumento</param> public void AumentarSalario(ref double valorGratificacao) { valorGratificacao = this._salario + valorGratificacao; this.Salario = valorGratificacao; } /// <summary> /// Destrutor /// </summary> ~Pessoa() { } } Classe Pessoa. Após implementação da classe Pessoa é possível instanciá-la. Dentro do projeto utilize algum recurso de interface de usuário para instanciar. Uma classe é instanciada por meio da palavra reservadanew. A sintaxe básica para criar um objeto a partir de uma classe é Classe objeto = new Classe(); A classe Pessoa tem dois construtores, o primeiro é o construtor padrão que não recebe parâmetros, enquanto o segundo permite inicializar o objeto com alguns parâmetros. Vejamos os exemplos a seguir. Pessoa pessoa = new Pessoa(); Classe Pessoa instanciada com seu construtor padrão. Pessoa pessoa = new Pessoa("André", EnumSexo.Masculino, 72, 1.82, 09/09/1981, 10000.00); Classe Pessoa instanciada com construtor sobrecarregado. Utilizando Construtores As classes podem apresentar métodos especiais chamados de construtores. Os construtores são métodos especiais capazes de inicializar a classe com algum valor. O método construtor pode ser identificado por um método com o mesmo nome da classe. O construtor é chamado sempre que instanciamos uma classe utilizando o comando new. Uma classe pode ter diversos construtores, cada um com uma assinatura diferente. Na classe Pessoa temos dois construtores, um construtor padrão que não possui parâmetros (assinatura 1), e um segundo com parâmetros que inicializam os atributos da classe (assinatura 2). Vejamos o trecho de código a seguir retirado da classe Pessoa. /// <summary> /// Construtor: Inicializa o objeto (Sobrecarregado) /// </summary> 24 /// <param name="nome">Nome da pessoa</param> /// <param name="sexo">Sexo da pessoa</param> /// <param name="peso">Peso da pessoa</param> /// <param name="altura">Altura da pessoa</param> /// <param name="dataNascto">Data de nascimetno da pessoa</param> /// <param name="salario">Salário da pessoa</param>/// public Pessoa(string nome, EnumSexo sexo, double peso, double altura, DateTime dataNascto, double salario) { _nome = nome; _sexo = sexo; _peso = peso; _altura = altura; _dataNascto = dataNascto; _idade = CalcularIdade(DateTime.Now); _salario = salario; } /// <summary> /// Construtor padrão (Sobrecarregado) /// </summary> public Pessoa() { _nome = ""; _sexo = EnumSexo.Masculino; _idade = 0; _peso = 0; _altura = 0; _dataNascto = DateTime.Now; _salario = 0; } Utilizando Destrutores Quando o garbage collector faz o processo de desalocação dos objetos, podemos fazer com que uma função seja executada antes que o objeto seja eliminado. Estas funções são chamadas funções destrutoras. Em geral utilizamos estas funções para destruirmos instâncias de objetos não gerenciáveis, tendo em vista que o framework não tem controle sobre este tipo de objeto. A implementação de um destrutor é semelhante a sintaxe utilizada para construirmos um construtor, com a diferença que devemos utilizar o símbolo “~” na frente da declaração. O exemplo a seguir mostra o destrutor da classe Pessoa, que neste caso está vazio, ou seja, sem implementação, pois não houve necessidade. /// <summary> /// Destrutor /// </summary> ~Pessoa() { } Poderíamos ter uma classe que faz uso de um objeto COM (componente VB6). Este objeto aloca uma série de recursos não gerenciáveis que são utilizados durante a execução de métodos da classe. Para garantirmos que os recursos deste objeto COM sejam liberados devemos implementar um destrutor para a classe, de forma a liberar qualquer recurso alocado pela classe COM. Propriedades Propriedades são métodos que protegem (encapsulam) o acesso a membros da classe, ou seja, separa os elementos visíveis de um objeto dos invisíveis. Todos os atributos da classe Pessoa exemplificada anteriormente foram encapsulados, ou seja, protegidos por meio das suas respectivas propriedades. 25 O código a seguir foi retirado da classe Pessoa que declara o atributo _nome como privado, ou seja, somente é acessado diretamente pela classe. Externamente (fora da classe) o atributo pode ser acessado pela propriedade Nome. As palavras reservadas get, e set indicam que a propriedade pode recuperar (get) o valor do atributo _nome e receber (set) um novo valor. private string _nome; /// <summary> /// Configura ou recupera o nome da pessoa. /// </summary> public string Nome { get { return _nome; } set { _nome = value; } } Modificadores de acesso Os modificadores de acesso dizem ao compilador a forma como classe e seus membros podem ser acessados externamente. Veja na tabela a seguir os modificadores: Modificadores de Acesso - CLASSES Modificador Tipo de Acesso Public Permite que a classe seja acessada por qualquer assemblie. Selead Não permite que a classe seja herdada. Partial Permite que a classe tenha seu escopo dividido em vários arquivos. Static Especifica que a classe somente tem membros estáticos. Não pode ser instanciada. Abstract Define moldes para classes filhas. Não pode ser instanciada. Modificadores de Acesso – MEMBROS DA CLASSE Modificador Tipo de Acesso Public Permite que os membros das classes sejam acessados por qualquer outro escopo Protected Permite que membros sejam usados apenas pela classe que o contém e permite que estes sejam “herdados” para classes derivadas da original. Private O membro é de uso exclusivo da classe onde é declarado. Internal Permite acesso somente por classes do mesmo assemblie. Static Permite acesso, sem necessidade do objeto ser instanciado. Abstract São métodos de classes Abstract que não possuem implementação (sem codificação). Virtual Permite que os métodos sejam sobrescritos por classes filhas. Readonly Limita acesso a somente leitura aos atributos da classe Quando o modificador de acesso não é especificado para atributos, o compilador assume o modificador private como padrão. Para classes o modificador padrão é public. Métodos e passagem de parâmetros No C# os métodos são análogos a funções e procedimentos em outras linguagens de programação como C e Pascal. Um método pode ter variáveis locais, que são variáveis declaradas “dentro” do método e só podem ser utilizadas (escopo) apenas dentro do método onde foram declaradas. Os parâmetros do método também são locais a ele. Inicialmente é preciso informar o tipo de visibilidade do método por meio dos modificadores de acesso a membros (visto anteriormente). Logo após é necessário informar o seu tipo de retorno. Caso o método não 26 tenha nenhum retorno (o que é equivalente as sub-rotinas em outras linguagens) deverá ser utilizada a palavra reservada void. Em seguida é dado um nome ao método, e entre parênteses, o conjunto de parâmetros aceitos pelo método, se necessário. Os parâmetros são declarados da mesma forma que as variáveis (tipo de dado seguido do nome do parâmetro) e devem ser separados por vírgula. Após a declaração dos parâmetros devemos então informar o corpo do método entre chaves. Para métodos que retornam algum valor é obrigatório informar a cláusula return com o valor de retorno dentro do corpo do método. Vejamos alguns exemplos. //Método simples, sem retorno e sem parâmetros (Equivalente a procedimentos em outras linguagens) protected void NomeDoMetodo() { } //Método sem retorno e com parâmetros (Equivalente a procedimentos com parâmetros em outras linguagens). protected void NomeDoMetodo(string param1, int param2) { } //Método que retorna uma string e com parâmetros. (Equivalente a função em outras linguagens). protected string NomeDoMetodo(string param1, int param2) { return(“string de retorno”); } O código cima apresenta três métodos, o primeiro sem retornoe sem parâmetros, o segundo sem retorno e com parâmetros e o último com retorno e parâmetros. A classe Pessoa usada como exemplo apresenta 3 métodos, IMC, IMCClassificacao e AumentarSalario, sendo este último sobrecarregado duas vezes. O primeiro retorna o valor do IMC, o segundo retorna a classificação do IMC da pessoa, e o último aumenta o salário da pessoa conforme os parâmetros da assinatura do método. Também apresenta um quarto método protegido, CalcularIdade, recebendo como parâmetro uma data de referência para o cálculo da idade da pessoa. /// <summary> /// Retorna o IMC da pessoa quando peso e altura são informados /// </summary> /// <returns>Valor do IMC</returns> public double IMC() { if ((Altura > 0) && (Peso > 0)) { return (Peso / System.Math.Pow(Altura, 2)); } return (0); } /// <summary> /// Retorna a classificação do peso da pessoa conforme o cálculo do IMC. /// </summary> /// <returns></returns> public string IMCClassificacao() { 27 double imc = IMC(); string retorno = "Seu IMC é " + imc.ToString("N2") + ". "; if (imc < 18.5) retorno += "Você está abaixo do peso ideal."; else if (imc <= 24.9) retorno += "Parabéns. Você está em seu peso normal!"; else if (imc <= 29.9) retorno += "Você está acima de seu peso (sobrepeso)."; else if (imc <= 34.9) retorno += "Obesidade grau I."; else if (imc <= 39.9) retorno += "Obesidade grau II."; else retorno += "Obesidade grau III. Procure um médico."; return (retorno); } /// <summary> /// Calcula a idade da pessoa. /// </summary> /// <param name="dataReferencia">Data em que se quer saber a idade da pessoa.</param> /// <returns></returns> protected int CalcularIdade(DateTime dataReferencia) { int idade = dataReferencia.Year - this._dataNascto.Year; return (idade); } /// <summary> /// Aumenta o salário conforme o percentual informado. /// </summary> /// <param name="percentualAumento">Percentual de aumento do salário.</param> /// <param name="novoSalario">Retorna o novo salário após o aumento.</param> public void AumentarSalario(double percentualAumento, out double novoSalario) { novoSalario = this._salario + (this._salario * (percentualAumento / 100)); this.Salario = novoSalario; } /// <summary> /// Aumenta o salário da pessoa conforme o valor informado. /// </summary> /// <param name="valorGratificacao">Entrada: valor do aumento de salário; Saída: novo salário com aumento</param> public void AumentarSalario(ref double valorGratificacao) { valorGratificacao = this._salario + valorGratificacao; this.Salario = valorGratificacao; } Os modificadores in/out/ref Quando criamos parâmetros em métodos do C#, estes parâmetros são passados por padrão como valores (in), ou seja, se estes valores forem alterados durante a execução do método, a mudança destes valores não será refletida na variável passada por parâmetro. Este comportamento é atribuído ao modificador de parâmetro in, que é implícito e não precisa ser informado. O exemplo abaixo extraído da classe Pessoa utiliza passagem de parâmetros por valor. /// <summary> /// Calcula a idade da pessoa. /// </summary> /// <param name="dataReferencia">Data em que se quer saber a idade da pessoa.</param> /// <returns></returns> 28 protected int CalcularIdade(DateTime dataReferencia) { int idade = dataReferencia.Year - this._dataNascto.Year; return (idade); } No exemplo acima, mesmo que o parâmetro dataReferencia fosse alterado dentro do método seu valor externamente não seria modificado. Este comportamento pode ser alterado utilizado os moficadores ref e out. O modificador ref O parâmetro ref permite a passagem de valores por referência. Utilizando a passagem de valores por referência, os valores modificados dentro da função refletem suas alterações para a função chamadora. Para utilizarmos o operador ref, devemos informá-lo tanto na declaração do parâmetro como também na chamada do método. Necessariamente o uso do ref requer que a variável a ser passada como parâmetro seja inicializada antes de passar sua referência. Vejamos um exemplo. ... double gratificacao = 1000.00; pessoa.AumentarSalario(ref gratificacao); ... /// <summary> /// Aumenta o salário da pessoa conforme o valor informado. /// </summary> /// <param name="valorGratificacao">Entrada: valor do aumento de salário; Saída: novo salário com aumento</param> public void AumentarSalario(ref double valorGratificacao) { valorGratificacao = this._salario + valorGratificacao; this.Salario = valorGratificacao; } O modificador out A diferença entre o modificador out e o ref é que o out permite a passagem de uma variável não inicializada por parâmetro, o que não é permitido em parâmetros ref. Veja o exemplo a seguir. ... double novoSalario; pessoa.AumentarSalario(double.Parse(20, out novoSalario); ... /// <summary> /// Aumenta o salário conforme o percentual informado. /// </summary> /// <param name="percentualAumento">Percentual de aumento do salário.</param> /// <param name="novoSalario">Retorna o novo salário após o aumento.</param> public void AumentarSalario(double percentualAumento, out double novoSalario) { novoSalario = this._salario + (this._salario * (percentualAumento / 100)); this.Salario = novoSalario; } 29 Quando utilizamos o modificador out, a variável deve obrigatoriamente ser inicializada dentro da função que contem o parâmetro out. Herança A herança é um conceito extremamente utilizado dentro do .NET Framework. Este conceito é utilizado quando precisamos que uma determinada classe tenha todas as características de outra classe com algumas modificações em seu comportamento, ou mesmo algumas funcionalidades adicionais. No C# é possível uma classe suportar somente uma herança. Portanto, podemos especificar somente uma classe base numa classe filha. A herança múltipla é conquistada através do uso de interfaces. using System; public class ClassePai { public ClassePai() { Console.WriteLine("Construtor da Classe Pai."); } public void Imprimir () { Console.WriteLine("Eu sou a Classe Pai."); } } using System; public class ClasseFilha : ClassePai { public ClasseFilha() { Console.WriteLine("Construtor da Classe Filha."); } } public class Program { public static void Main() { ClasseFilha classeFilha = new ClasseFilha(); classeFilha. Imprimir(); Console.ReadKey(); } } Listagem 1: Herança em C#. A Listagem 1 mostra duas classes. A primeira classe é chamada ClassePai (base), a segunda é a ClasseFilha que herda a ClassePai. Veja que na assinatura da classe ClasseFilha é estabelecida a herança com a ClassePai através do “:”. public class ClasseFilha : ClassePai O exemplo da Listagem 1 representado graficamente fica assim: 30 Depois de estabelecida a herança, a classe derivada tem exatamente as mesmas capacidades da classe base. Portanto, pode-se dizer, a ClasseFilha "é" umaClassePai. Isso é demonstrado no método Main da classe Program. A ClasseFilha não tem o seu próprio método Imprimir, por isso usa o método Imprimir da ClassePai. A classe base é automaticamente instanciada antes de suas classes derivadas. Isso pode ser constatado observando o resultado da saída do exemplo da Listagem 1, onde o construtor da ClassePai é o primeiro a ser executado, e depois o construtor da ClasseFilha. Pode haver a necessidade de se criar uma nova implementação de um método existente na classe base, o que é totalmente possível de se fazer. Na Listagem 2 a classe ClasseFilha declara seu próprio método Imprimir e ainda faz uma chamada ao método Imprimir da classe base através da palavra reservada base. Observe uma pequena mudança na assinatura do método Imprimir da ClasseFilha, agora temos a palavra reservada new indicando uma nova implementação do método Imprimir da ClassePai. using System; public class ClassePai { public ClassePai() { Console.WriteLine("Construtor da Classe Pai."); } public void Imprimir() { Console.WriteLine("Eu sou a Classe Pai."); } } using System; public class ClasseFilha : ClassePai { public ClasseFilha() { Console.WriteLine("Construtor da Classe Filha."); } public new void Imprimir() { base. Imprimir(); //Invoca o método Imprimir da classe base Console.WriteLine("Eu sou a Classe Filha."); } } public class Program { 31 public static void Main() { ClasseFilha classeFilha = new ClasseFilha(); classeFilha. Imprimir(); Console.ReadKey(); } } Listagem 2: Invocando método da classe base. As classes que herdam características de outras classes podem precisar alterar propriedades ou mesmo chamar métodos que estão disponíveis na classe base, como ocorreu no exemplo da Listagem 2. Isto é perfeitamente possível caso tenhamos estes métodos ou propriedades com visibilidade public ou protected. Porém se estes métodos possuírem visibilidade private, poderão ser vistos apenas na classe base e a classe filha não poderá ter acesso a estes valores. Polimorfismo Polimorfismo é o princípio pelo qual duas ou mais classes derivadas de uma mesma classe base podem invocar métodos que tenham a mesma identificação (assinatura) mas comportamentos diferentes, especializados para cada classe derivada, usando para tanto uma referência a um objeto do tipo da classe base. Antes de verificarmos como o polimorfismo funciona sob o ponto de vista do usuário da classe, vamos ver como podemos implementar comportamentos diferentes para um mesmo métodos na classe base e na classe filha. using System; public class ClassePai { public ClassePai() { Console.WriteLine("Construtor da Classe Pai."); } public virtual void Imprimir() { Console.WriteLine("Eu sou a Classe Pai."); } } using System; public class ClasseFilha1 : ClassePai { public ClasseFilha1() { Console.WriteLine("Construtor da Classe Filha."); } public override void Imprimir() { base.Imprimir(); Console.WriteLine("Eu sou a Classe Filha."); } } using System; public class ClasseFilha2 : ClassePai { public ClasseFilha2() { Console.WriteLine("Construtor da Classe Filha 2."); } public override void Imprimir() 32 { base.Imprimir(); Console.WriteLine("Eu sou a Classe Filha 2."); } } using System; public class Program { public static void Imprimir(ClassePai classePai) { classePai.Imprimir(); } public static void Main() { Console.WriteLine("ClasseFilha 1"); ClasseFilha1 classeFilha1 = new ClasseFilha1(); Imprimir(classeFilha1); Console.WriteLine(""); Console.WriteLine("ClasseFilha 2"); ClasseFilha2 classeFilha2 = new ClasseFilha2(); Imprimir(classeFilha2); Console.ReadKey(); } } Listagem 3: Implementação do Polimorfismo. O exemplo da Listagem 3 representado graficamente fica assim: Na Listagem 3, temos a classe base ClassePai, e as classes derivadas (filhas) ClasseFilha1 e ClasseFilha2. Observe que ambas as classes derivadas reimplementaram o método Imprimir, ou seja, o método Imprimir terá comportamento diferente conforme a classe instanciada (ClasseFilha e/ou ClasseFilha2). Para permitirmos que as classes filhas possam alterar o comportamento do método Imprimir da ClassePai utilizamos o modificador virtual em sua assinatura na classe ClassePai. Até aqui nenhuma novidade, pois na Listagem 2 conseguimos reimplementar o método Imprimir sem usar o conceito de polimorfismo. Para sobrepormos um método usando o conceito de polimorfismo, na assinatura do método da classe derivada deve-se usar o modificador override como na Listagem 3. 33 A grande novidade está do método estático Imprimir da classe principal Program. Ele recebe como parâmetro a um objeto da ClassePai, mas em sua chamada dentro do método Main é passado um das classes filhas (ClasseFilha e ClasseFilha2). E no momento de execução do código, o run-time se encarrega de selecionar o método Imprimir conforme a classe filha do parâmetro. Utilize o Visual Studio em modo Debug e veja na prática o que acontece com o método Imprimir. Nada impede de utilizamos diretamente o método Imprimir das classes filhas sem passar pela classe base. Conversão entre as classes Agora que já vimos como funcionam os métodos polimórficos, vamos ver algumas regras relativas a conversão de dados de classes base para classes filhas e como esta implementação leva ao conceito de polimorfismo. Quando construímos uma instância de uma classe especializada (ex. ClasseFilha1) podemos atribuir esta instância a uma objeto o tipo da classe base (ClassePai). Isto porque a classe especializada (ClasseFilha1) suporta todas as características da classe base, e portanto pode ser armazenada em um objeto deste tipo, conforme o exemplo a seguir. ClassePai classePai = new ClassePai(); ClasseFilha1 classeFilha1 = new ClasseFilha1(); classePai = classeFilha1; Classes Abstratas Pode acontecer que ao escrever um método para uma classe base não saibamos como ele será implementado. Neste caso, a implementação será feita pela classe que herdar o método (a classe filha). Pode acontecer também que um determinado método será sobreposto com certeza na classe filha, então, não há a necessidade de sua implementação na classe base. Nestes casos definimos apenas a assinatura do método e a definição fica por conta da classe que irá herdar a classe base. Estas classes são chamadas classes abstratas, e o método não implementado é chamado de método abstrato. As classes abstratas não podem ser instanciadas através da palavra chave new, é considerada uma classe genérica. Contém métodos abstratos que devem ser implementados nas classes que derivam dela, e ainda pode conter métodos virtuais e não-abstratos (implementados). Um método abstrato não apresenta implementação na classe base. O exemplo a seguir implementa uma classe abstrata com membros abstratos e não-abstratos. Using System; public abstract class Pessoa { public abstract string Nome { get; set; } public abstract int Id { get; } public abstract void Cadastrar(); public virtual void Viajar() { /* Ação */ } 34 } O exemplo a seguir deriva a classe abstrata Pessoa do exemplo anterior,
Compartilhar