Framewoks de Desenvolvimento Frameworks de Desenvolvimento Conteúdo Unidade 1 Definição de Framework 5 Classificação dos Frameworks 7 Profissionais Envolvidos 8 Benefícios e Desafios Decorrentes da Adoção de um Framework Unidade 2 Plataforma de Desenvolvimento Java 10 11 Ambiente Eclipse Unidade 3 Introdução ao Framework .NET 18 Visão Geral 19 O que é o Framework .NET 19 Common Language Runtime 19 Biblioteca de Classes do Framework .NET 20 Dificuldades encontradas no desenvolvimento de sistemas para Windows antes do .NET 21 A abordagem .NET 24 Unidade 4 A Arquitetura .NET 25 CLR (Commom Language Runtime) 25 CTS (Common Type System) 25 CLS (Common Language Specification) 26 BCL (Base Classe Library) 26 Unidade 5 Criando Aplicações com o Visual Studio 28 Versões do Framework .NET e Visual Studio 28 Conhecendo a IDE 29 Criando um Novo Projeto 29 Componentes da IDE 31 Menu 2 13 31 Frameworks de Desenvolvimento Toolbar (Barra de Ferramentas) 32 Toolbox 32 Solution Explorer 35 Properties 36 Janela de Design 38 Janela de Código – Code View 38 Criando um Hello World 38 Criando uma Calculadora 40 Passo 1 – Ajustando o Formulário 41 Passo 2 – Posicionando os Controles 41 Passo 3 – Adicionando Funcionalidades 43 Unidade 6 Programação Orientada a Objetos Introdução 47 Por que OOP existe? 47 Conceitos de encapsulamento, herança e polimorfismo 50 Implementação prática dos conceitos 52 Herança e Agregação 56 Interfaces 63 Tratamento de exceções 69 Conversão de tipos (Typecasting) 75 Unidade 7 Segurança em Aplicações .NET 81 Utilização de Strong Names 81 A importância do StrongName 82 SQL Injection 86 Unidade 8 Acessando Dados com ADO 3 47 88 Camada Desconectada 88 Camada Conectada 88 Frameworks de Desenvolvimento ADO Object Model 89 Camada Desconectada 89 Camada Conectada 90 Contectando a um Banco de Dados 91 Criando um Banco de Dados 91 Criando um Formulário de Inserção de Dados 93 Criando uma Listagem de Dados 96 Unidade 9 Depurando Aplicações no Visual Studio 99 Erros de Sintaxe (Syntax Errors) 99 Erros em tempo de execução (runtime) 99 Erros de Semântica 99 A Classe Debug 100 Habilitando a Depuração 100 Adicionando BreakPoints 101 Executando a Aplicação em Modo de Depuração 101 Execução Passo a Passo 101 Usando a Janela de Depuração 102 Exibindo a Janela Window 102 Depuração Remota 102 Tracing 102 Classe TraceContext 103 Habilitando o Tracing em uma Aplicação Web 104 Dados do Trace 104 Categorias do Trace 104 Unidade 10 Compilando, Testando e Distribuindo Aplicações .NET ..........................106 Criando um Projeto de Instalação 4 106 Frameworks de Desenvolvimento Unidade 1 Definição de Framework Um framework captura a funcionalidade comum a várias aplicações. As aplicações devem ter algo razoavelmente grande em comum: pertencem a um mesmo domínio de problema: A definição que usamos foca quatro características principais de um framework (Orientado a Objeto): - Um framework provê uma solução para uma família de problemas semelhantes; - Usando um conjunto de classes e interfaces que mostra como decompor a família de problemas; - E como objetos dessas classes colaboram para cumprir suas responsabilidades; - O conjunto de classes deve ser flexível e extensível para permitir a construção de várias aplicações com pouco esforço, especificando apenas as particularidades de cada aplicação; Observe que um framework é uma aplicação quase completa, mas com pedaços faltando. Ao receber um framework, seu trabalho consiste em prover os pedaços que são específicos para sua aplicação. 5 Frameworks de Desenvolvimento Na prática, então, um framework é um conjunto de facilidades definidas por um conjunto de funcionalidades que interagem entre si e que é capaz de auxiliar no trabalho do desenvolvedor em determinada área de atuação desse framework. Esse conceito começou a ser utilizado para ajudar no processo de reutilização de softwares ou pedaços desses softwares. 6 Frameworks de Desenvolvimento Classificação dos Frameworks Orientados a objetos / baseados em componentes Os frameworks podem ser classificados em caixa-branca e caixa-preta [Fayad 97]. Os frameworks caixa-branca baseiam-se nos mecanismos de herança e ligação dinâmica (dynamic binding) presentes em orientação a objetos. Os recursos existentes em um framework caixa-branca são reutilizados e estendidos a partir de: herança de classes do framework e sobrecarga (overriding) de métodos "hook" pré-definidos. Métodos "hook" são definidos em interfaces ou classes abstratas e devem necessariamente ser implementados por uma aplicação. Um exemplo de método hook na linguagem Java é o actionPerformed(Action Event e) pertencente à interface ActionListener do framework AWT (Abstract Window Toolkit). Este método é chamado sempre que ocorre um evento ActionEvent correspondendo, por exemplo, a um clique de botão. Os padrões de projeto (design patterns) utilizados são o Command, o Observer e o Template Method [Gamma 94]. Frameworks caixa-preta são baseados em componentes de software. A extensão da arquitetura é feita a partir de interfaces definidas para componentes. Os recursos existentes são reutilizados e estendidos por meio de: definição de um componente adequado a uma interface específica e integração de componentes em um framework que utiliza padrões de projeto como o Strategy [Gamma 94]. Quanto ao contexto de utilização do framework há uma classificação proposta em [Fayad 97], segundo a qual os frameworks são classificados em: "Framework de Infraestrutura de Sistema", "Framework de Integração de Middleware" e "Framework de Aplicação". Frameworks de infraestrutura de sistema tratam de questões de projeto como sistemas operacionais, comunicação, interfaces gráficas e linguagens de programação. O framework AWT seria classificado como um framework de infraestrutura de sistema. Frameworks de integração de middleware são responsáveis por integrar aplicações distribuídas e componentes em uma mesma arquitetura. Exemplos de frameworks de middleware são os ORB (Object Request Broker) como o CORBA - Common ORB Architecture, e o RMI - Remote Method Invocation. Frameworks de aplicação tratam de questões de projeto de domínios de aplicação, como telecomunicações, finanças, produção e educação. O framework que se está desenvolvendo neste trabalho é um framework de aplicação. 7 Frameworks de Desenvolvimento Profissionais Envolvidos No processo de desenvolvimento de um framework deve-se produzir uma estrutura de classes com a capacidade de adaptar-se a um conjunto de aplicações diferentes. A principal característica buscada ao desenvolver um framework é a generalidade em relação aos conceitos e funcionalidades do domínio tratado. Além disso, é fundamental que a estrutura produzida seja flexível. Pode-se afirmar que o desenvolvimento de um framework é diferente do desenvolvimento de uma aplicação padrão. A distinção mais importante é que frameworks tem que cobrir todos os conceitos relevantes do domínio enquanto uma aplicação se preocupa somente com os conceitos mencionados nos requisitos da aplicação (Bosh, 1999). A complexidade em se desenvolver um framework deve-se aos seguintes fatores (Silva, 2000): • Necessidade de considerar os requisitos de um conjunto significativo de aplicações de modo a torná-lo genérico. • Necessidade de ciclos de evolução voltados a dotar a estrutura de classes do framework de alterabilidade (capacidade de alterar suas funcionalidades sem conseqüências imprevistas sobre o conjunto da estrutura) e extensibilidade (capacidade de ampliar a funcionalidade presente sem conseqüências imprevistas sobre o conjunto da estrutura). Em termos práticos, dotar um framework de generabilidade, alterabilidade e extensibilidade requerem uma cuidadosa identificação das partes que devem ser flexíveis e a seleção de soluções de projetos de modo a produzir uma arquitetura bem estruturada. Isto conduz a observação de princípios de projeto orientados a objetos. Em termos ideais, um framework deve abranger todos os conceitos gerais de um domínio de aplicação, deixando apenas aspectos particulares para serem definidos nas aplicações específicas (Silva, 2000). No contexto de desenvolvimento, a existência do framework estará sempre relacionada à existência de outros artefatos que são originadores do framework, originados a partir dele ou que exercem influência na definição das estruturas de classes. A figura mostra as várias fontes de informação que influem na definição da estrutura de um framework: artefatos de software existentes, artefatos de software produzidos a partir do framework e o conhecimento do desenvolvedor do framework (ou a equipe de desenvolvimento). As setas representam o fluxo de informações que levam à produção da estrutura de classes do framework. Como um framework geralmente não consegue ser uma construção completa de um domínio, mas sim uma descrição aproximada, é possível que a construção de aplicações sob um framework leve à obtenção de novos conhecimentos do domínio tratado, que talvez não estivessem disponíveis durante a construção do framework. Estas novas informações podem levar à necessidade de alterar o framework, causando a sua evolução como ilustra a figura: 8 Frameworks de Desenvolvimento Silva (2000) aborda que o desenvolvimento tradicional de aplicações envolve dois tipos de indivíduos: o desenvolvedor de aplicação e o usuário de aplicação (ambos podem corresponder a grupos de indivíduos com diferentes funções). Desenvolvedores devem levantar os requisitos de uma aplicação, desenvolvê-la e entregá-la aos usuários, que por sua vez interagem com a aplicação apenas através de sua interface. O desenvolvimento de frameworks introduz outro indivíduo, a saber, o desenvolvedor de framework . Nesse contexto, o papel do usuário de aplicação é o mesmo descrito acima. O papel do desenvolvedor de aplicações difere do caso anterior pela inserção do framework no processo de desenvolvimento de aplicações. Com isto, o desenvolvedor de aplicações é um usuário de um framework que deve estender e adaptar a estrutura deste framework para a produção de aplicações. De forma genérica as atividades macros são descritas a seguir: • Análise de domínio - processo de identificar e organizar o conhecimento sobre alguma classe de problemas – o domínio do problema – para dar suporte à descrição e solução destes (Arango e Prieto-Díaz, 1991). • Projeto de framework – o objetivo ao realizar essa atividade é obter um framework flexível. A atividade é árdua já que é difícil encontrar as abstrações corretas e identificar as partes estáveis e variáveis do framework. Para aumentar a extensibilidade e flexibilidade do framework padrões de projeto podem ser usados (Mattsson, 2000). • Instanciação do framework – a instanciação difere dependendo do tipo de framework: caixa branca (o usuário constrói classes a partir das classes disponíveis), caixa preta (o usuário tem que escolher uma das classes fornecidas). Um framework pode ser instanciado uma ou mais vezes dentro da mesma aplicação ou instanciado em diferentes aplicações (Mattsson,2000). 9 Frameworks de Desenvolvimento Benefícios e Desafios Decorrentes da Adoção de um Framework Os principais benefícios da utilização de frameworks de aplicação orientada a objetos são a modularidade, a reusabilidade, a extensibilidade e a inversão de controle que eles fornecem aos desenvolvedores (Fayad e outros, 1999): • Modularidade: frameworks promovem a modularidade por encapsular detalhes de implementação volátil em interfaces estáveis. A modularidade ajuda a aumentar a qualidade do software por localizar o impacto de mudanças no projeto e na implementação. Essa localização reduz o esforço requerido para entender e manter o software existente. • Reusabilidade: as interfaces estáveis fornecidas pelo framework promovem a reusabilidade por definir um componente genérico que pode ser reaplicado para criar novas aplicações. A reusabilidade influencia no conhecimento do domínio e prioriza os esforços de desenvolvedores experientes a fim de evitar recriação e revalidação de soluções comuns a requisitos recorrentes de aplicações e desafios de projeto de software. • Extensibilidade: um framework promove a extensibilidade por fornecer métodos adaptáveis (hook) explícitos (Pree, 1995). Permitindo as aplicações estenderem suas interfaces estáveis. A extensibilidade é essencial para assegurar a especialização de novos serviços da aplicação e novas características. • Inversão de controle: a arquitetura do framework é caracterizada por uma inversão de controle. Isso permite ao framework (ao invés da aplicação) determinar que conjunto de métodos específicos da aplicação invocar em resposta a eventos externos. Frameworks são considerados de grande importância para o desenvolvimento de aplicações orientadas a objetos. Eles são considerados como uma das principais formas através da qual sistemas orientados a objetos podem alcançar a reusabilidade. 10 Frameworks de Desenvolvimento Unidade 2 Plataforma de Desenvolvimento Java Plataforma Java é o nome dado ao ambiente computacional, ou plataforma, da empresa Sun Microsystems (agora Oracle). A plataforma permite desenvolver aplicativos utilizando qualquer uma das linguagens criadas para a plataforma Java, sendo a linguagem padrão a que leva seu próprio nome: Linguagem Java. Uma grande vantagem da plataforma é a de não estar presa a um único sistema operacional ou hardware, pois seus programas rodam através de uma máquina virtual que pode ser emulada em qualquer sistema que suporte a linguagem Java. O universo Java é um vasto conjunto de tecnologias, composto por três plataformas principais que foram criadas para segmentos específicos de aplicações: - Java SE (Java Platform, Standard Edition). É a base da plataforma; inclui o ambiente de execução e as bibliotecas comuns. - Java EE (Java Platform, Enterprise Edition). A edição voltada para o desenvolvimento de aplicações corporativas e para internet. - Java ME (Java Platform, Micro Edition). A edição para o desenvolvimento de aplicações para dispositivos móveis e embarcados. Além disso, podem-se destacar outras duas plataformas Java mais específicas: - Java Card. Voltada para dispositivos embarcados com limitações de processamento e armazenamento, como smart cards e o Java Ring. - JavaFX. Plataforma para desenvolvimento de aplicações multimídia em desktop/web (JavaFX Script) e dispositivos móveis (JavaFX Mobile). Tecnologias Java A plataforma Java é constituída de um grande número de tecnologias, cada uma provê uma porção distinta de todo o ambiente de desenvolvimento e execução de software. Os usuários finais, tipicamente, interagem com a máquina virtual Java (Java Virtual Machine, ou JVM) e um conjunto padrão de bibliotecas de classe. 11 Frameworks de Desenvolvimento Existe um grande número de maneiras de se utilizar uma aplicação Java, incluíndo applets embutidas em páginas web, aplicativos de uso geral em desktops, aplicativos em aparelhos celulares e em servidores de aplicações para Internet. Os desenvolvedores de aplicações em Java utilizam um conjunto de ferramentas de desenvolvimento, o JDK. 12 Frameworks de Desenvolvimento Ambiente Eclipse Eclipse é um IDE (ambiente de desenvolvimento integrado) de código aberto. Existe mais ferramentas similares de código aberto disponíveis, mas o Eclipse IDE acabou tornando-se uma ótima alternativa em virtude da quantidade de “plug-ins” disponíveis para dar suporte aos diversos tipos de tecnologias que envolvem o desenvolvimento de softwares e sistemas. Sua instalação é muito simples, podemos baixá-lo de http://www.eclipse.org em forma de arquivo ZIP e só temos que descompactá-lo na pasta onde quisermos tê-lo instalado. No site oficial, há opções para download que sugerem um desenvolvimento mais focado para uma ou outra área. Isso se deve ao fato de que essas opções de download já trazem embutidos alguns plug-ins necessários para facilitar o desenvolvimento dos projetos focados em uma ou outra área. Para executá-lo basta acionar o arquivo “Eclipse.exe”. Uma vez iniciado abrirá uma tela inicial solicitando onde queremos que o Eclipse vá salvando os projetos que criarmos: Obs.: Nessa apostila optamos por usar a versão “Eclipse IDE for Java EE Developers”, que já traz o suporte para o desenvolvimento de aplicações web. 13 Frameworks de Desenvolvimento Após escolher a área de trabalho (onde serão criados os diretórios - projetos – com seus códigos), abrirá a primeira tela da IDE, dando as “boas vindas”. Essa página inicial pode ser fechada. Atrás dela está efetivamente o ambiente de desenvolvimento. Caso seja de seu interesse, navegue pelas opções que aparecem nessa tela inicial para obter maiores informações sobre a IDE. 14 Frameworks de Desenvolvimento A primeira tela realmente significativa do Eclipse IDE possui uma série de informações organizadas em painéis. O “Package Explorer”, que está no lado esquerdo da tela, é a área onde estarão dispostas as informações básicas do seu projeto – arquivos, diretórios, plugins, etc. Os outros painéis não são tão importantes e alguns podem até ser fechados de acordo com a preferência do desenvolvedor. Todos os painéis podem ser abertos/fechados através do menu “Window”, em “Show View”. Outro painel interessante é o “Outline”, que fornece informações sobre as classes, métodos e outros recursos usados no seu projeto. Para vermos esses painéis em pleno funcionamento, criaremos um exemplo básico “Olá Mundo”, como segue: - Vá ao menu “File” e escolha “New” e dentro dessa opção “Java Project”. 15 Frameworks de Desenvolvimento - Defina um nome para seu projeto. - O Eclipse já encontra automaticamente o JDK instalado no seu computador, bastando apenas avançar para a próxima etapa. - Nessa próxima tela (que é opcional) você pode configurar plugins e outros recursos. Se isso não for do seu interesse, apenas confirme e finalize essa etapa. 16 Frameworks de Desenvolvimento - Na etapa seguinte, você deverá criar um novo arquivo, vinculado ao projeto Java que você acabou de criar. - Dê um nome (Mundo) para esse novo arquivo (classe Java), escolha se você quer que essa nova classe já venha com o código referente ao método “main” e construtores da superclasse (caso esteja herdando de outra classe). 17 Frameworks de Desenvolvimento Como todo bom software de desenvolvimento, o Eclipse traz um auxílio ao desenvolvedor quando esse está escrevendo o código, que chamamos de “auto completar”. Esse recurso permite que não seja necessário decorar todas as possibilidades de uso das funções e métodos disponíveis da linguagem. Após escrever o código, podemos visualizar o resultado no painel “Console” (nesse nosso exemplo) ou em um navegador (caso seja um projeto web) ou outro local conforme a configuração do projeto criado. Unidade 3 Introdução ao Framework .NET 18 Frameworks de Desenvolvimento Visão Geral O Framework .NET é um framework de desenvolvimento criado pela Microsoft para a criação de aplicações voltadas às plataformas Microsoft e outras. Entender o funcionamento básico do Framework .NET é essencial pois todo o desenvolvimento de aplicações utilizando esse framework se resume na utilização das classes que compõem o mesmo. Nesse capítulo veremos os componentes chaves do Framework .NET, assim como o papel que cada um desempenha. O que é o Framework .NET O framework .NET possue dois componentes chaves: O Common Language Runtime A biblioteca de classes do .NET Framework O Common Language Runtime (CLR) é o agente que gerencia as aplicações .NET em tempo de execução. Ele fornece recursos chave como gerenciamento de memória, threads, e gerenciamento de recursos. As aplicações que rodam sobre o CLR são conhecidas como aplicações de código gerenciado (managed code), todas as demais são conhecidas como aplicações de código não gerenciado (unmanaged code). As bibliotecas do Framework .NET são um conjunto de classes reutilizáveis que fornecem todas as funcionalidades que sua aplicação necessita. Essas bibliotecas permitem você desenvolver tanto aplicações Windows como aplicações ASP.NET, ou mesmo aplicações para dispositivos móveis Windows Mobile. Common Language Runtime A Common Language Runtime (CLR) é a máquina virtual do Framework .NET. Ela roda em sistemas operacionais Windows (XP, Vista, Server, etc), e sua função é executar as aplicações .NET compiladas em um formato bytecode denominado MSIL (Microsoft Intermediate Language). Durante a execução, o CLR JIT (Just-in-time ) compila o bytecode em linguagem de máquina nativo e executa a aplicação. Ou ainda, o código MSIL pode já estar precompilado em código nativo, e nesse caso o JIT não é necessário, o que aumenta o desempenho da aplicação. O CLR oferece os seguinte serviços: Gerenciamento de memória e garbage collection Gerenciamento de thread 19 Frameworks de Desenvolvimento Gerenciamento de exceções Segurança Os desenvolvedores .NET pode desenvolver aplicações usando qualquer linguagem .NET como C#, VB.NET, ou C++. O bytecode MSIL permite a criação de aplicações .NET portáveis para outras plataformas pelo fato de as mesmas só serem compiladas em linguagem de máquina na hora de sua execução. As aplicações .NET não são aplicações Win32 propriamente ditas (apesar de executarem no ambiente Windows),razão pela qual o runtime Win32 não sabe como executá-las. O Win32, ao identificar uma aplicação .NET, dispara o runtime .NET que, a partir desse momento, assume o controle da aplicação no sentido mais amplo da palavra, porque, dentre outras coisas, é ele quem vai cuidar do gerenciamento da memória via um mecanismo de gerenciamento de memória chamado Garbage Collector (GC) ou coletor de lixo, acerca do qual falaremos mais tarde. Esse gerenciamento da memória torna os programas menos susceptíveis a erros. Mais ainda, o CLR como seu próprio nome o diz, é compartilhado e, portanto, não temos um runtime para VB.NET, outro para C# etc. É o mesmo para todo mundo. Biblioteca de Classes do Framework .NET A biblioteca de classes do Framework .NET permite a você desenvolver: Aplicações console Aplicações windows Windows services Aplicações ASP.NET Web Services Aplicações Windows Communication Foundation (WCF) Aplicações Windows Presentation Foundation (WPF) Aplicações Windows Workflow Foundation (WF) As bibliotecas de classe são organizadas utilizando a hierarquia de namespaces. Por exemplo, todas as classes que executam operação de IO estão localizadas no namespace System.IO, e as classes que executam expressões regulares estão localizadas sob o namespace System.Text.RegularExpressions. As bibliotecas do Framework .NET estão divididas em duas partes: Framework Class Library (FCL) 20 Frameworks de Desenvolvimento Base Class Library (BCL) A BCL é um subconjunto é um subconjunto de toda a biblioteca e contém um conjunto de classes que fornecem funcionalidades básicas para suas aplicações. Algumas classes da BCL, estão guardadas na DLL mscorlib.dll, System.dll, e System.core.dll. A BCL está disponível em todas as linguagens suportadas pelo Framework .NET e encapsula todas funções comuns como manipulação de arquivos, acesso a banco de dados, manipulação de graficos, e documentos XML. A FCL compõem toda a biblioteca e fornece as classes para desenvolver todos os diferentes tipos de aplicações descritos acima. Dificuldades encontradas no desenvolvimento de sistemas para Windows antes do .NET Algumas das dificuldades encontradas no desenvolvimento de sistemas Windows antes do .NET são: Complexidade associada a linguagens de programação de difícil sintaxe, e ainda as dores de cabeça provocadas pelo gerenciamento da memória heap por parte do programador. Pouca integração e reaproveitamento de código entre linguagens de programação diferentes; ausência de implementação de mecanismo de herança entre linguagens diferentes. Diversidade com pouca integração na resolução de problemas complexos, dificultando a compreensão e o desenvolvimento dos sistemas. Falta de portabilidade de código executável entre plataformas diferentes. Vejamos a evolução histórica das ferramentas da Microsoft: 21 Frameworks de Desenvolvimento Apenas para ilustrar um pouco a situação atual, vamos apresentar um pequeno estudo de caso. Para simplificar o nosso problema, vamos considerar apenas as soluções Microsoft. Imaginemos uma situação hipotética na qual é solicitada uma solução de home banking que aceite requisições de um browser da Internet ou qualquer outro dispositivo como handheld, telefone celular etc.; vejamos qual seria a resposta imediata dos recursos de software que eu iria precisar: 1. Uma linguagem de programação para desenvolver as páginas dinâmicas: de cara, VBScript ou JScript. 2. Precisamos desenvolver alguns objetos COM ou COM+ no servidor, mas por questões de performance e poder de linguagem, escolhemos a linguagem C++, e claro, o compilador C++ do MS Visual Studio. 3. Vamos precisar de alguns componentes para executar no MS Queue server ou então no MS transaction server, e escolhemos a linguagem Visual Basic porque temos pessoal que já fez esse tipo de trabalho usando VB. 4. Bem, vamos falar o óbvio, mas precisamos também de Web designers com domínio de HTML, Flash, ferramentas de editoração gráfica etc. 5. Ainda temos um problema para resolver, que é o fato de termos clientes heterogêneos que não conseguem ler um formato padrão como uma Web page em HTML. Ok, agora é o momento de correr atrás do pessoal com todas essas competências, tentar gerenciar essa salada de tecnologias e linguagens de programação e, de quebra, fazer funcionar tudo direitinho. 22 Frameworks de Desenvolvimento E esta era uma situação bem comum no desenvolvimento de software até pouco tempo atrás: ter de costurar uma série de linguagens + ferramentas + tecnologias + modelos de objetos + linguagens de script vs. linguagens de programação completas + linguagens de marcação. Usando o .NET podemos proporcionar uma solução alternativa, de menor complexidade de implementação, mais integrada: 1. Uma linguagem de programação para desenvolver as páginas dinâmicas no servidor Web: C# usando o Visual Studio. 2. Uma linguagem de programação para desenvolver objetos reusáveis, armazenados em uma DLL no servidor: C# usando o Visual Studio. 3. Uma linguagem de marcação maleável o suficiente de sorte que permita mostrar o conteúdo em diversos dispositivos: XML, gerado pelo C# ou pré-montado para alguns casos. 4. Todo o trabalho de formatação e transformação dos documentos XML gerados pela solução de homebank será feito usando XSL para gerar a linguagem de marcação suportada no lado cliente. Ah! Com que linguagem vamos fazer estas transformações? Com C# é claro! Mas os nossos desenvolvedores têm um background muito forte em VB, de forma que nós descartamos o C# como alternativa”. Não tem problema, tudo o que foi dito acima continua válido, vamos mudar apenas a linguagem de C# para VB”. A plataforma .NET permite que usemos a linguagem de programação da qual mais temos domínio e mesmo assim continuamos a usufruir todo o seu potencial. O exemplo anterior foi apenas para ilustrar o contexto atual de desenvolvimento de sistemas complexos, onde temos de realmente fazer uma ginástica muito grande integrar todas as partes constituintes da nossa solução. A boa notícia é que, como mostramos no exemplo, com .NET esta situação está, digamos assim, findando esse problema, porque, como você pode ter percebido, a sua solução caiu de três linguagens de programação para apenas uma, e o resto das tecnologias que usamos (COM+, por exemplo) se integra perfeitamente com o restante da solução. Apenas falando no quesito da clareza e reutilização de código, algumas bibliotecas de classes, como MFC (Microsoft Foundation Class), surgem nesse ínterim, mas têm como foco a linguagem C/C++ e não podem ser usadas a partir do Power Builder, por exemplo, ou então Delphi, que tem a sua própria biblioteca de componentes reutilizáveis. O que equivale a dizer que essas bibliotecas não podem ser usadas a partir de qualquer linguagem de programação, o que torna o reaproveitamento de código ainda mais difícil. Mesmo tecnologias como COM e CORBA sempre apresentam os mesmos problemas de dificuldade de aprendizado por causa de sua complexidade; ou então, mesmo quando oferecem um modelo de objetos comum a ser usado por outras linguagens que não VB ou C++,acabam esbarrando no fato de que cada linguagem de programação implementa os tipos de uma forma diferente. E finalmente, quando achamos que conseguimos resolver os problemas dos tipos, somos barrados porque não conseguimos programar relações de herança entre linguagens diferentes. 23 Frameworks de Desenvolvimento Paralelamente às iniciativas da Microsoft, em 1995 surge a linguagem JAVA (na verdade, mais que uma linguagem, é uma plataforma de desenvolvimento) e, apesar de oferecer há mais de dez anos a proposta de portabilidade de código executável, (leia-se, “compile uma vez e rode em qualquer plataforma”), é “JAVA-cêntrica”, o que obriga o programador a aprender uma nova linguagem se realmente quiser usufruir os recursos que ela oferece. Mas você pode perguntar: “e .NET não nos obriga a aprender C#?” A resposta é não e saberemos mais adiante como isso é feito. A abordagem .NET Citaremos a seguir algumas das características de .NET que visam a resolver os problemas citados acima: Independência de linguagem de programação: o que permite a implementação do mecanismo de herança, controle de exceções e depuração entre linguagens de programação diferentes. Reutilização de código legado: o que implica em reaproveitamento de código escrito usando outras tecnologias como COM, COM+,ATL, DLLs e outras bibliotecas existentes. Tempo de execução compartilhado: o “runtime” de .NET é compartilhado entre as diversas linguagens que a suportam, o que quer dizer que não existe um runtime diferente para cada linguagem que implementa .NET. Sistemas auto-explicativos e controle de versões: cada peça de código .NET contém em si mesma a informação necessária e suficiente de forma que o runtime não precise procurar no registro do Windows mais informações sobre o programa que está sendo executado. O runtime encontra essas informações no próprio sistema em questão e sabe qual a versão a ser executada, sem acusar aqueles velhos conflitos de incompatibilidade ao registrar DLLs no Windows. Simplicidade na resolução de problemas complexos. 24 Frameworks de Desenvolvimento Unidade 4 A Arquitetura .NET Para melhor entendermos tudo o que temos dito até aqui, vamos falar um pouco da arquitetura de .NET e os seus principais componentes. CLR (Commom Language Runtime) O CLR, ou tempo de execução compartilhado, é o ambiente de execução das aplicações .NET. Como o leitor já deve ter atentado, as aplicações .NET não são aplicações Win32 propriamente ditas (apesar de executarem no ambiente Windows),razão pela qual o runtime Win32 não sabe como executá-las. O Win32, ao identificar uma aplicação .NET, dispara o runtime .NET que, a partir desse momento, assume o controle da aplicação no sentido mais amplo da palavra, porque, dentre outras coisas, é ele quem vai cuidar do gerenciamento da memória via um mecanismo de gerenciamento de memória chamado Garbage Collector (GC) ou coletor de lixo, acerca do qual falaremos mais tarde. Esse gerenciamento da memória torna os programas menos susceptíveis a erros. Mais ainda, o CLR como seu próprio nome o diz, é compartilhado e, portanto, não temos um runtime para VB.NET, outro para C# etc. É o mesmo para todo mundo. CTS (Common Type System) O CTS, ou Sistema Comum de Tipos, que também faz parte do CLR, define os tipos suportados por .NET e as suas características. Cada linguagem que suporta .NET tem de, necessariamente, suportar esses tipos. Apesar de que a especificação não demanda que todos os tipos definidos no CTS sejam suportados pela linguagem, esses tipos podem ser um subconjunto do CTS, ou ainda um superconjunto. No módulo dois falaremos mais a respeito dos diferentes tipos especificados no CTS. Um conjunto de classes básicas que define todos os tipos é implementado na CTS. Por exemplo: um tipo Enum deve derivar da classe System. Enum e todas as linguagens devem implementar o tipo Enum dessa forma. Todo tipo deriva da classe Object, porque em .NET tudo é um objeto e, portanto, todos os tipos devem ter como raiz essa classe. E é dessa forma que os diversos tipos nas diversas linguagens são implementados, obedecendo às regras definidas no CTS. Na .NET, e em C# conseqüentemente, todos os tipos derivam de uma raiz comum: a classe Object, o que equivale a dizer que todos os tipos são objetos, por definição. 25 Frameworks de Desenvolvimento CLS (Common Language Specification) O CLS, ou Especificação Comum da Linguagem, é um subconjunto do CTS, e define um conjunto de regras que qualquer linguagem que implemente a .NET deve seguir a fim de que o código gerado resultante da compilação de qualquer peça de software escrita na referida linguagem seja perfeitamente entendido pelo runtime .NET. Seguir essas regras é um imperativo porque, caso contrário, um dos grandes ganhos do .NET, que é a independência da linguagem de programação e a sua interoperabilidade, fica comprometido. A grosso modo, dizer que uma linguagem é compatível com o CLS significa dizer que mesmo quando esta é sintaticamente diferente de qualquer outra que implemente .NET, semanticamente ela é igual, porque na hora da compilação será gerado um código intermediário (e não código assembly dependente da arquitetura do processador) equivalente para duas peças de código iguais, porém escritas em linguagens diferentes. É importante entender esse conceito para não pensar que o código desenvolvido em C# não pode interagir com código desenvolvido em VB ou outras linguagens, porque mesmo estas sendo diferentes, todas são compatíveis com o CLS. BCL (Base Classe Library) Como era de se esperar, uma plataforma que promete facilitar o desenvolvimento de sistemas precisa ter uma biblioteca de classes básica que alavanque a simplicidade e a rapidez no desenvolvimento de sistemas. É este o objetivo da BCL (Biblioteca de Classes Base), oferecer ao desenvolvedor uma biblioteca consistente de componentes de software reutilizáveis que não apenas facilitem, mas também que acelerem o desenvolvimento de sistemas. Na BCL encontramos classes que contemplam desde um novo sistema de janelas a bibliotecas de entrada/saída, gráficos, sockets, gerenciamento da memória etc. Esta biblioteca de classes é organizada hierarquicamente em uma estrutura conhecida como namespace. Ao desenvolver um componente de software reusável, este precisa ser estruturado em um namespace para que possa ser usado a partir de um outro programa externo. A seguir mostramos uma tabela com alguns dos principais namespaces que fazem parte da BCL: 26 Frameworks de Desenvolvimento 27 Frameworks de Desenvolvimento Unidade 5 Criando Aplicações com o Visual Studio O Visual Studio 2008 é um ambiente extremamente versátil e poderoso para o desenvolvimento de aplicações .NET. Técnicamente, é possível escrever aplicações .NET usando um editor de texto simples e um compilador. Entretanto, o Visual Studio torna essa tarefa muito mais fácil e produtiva por possuir um ambiente totalmente integrado e voltado à produtividade. Ele também disponibiliza um depurador e tem suporte a IntelliSense. Temos ainda a vantagem de trabalhar com todos os dados de forma integrada dentro de um mesmo projeto, com suporte a versionamento e compilação distintas para ambientes de produção e desenvolvimento, dando assim controle no gerenciamento do ciclo de vida da aplicação. Versões do Framework .NET e Visual Studio A seguinte tabela mostra a relação entre a versão do Visual Studio e a versão do Framework que contém o mesmo: Versão do Framework Versão Visual Studio 1.0 Visual Studio .NET 2002 1.1 Visual Studio .NET 2003 2.0 Visual Studio 2005 3.0 Disponibilizado com o Windows Vista 3.5 Visual Studio 2008 C om o Visual Studio 2008 é possível desenvolver para múltiplas versões do Framework .NET: 2.0, 3.0 e 3.5. Permitindo a continuidade e migração de aplicações desenvolvidas em 28 Frameworks de Desenvolvimento versões anteriores. Sem a necessidade de migrar toda a aplicação de uma só vez. Você pode também, criar uma nova aplicação voltada a uma versão anterior do Framework, bastando para isso indicar a versão desejada ao criar um novo projeto. Conhecendo a IDE Ao iniciar o Visual Studio 2008, temos acesso a sua interface: Criando um Novo Projeto Para criar um novo projeto selecione File > New > Project Será exibida a caixa de diáligo New Project, que exibirá os templates de aplicações do Visual Studio. Um grande número de templates de projetos e itens de projeto já vem pré-instalados com o Visual Studio. Esses templates aparecem como tipos de projetos dentro do menu File > New Project. Você pode utilizar qualquer um desses templates para criar um projeto básico que criará para você um conjunto mínimo de itens que sua aplicação necessita como classes, 29 Frameworks de Desenvolvimento controles, e referências a bibliotecas do framework. Você pode usar os templates tanto para criar aplicações desktop Windows Forms, como aplicações Web. Os templates estão categorizados pela linguagem e pelo tipo de aplicação. Um nome de projeto default é apresentado (WindowsFormsApplication1) no campo Name e também: Location: a pasta onde o projeto será salvo Solution Name: o nome da solução, que por default tem o mesmo nome do projeto. Entretanto, você pode modificar o nome da solução se você quiser que seja diferente. Lembre-se que uma solução pode ter mais de um projeto. Create directory for solution: se você desmarcar essa opção, uma solução não será criada para o seu projeto. Você também pode, no canto superior esquerdo, selecionar a versão do Framework para qual você está desenvolvendo a aplicação: 30 Frameworks de Desenvolvimento Selecione a linguagem Visual C#, nos templates “Windows Forms Application” e clique em OK. Um novo projeto vazio será criado. Componentes da IDE Menu A barra de menu contém os comandos do Visual Studio como o menu File, para criação de novos projetos, abertura de projetos existentes, opção de salvar projetos formulários, etc. 31 Frameworks de Desenvolvimento É possível de ser customizado em Tools > Customize. Toolbar (Barra de Ferramentas) Contém atalhos para muitos dos comandos mais utilizados. Assim como o menu também é customizavel, bastando para isso clicar com o botão direito em qualquer toolbar existente e abrirá uma lista de barras de ferramentas disponíveis, e ao final da lista, a opção de customizar as mesmas. Toolbox A Toolbox contém todos os controles que você pode utilizar em suas aplicações. Você pode arrastar os controles da toolbox para dentro do painel de design da sua aplicação. Cada aba da toolbox contém controles agrupados para fins específicos. Você pode criar sua própria tab para armazenar seus próprios controls. Para tanto basta clicar com o botão direito na Toolbox e selecionar Add Tab. 32 Frameworks de Desenvolvimento Para adicionar controles a Toolbox, clique com o botão direiton na aba em que você quer adicioná-los e selecione a opção Choose Items. Abrirá a caixa de diálogo Choose Toolbox Itens. Você pode adicionar os seguintes tipos de controles à Toolbox: Componentes do Framework .NET 33 Frameworks de Desenvolvimento Componentes COM Componentes WPF Atividades de Workflow Você também pode clicar no botão Browse e procurar pela DLL que contém os controles que você quer adicionar a aplicação. Outra maneira de adicionar controles à Toolbox é arrastando a DLL para dentro da Toolbox. Você pode reposicionar a Toolbox arrastando e colocando ela dentro de um dos pontos de ancoramento que aparecerão na tela: Se você tiver problema de espaço em seu monitor, pode ainda, utilizar o botão Auto Hide para ganhar algum espaço na tela. 34 Frameworks de Desenvolvimento Solution Explorer Na aba Solution Explorer, temos todos os arquivos e recursos utilizados em nosso projeto. A solução pode conter um ou mais projetos. Os botões diponíveis na barra de ferramentas dessa aba são: Properties Show All Files Refresh View Code View Designer View Class Diagram Esses botões são sensíveis ao contexto, ou seja, alguns deles podem não estar visíveis dependendo do ítem que estiver selecionado. Por exemplo, ao selecionar o nome do projeto, os botões View Code e View Designer não são mostrados. Para adicionar novos itens como Formulários ou Classes ao seu projeto, clique com o botão direito sobre o nome do projeto no Solution Explorer, e selecione Add, e escolha o item a ser adicionado: 35 Frameworks de Desenvolvimento Você também pode adicionar projetos novos em File>Add New Project ou existentes em File > Existing Project. Quando você tem mais de um projeto na aplicação, um dos projetos deve ser marcado como StartUp Project. Que será o projeto que irá será depurado ao se pressionar F5. Para alterar o projeto inicial, clique com o botão direito sobre o projeto que você deseja que seja o inicial e selecione Set as Startup Project. Properties A janela de propriedades mostra as propriedades associadas ao diversos itens selecionados no projeto (Windows Forms, controles, projetos, soluções etc). A figura abaixo mostra a janela de propriedades de um objeto Windows Form. Por default as propriedades são listadas por categoria, mas você pode optar por visualizá-las em ordem 36 Frameworks de Desenvolvimento alfabética. Alternativamente, você pode escolher exibir os eventos relacionados ao objeto selecionado. Todos os valores default são exibidos em fonte normal, enquanto os valores que você alterar são exibidos em negrito. Isso é muito útil para identificar rapidamente quais atributos você mudou. Você também pode usar a Properties window para exibir eventos. Para cada evento você pode associar uma função já existente no projeto para gerencia-lo, ou então criar uma, simplesmente dê um duplo clique na propriedade em que você quer criar o evento. 37 Frameworks de Desenvolvimento Janela de Design A janela de Design permite você visualizar e desenhar a interface da sua aplicação. Dependendo do tipo de projeto que você estiver desenvolvendo, um tipo diferente de janela aparecerá para você arrastar controles para ela. Para alternar para o code-behind da aplicação, você pode tanto usar um duplo clique na interface, ou usar um clique com o botão direito sobre o Solution Explorer e selecionar o item View Code. Janela de Código – Code View Permite você visualizar e escrever a lógica da sua aplicação. Você pode alternar entre o modo de design e o código clicando nas tabs. Você pode organizar as tabs tanto horizontalmente como verticalmente, clicando com o botão direito e selecionado New Horizontal Tab Group ou New Vertical Tab Group. Criando um Hello World Vamos criar nosso primeiro aplicativo Windows Form. Caso já se encontre no Visual Studio, feche o projeto atual, se houver algum aberto, em File > Close Project. Vamos criar uma nova aplicação Windows Form, vá em File > New Project, em Project Types, selecione Visual C# > Windows, no campo Templates selecione Windows Forms Application, no campo name dê o nome de HelloWorld e clique em Ok 38 Frameworks de Desenvolvimento Na janela de Design, selecione o Form recém criado, e na janela Properties altere a propriedade Text para Hello World. Em seguida, altere a propriedade Name para frmHello. Na Toolbox, abra a aba Common Controls e arraste um controle Button para dentro do Form. Selecione o label recém criado, e na janela Properties altere a propriedade Text para “Clique aqui” e a propriedade Name para btnOla. Agora adicionaremos algumas funcionalidades ao Form. Na janela de Design, dê um duplo clique sobre o botão recém criado. Abrirá a aba de edição do code-behind. Insira a seguinte linha de comando: MessageBox.Show("Hello world!"); O código completo deve ficar como o a seguir: using using using using using using using using System; System.Collections.Generic; System.ComponentModel; System.Data; System.Drawing; System.Linq; System.Text; System.Windows.Forms; namespace HelloWorld { public partial class frmHello : Form { public frmHello() { InitializeComponent(); } private void btnOla_Click(object sender, EventArgs e) { MessageBox.Show("Hello world!"); } } } Seu formulário deve ficar como o a seguir: 39 Frameworks de Desenvolvimento Para executá-lo, vá em Debug > Start Debugging ou pressione F5 ou ainda, clique no botão Start Debugging da barra de ferramentas do Visual Studio. A aplicação deve executar corretamente, se não hover erros, abrindo o formulário, e ao clicar no botão aparecerá a seguinte mensagem: Criando uma Calculadora Nesse exemplo vamos criar uma calculadora. Ela terá dois campos para a entrada dos números a serem operados, um label para apresentação do resultado, e 5 cinco botões para execução das operações de soma, subtração, multiplicação, adição e potênciação, e também um botão para limpar os campos e resultado e um botão para fechar a calculadora. Inicie o Visual Studio, se você já estiver como ele iniciado, salve o seu trabalho e feche o projeto atual em File > Close Project. 40 Frameworks de Desenvolvimento Crie um novo Projeto em File > New Project, em Project Types selecione Visual C# > Windows, e em Templates selecione Windows Forms Application. No campo nome escreva “Calculadora” e clique Ok. Passo 1 – Ajustando o Formulário Selecione o formulário recém criado, e altere as propriedades do mesmo na janela Properties conforme a seguir: Text: Calculadora Name: frmCalculadora Passo 2 – Posicionando os Controles Arraste para o formulário apartir da Toolbox, aba Common Controls, os controles listados abaixo alterando suas propriedades conforme a seguir: Controle Label Text: Primeiro Operador Name: lblOperador1 Controle Textbox Name: txtOperador1 Controle Label Text: Segundo Operador Name: lblOperador2 Controle Texbox: Name: txtOperador2 Controle Label Name: lblResultado Text: 0 41 Frameworks de Desenvolvimento Controle Button: Text: + Name: btnSomar Controle Button: Text: Name: btnSubtrair Controle Button Text: * Name: btnMultiplicar Controle Button Text: / Name: btnDividir Controle Button Text: Pot Name: btnPotenciacao Controle Button Text: Limpar Name: btnLimpar Controle Button Text: Sair Name: btnSair Ajuste a forma, disposição e tamanho de cada controle conforme for necessário e salve seu projeto. Seu formulário deve ficar parecido com o a seguir: 42 Frameworks de Desenvolvimento Passo 3 – Adicionando Funcionalidades Agora iremos adicionar algum código C# para adicionar as funcionalidades da nossa calculadora. Primeiro criaremos três variáveis auxiliares que receberão os valores dos Operadores 1 e 2, já convertidos de String para Double, e uma variável Double para armazenar o resultado da operação. E finalmente uma função que fará a conversão dos valores nos campos txtOperador1 e txtOperador2 para double. Clique em uma área sem controles do formulário com o botão direito, e selecione View Code. Dentro do código C# do formulário, vamos inserir o código abaixo: Primeiro vamos adicionar o código para cáculo da soma. Clique duas vezes sobre o botão “+” e adicione o código abaixo ao evento Click do mesmo: using using using using using 43 System; System.Collections.Generic; System.ComponentModel; System.Data; System.Drawing; Frameworks de Desenvolvimento using System.Linq; using System.Text; using System.Windows.Forms; namespace Calculadora { public partial class frmCalculadora : Form { double op1; double op2; double resultado; public void converteEntradas() { op1 = 0; op2 = 0; if (txtOperador1.Text != "") { Double.TryParse(txtOperador1.Text, out op1); } if (txtOperador2.Text != "") { Double.TryParse(txtOperador2.Text, out op2); } } public frmCalculadora() { InitializeComponent(); } } } Agora adicionaremos o código ao evento click do botão somar. Para isso, alterne para a aba Form1.cs [Design] e clique duas vezes sobre o botão +, adicione o seguinte código: private void btnSomar_Click(object sender, EventArgs e) { converteEntradas(); resultado = op1 + op2; lblResultado.Text = resultado.ToString(); } Alterne novamente para o modo de Design e dê um duplo clique no botão “-“: 44 Frameworks de Desenvolvimento private void btnSubtrair_Click(object sender, EventArgs e) { converteEntradas(); resultado = op1 - op2; lblResultado.Text = resultado.ToString(); } Observe que esse procedimento é necessário, pois ao fazer isso o Visual Studio, automaticamente liga a função sendo criada ao evento click do controle. O que pode ser verificado na janela de propriedades, ao se clicar no botão Events Da mesma forma para os demais botões com o código abaixo: private void btnMultiplicar_Click(object sender, EventArgs e) { converteEntradas(); resultado = op1 * op2; lblResultado.Text = resultado.ToString(); } private void btnDividir_Click(object sender, EventArgs e) { converteEntradas(); resultado = op1 / op2; lblResultado.Text = resultado.ToString(); } private void btnPotenciacao_Click(object sender, EventArgs e) { converteEntradas(); resultado = Math.Pow(op1, op2); lblResultado.Text = resultado.ToString(); } private void btnLimpar_Click(object sender, EventArgs e) { txtOperador1.Text = ""; 45 Frameworks de Desenvolvimento txtOperador2.Text = ""; } private void btnSair_Click(object sender, EventArgs e) { Application.Exit(); } Salve o projeto e execute a aplicação pressionando F5. 46 Frameworks de Desenvolvimento Unidade 6 Programação Orientada a Objetos Introdução A programação orientada a objetos (OOP) veio para ficar, sem dúvida nenhuma. Ela permite que sistemas complexos sejam desenvolvidos com mais facilidade, tanto na implementação inicial quanto na manutenção. O produto mais popular do Visual Studio até hoje tem sido o Visual Basic. Porém, reclamava-se muito da ausência de um suporte mais completo a todos os requisitos que caracterizavam uma linguagem de programação orientada a objetos (OOP). Com a arquitetura .NET, a Microsoft parece ter resolvido atender ao clamor da comunidade de desenvolvedores atacando em todos os flancos. Vários melhoramentos foram feitos no Visual Basic, de forma que ele pode ser considerado agora como orientado a objetos; mas é em C#, sem dúvida alguma, que se terá acesso a uma linguagem de programação que implementa, de maneira simples e direta (sem as complicações do C++), todos os requisitos de uma linguagem OOP em conjunto com uma forte tipagem de dados (um dos pontos fracos do VB). Com isso, a programação torna-se mais sólida e muitos erros podem ser eliminados ainda em tempo de compilação. Por que OOP existe? Antes de continuarmos, vamos voltar um pouco no tempo e entender de onde vem a idéia por trás da programação orientada a objetos. O conceito predominante de programação antes de OOP era a chamada programação procedural. Consistia basicamente em dividir a tarefa de programação em pequenos blocos de código chamados de procedimentos (procedures, em inglês), também conhecidos na época como sub-rotinas. Em todos os casos, o que se fazia, basicamente, era escrever um trecho de código que manipulasse os valores de algumas variáveis e desse algum tipo de retorno. Exemplificando (código em português): x = 0 Enquanto x < 10 47 Frameworks de Desenvolvimento x = x + 1 Fim Enquanto O exemplo acima é muito simples, mas bastante ilustrativo. Mostra que existem dados (variáveis) e códigos que manipulam esses dados (estruturas de controle). Qual o inconveniente disso? Supondo que x fosse um valor que tivesse de ser exibido na tela, seria necessário acrescentar algum código que fizesse isso. Ou seja, x não era “capaz” de se “autoexibir” na tela. O código completo seria: x = 0 Enquanto x < 10 x = x + 1 Fim Enquanto PosicionarCursor 0,0 Imprimir x Se fosse feita outra alteração no valor de x, você teria de executar novamente os comandos de impressão para que o valor fosse atualizado na tela. O ponto a que queremos chegar é que dados e códigos eram concebidos como elementos separados. Havia, inclusive, uma definição que dizia: dados + código = programa. Com a OOP, uma das idéias básicas era eliminar essa distância entre dados e código e fazer com que ambos ficassem mais interligados. Ambos seriam capazes de interagir de forma mais homogênea e autônoma. Primeiramente, expandiu-se a idéia de procedimento para a idéia de classe. Uma classe permite que vários procedimentos e dados sejam armazenados dentro dela. Os procedimentos passaram a chamar-se métodos e os dados passaram a chamar-se propriedades. Mas não foi uma mera maquiagem e uma mudança de nome para a mesma coisa. De fato, o conceito de classe mudou radicalmente a visão da programação. Uma classe define como um objeto deve funcionar. Fazendo uma analogia clássica, é como o projeto de uma casa: estabelece como as coisas têm de ser. A partir dessa planta, podem ser construídas várias casas idênticas. Isso é chamado de instância em OOP. Quando dizemos instanciar uma classe, significa colocar “no ar” um objeto baseado na descrição da classe. Vamos usar o próprio Windows como referência. Uma das coisas mais comuns na interface do Windows é o botão OK. Você provavelmente o verá na grande maioria das janelas que abrir. Observe que ao clicar com o mouse sobre aquele botão, ele produz um efeito de forma que parece realmente ter sido pressionado. 48 Frameworks de Desenvolvimento Porém, não existem apenas botões OK. Você encontra botões dos mais variados tipos nas mais diversas janelas. Na figura anterior, você nota que existem três botões idênticos, cada qual com um título diferente. Internamente, a propriedade que define o rótulo do botão é chamada de Text. Assim como existe Text, existem diversas outras propriedades. Uma delas é Color, que define a cor do botão; outra define se o botão está pressionado ou não. Resumindo, existem propriedades que definem diversos aspectos do botão. O grande detalhe é que, ao alterar uma propriedade, obtém-se um efeito imediato. Exemplificando em forma de código (fictício): BotaoOk.Pressionado = Verdadeiro Ou BotaoOk.Pressionar Ambos os códigos produzem um efeito imediato. Observe aqui duas coisas: primeiro, que a propriedade ou método vêm sempre atrelados a um objeto. Segundo, que se parece muito com a atribuição de uma variável no primeiro caso ou lembra meramente a execução de algum procedimento no segundo código. Você poderia também ter os códigos inversos: BotãoOk.Pressionado = Falso ou BotãoOk.Liberar A grande mágica em OOP é que propriedades podem disparar a execução de métodos e métodos podem alterar os valores de propriedades sem que você precise fazer nada para que isso aconteça ao manipular o objeto. Isso faz com que os objetos tenham vida própria. Os acadêmicos gostam de dizer que a OOP faz com que você programe “moldando” objetos muito parecidos com a realidade. Bom, acho isso um exagero. Mas concordo que os resultados que você vê são mais concretos, embora a programação por trás seja ainda mais abstrata do que a procedural. Em relação a programação procedural e orientada a objetos, é importante salientar que ambas não são inimigas e que a OOP não veio para aniquilar ninguém. Pelo contrário, a OOP abraça e expande o conceito de programação procedural para horizontes ainda mais amplos. 49 Frameworks de Desenvolvimento Conceitos de encapsulamento, herança e polimorfismo OK, objetos têm propriedades que podem ser manipuladas e gerar resultados visíveis e imediatos. Então vamos primeiramente entender a mecânica de funcionamento dos objetos. Como já dissemos, ao alterar o valor de uma propriedade, códigos são executados de forma a produzir um resultado visível. No exemplo do botão, qualquer modificação nas propriedades Color ou Text produzirão efeitos imediatos. Porém, ao usar um objeto, você não “vê” o código nem precisa conhecê-lo para criar um botão. Isto é chamado de encapsulamento.Os códigos usados para alterar cores, títulos, forma e aspecto do botão ficam escondidos na implementação da classe. Também era possível encapsular funcionalidade dentro da programação procedural através da criação das chamadas bibliotecas de funções. Mas em OOP, esse conceito vai um pouco além. Vamos entender por quê. Suponha que você não está satisfeito e gostaria de criar botões redondos como os que você encontra no painel do Media Player do Windows, por exemplo. Você precisa conhecer mais dois conceitos para poder atingir esse resultado: Herança e Polimorfismo. Herança significa partir de algo já pronto e modificá-lo com o propósito de deixá-lo mais adequado a determinada finalidade. Supondo que você tem uma classe botão com propriedades básicas; você pode herdar aquelas características básicas e adicionar ainda mais funcionalidade.O detalhe é que, ao “derivar” uma classe a partir de outra, você não precisa conhecer o código da classe anterior. A sua nova classe não trará os códigos da classe pai. Eles continuarão encapsulados lá e você poderá usá-los se quiser.O dado importante é que você não precisa manipular os códigos da classe pai. Como a intenção seria criar um botão com características de funcionamento diferentes, muitas das propriedades anteriores passariam a funcionar de modo diferente. Observe à esquerda do painel do Media Player que existem botões com os títulos de “Now Playing”, “Media Guide”, “CD Audio” etc. É de se imaginar que exista uma propriedade Text para aquele tipo de botão também. Ou seja, vamos assumir neste caso que existe a propriedade Text para todos os botões do Windows. Mas por que alguns Texts são diferentes dos outros? Isso se chama Polimorfismo. Ou seja, cada novo “descendente” de uma classe pode entender a propriedade Text de uma forma diferente e aplicar seus próprios códigos de modo a redefinir seu comportamento. Resumindo, Herança, Polimorfismo e Encapsulamento são os três mecanismos básicos que uma linguagem deve comportar para ser considerada inteiramente orientada a objetos. 50 Frameworks de Desenvolvimento Orientação a eventos Um tópico que não deve ser confundido com orientação a objetos é a orientação a eventos. Uma linguagem de programação pode ser orientada a objetos sem ser orientada a eventos e vice-versa. A união desses dois conceitos, entretanto, produz resultados muito mais interessantes que os dois separadamente. Como o próprio nome sugere, uma linguagem orientada a eventos é uma linguagem capaz de responder a determinados “acontecimentos” dentro de um determinado ambiente. Os eventos podem ser muitos: o clique do mouse, uma tecla pressionada, uma informação que chega pela placa de rede etc. Em ambientes gráficos, isso tem uma importância essencial. O mouse, sem dúvida alguma, foi o dispositivo que mais causou reviravolta no mundo da programação orientada a eventos. No tempo das telas de texto, muito comuns no ambiente DOS, mainframes e versões mais antigas de Unix, o principal dispositivo de entrada de dados era o teclado. Vejamos um exemplo de programa com interface de texto: Numa tela como essa, se o usuário quisesse pular de um campo para o outro, teria de usar o teclado e necessariamente passar pelos campos dentro de uma determinada seqüência.Como advento do mouse, passou a ser possível que o usuário se posicionasse em qualquer campo e houve uma “quebra” de estratégia. Nos tempos da tela de texto, era o programador quem dava as cartas e estabelecia a seqüência de operação de um programa. Hoje as coisas são diferentes, o programa tem de estar preparado para responder a seqüência de eventos que o usuário achar mais conveniente. Os sistemas operacionais também se tornaram multitarefa e várias ações podem estar acontecendo ao mesmo tempo. Programar com orientação a objetos combinada com orientação a eventos significa criar métodos que serão acionados quando determinadas situações ocorrerem. Geralmente, esses métodos têm nomes como Obj_Click (Obj_Clicar), Obj_KeyPress (Obj_PressionarTecla), assim facilitando sua identificação. Resumindo, o que 51 Frameworks de Desenvolvimento fazemos é posicionar estrategicamente um punhado de códigos de programação em pontos do programa que serão disparados em determinadas circunstâncias. A sensação que se tem é de que a programação ficou mais “solta” e mais “descontrolada”, mas ocorre exatamente o oposto. Implementação prática dos conceitos Vejamos agora, em termos de código, o que pode ser feito para criar classes em C#. Conforme já foi visto no módulo 2, C# é uma linguagem inteiramente orientada a objetos, de forma que tudo deve ser implementado dentro de classes. Não existem variáveis ou procedimentos “soltos”. Apenas para relembrar, o esqueleto básico de uma classe deverá ter mais ou menos o seguinte aspecto: escopo class NomeClasse { // Propriedades escopo tipo nome; escopo tipo nome; // Construtores escopo NomeClasse { // Especificações } // Métodos escopo tipo NomeMétodo { // Especificações } } 52 Frameworks de Desenvolvimento Evidentemente, esse exemplo está muito simplificado, mas traz a idéia básica: você tem de planejar quais serão as propriedades, os métodos, os construtores, destrutores e seus escopos e tipos. Exemplo: public class Cadastro { // Propriedades public string CPF; public string NomeCliente; // Construtores public Cadastro( ) { MessageBox.Show( "Eu sou o construtor 'default'!" ); } public Cadastro( string fCPF, string fNome ) { // Comandos de inicialização this.CPF = fCPF; this.NomeCliente = fNome; MessageBox.Show( "Eu sou um construtor customizado e recebi " + fCPF + " e " + fNome + " como parâmetros." ); } // Métodos public bool Gravou( ) 53 Frameworks de Desenvolvimento { // Código para gravação return true; } } Em C#, a regra para definir um construtor é muito simples: basta criar um método cujo nome seja idêntico ao da classe. Toda vez que uma instância da classe for criada, o construtor será automaticamente disparado. Observe também que, no exemplo anterior, foram criados dois construtores para a mesma classe. Isso, em OOP, é chamado de sobrecarga (overload) de método. A palavra sobrecarga pode trazer a idéia de estresse ou excesso de trabalho, mas nesse caso é um elemento que nos ajuda e muito! Quando um método é “sobrecarregado”, o editor do Visual Studio dá uma indicação clara disso. Vamos tomar como exemplo o MessageBox. Ao digitar MessageBox.Show, o editor mostra que existem doze alternativas diferentes para o método Show: O que diferencia um do outro é a combinação do número de parâmetros, seus nomes e tipos. Isso é conhecido como assinatura do método. O compilador reconhecerá automaticamente qual assinatura você está usando. No caso da nossa classe, podemos escolher qual construtor será usado fornecendo a assinatura correta: { // Construtor 'default' é chamado Cadastro Ficha1 = new Cadastro( ); // Construtor otimizado é chamado Cadastro Ficha2 = new Cadastro( "123", "De Oliveira Quatro" ); } 54 Frameworks de Desenvolvimento Um detalhe importante é que criamos duas instâncias da mesma classe e ambas jamais se misturam. Outro detalhe é que o chamado construtor default, o qual não recebe nenhum parâmetro, existe em todas as classes. Porém, se você criar um novo construtor customizado com parâmetros, o construtor default é silenciosamente removido. No nosso exemplo, a reescrita do construtor default se tornou obrigatória a partir do momento em que quisemos usar a inicialização padrão de uma classe. Vamos agora entender os escopos (visibilidade) de uma classe e de seus membros. Uma propriedade (ou campo) ou método podem ser public, protected, private ou internal. Em C#, eles são chamados de modificadores de acesso. Veja os significados: Quando você cria um objeto, muitos de seus elementos são visíveis e outros estão escondidos. Fazendo uma comparação com o mundo real, quando usamos um carro, muitos de seus elementos (ou propriedades) são facilmente acessíveis enquanto outros permanecem mais bem-guardados. A velocidade, o nível do combustível, o câmbio, todos esses elementos são visíveis externamente. Outros permanecem mais escondidos e são usados indiretamente: a injeção eletrônica, as válvulas, os eixos de tração etc. Criar uma classe passa pela mesma idéia. Na verdade, a criação de uma classe lida com dois alvos: existe uma parte pensada no programador que usará a classe em seu sistema e outra pensada no usuário, que normalmente verá apenas a “superfície” do objeto criado. Veremos o uso desses modificadores com exemplos práticos e isso se tornará mais claro. 55 Frameworks de Desenvolvimento Herança e Agregação O trabalho de desenvolvimento de um único sistema pode resultar na criação e/ou utilização de dezenas ou mesmo centenas de classes. Muitas dessas classes têm funcionalidades muito distintas, mas outras se cruzam e se complementam. Por causa desses eventuais “cruzamentos” de classes, é fundamentalmente necessário entender o modo como elas se relacionam. Assim como na vida real com as mais diversas pessoas, duas classes podem se relacionar baseada no “ser” e no “ter”. No caso do “ser”, diz-se que uma classe foi derivada de outra e ambas têm uma relação de parentesco. Pelo outro lado, o “ter” especifica que uma classe incorporou outra e ambas têm uma relação de amizade. Vamos tomar um exemplo da vida real: como se constrói um computador? Existem muitas peças que formam uma máquina “completa” hoje em dia: processador, HD, monitor etc. Observe que a Intel produz novos processadores a partir dos preexistentes. A partir disso, nota-se uma linha genealógica: 8086, 286, 386, 486, Pentium, Pentium II e por aí vai. Cada novo processador herda as características da geração anterior e agrega novos recursos e mais potência. Então podemos dizer que temos uma classe onde cada novo processador é filho da geração anterior. Conclusão: todos eles SÃO processadores.O relacionamento entre eles é baseado em “ser”. Já um computador seria uma classe que agrega várias outras. Assim como o exemplo do processador, HDs, Impressora e tantos outros periféricos também têm sua linha evolutiva. A gente diz que um computador TEM memória, disco rígido etc. Ou seja, trata-se de uma classe que incorpora diversas outras.O relacionamento do computador com os demais elementos é do tipo “ter”. Fácil de entender na teoria, não é? Pois vejamos agora um pouco de código: class Processador { public double Megahertz; public int AnoFabricacao; } class Intel386 : Processador { public Intel386( ) { 56 Frameworks de Desenvolvimento Megahertz = 33.3; AnoFabricacao = 1993; } } class DiscoRigido { public int CapacidadeGB; } class Computador { Intel386 MeuProcessador; DiscoRigido MeuHD; public Computador( ) { MeuHD.CapacidadeGB = 70; } } No exemplo anterior, Intel386 é uma classe derivada a partir de Processador. DiscoRigido é uma classe independente e Computador também. Na classe Computador, são agregadas as classes DiscoRigido e Processador. Criação de Propriedades Vamos explorar agora um pouco mais as possibilidades de criação de propriedades. Os exemplos anteriores foram muito simples. Uma propriedade, é preciso entender em primeiro lugar, se parece muito com uma simples variável, mas é bem mais do que isso. Uma 57 Frameworks de Desenvolvimento propriedade (também chamada de “atributo”, dependendo da forma como foi definida) é um valor ao qual é associado um método. Toda vez que for lido ou gravado o valor da propriedade, métodos podem entrar em ação. Essas ações são definidas pelas palavras get e set.Na prática, pode- se dizer que uma propriedade é composta de três elementos: um campo (local onde armazena-se o valor, também chamado de atributo), um método de leitura (get) e um método de gravação (set). Veja o exemplo da nossa classe Cadastro reescrita dentro dessas premissas: class Cadastro2 { protected string fCPF; public string CPF { set { fCPF = value; } get { return fCPF; } } } Como dissemos anteriormente, temos três elementos. As variáveis fCPF e fNome são os campos (fields) onde os valores são efetivamente armazenados. Observe que se trata de variáveis do tipo protected, o que significa que só serão vistas pelas classes descendentes (lembre-se do conceito de “ser”) e não serão manipuladas por outras classes (lembre-se do conceito de “ter”). O set e o get, como você pode notar, servem para recuperar e gravar novos valores. A palavra value é reservada no C# para receber o valor passado para a propriedade. Veja o exemplo: 58 Frameworks de Desenvolvimento InstanciaCadastro2.CPF = "123"; // Dispara o "set" x = InstanciaCadastro2.CPF; // dispara o "get" Para testar este código, sugerimos que você crie um projeto de teste bem simples, apenas um formulário com um botão e escreva o código dentro do evento click desse botão. Talvez você esteja se perguntando agora quem foi o maluco que resolveu complicar tanto a programação. Mas o fato é que isso, acredite ou não, torna as coisas mais fáceis. Se você tivesse de fazer com que um CPF fosse imediatamente validado ao ser atribuído, por exemplo, ficaria muito mais fácil dessa forma. Ou seja, assim que o valor fosse informado, a própria classe seria capaz de identificá-lo como válido ou não. No nosso estudo de caso, desenvolveremos um exemplo baseado nessa idéia.Uma das coisas que você pode fazer com uma propriedade e não consegue com uma variável, por exemplo, é torná-la somente leitura ou somente gravação. No caso de ter uma propriedade somente leitura, implemente apenas o método get. Se implementar apenas o set, ela será apenas de escrita. No caso de não ser uma propriedade “full” e apenas um campo (atributo), a forma seria: public readonly string CampoSomenteLeitura Métodos polimórficos Vamos criar uma classe pensando em um método cuja implementação seria diferente na classe pai e na classe filha. Ou seja, a implementação da classe filha será ligeiramente diferente e melhorada em relação à versão pai. Nesse caso, toda vez que temos um método que se modifica da classe pai para filho, esse método é chamado de virtual. Em OOP, um método virtual se caracteriza pela capacidade do compilador de detectar mudanças de acordo com o contexto da classe e viabilizar o polimorfismo. Em bom português: é o método camaleão. Vejamos este trecho de código para clarear um pouco mais: class PrimeiraGeracao 59 Frameworks de Desenvolvimento { public virtual void Mensagem( string msg ) { msg += " Primeira Geração "; MessageBox.Show( msg ); } } class SegundaGeracao : PrimeiraGeracao { public override void Mensagem( string msg ) { msg += " Segunda Geração "; base.Mensagem( msg ); } } class TerceiraGeracao : SegundaGeracao { public override void Mensagem( string msg ) { msg += " Terceira Geração "; base.Mensagem( msg ); } } 60 Frameworks de Desenvolvimento Observe que na primeira geração, o método Mensagem é definido como virtual. Nas duas gerações posteriores, ele é definido como override. Dentro de cada método override, observe a presença da palavra-chave base. Ela é essencial para permitir que o polimorfismo aconteça. Sua finalidade é acionar a codificação escrita na classe pai (o nível imediatamente anterior). Apenas a Primeira Geração gera uma saída na tela. As outras classes apenas agregam mais informação à mensagem final. A terceira passa para segunda, que passa para a primeira, que efetivamente imprime. Exemplo de utilização: TerceiraGeracao teste = new TerceiraGeracao( ); teste.Mensagem( "Mecanismo de Herança: " ); A grande vantagem por trás disso tudo é que ao gerar uma nova versão do método, você não precisa necessariamente conhecer o código da classe pai para fazer a nova implementação. Se quiser, pode até mesmo descartar o que foi escrito anteriormente, basta não incluir a chamada através da palavra- chave base. Existe também uma outra palavra-chave bastante importante nesse processo de herança e polimorfismo. Essa palavra é this. A palavra this refere-se sempre ao contexto atual. Na grande maioria dos casos, a palavra this pode ser omitida. Mas quando existem coincidências entre nomes de variáveis ou parâmetros com campos ou propriedades de uma classe, é conveniente usar this: class ContextoPai { public string Nome; // this.Nome refere-se a este item public ContextoPai( string Nome ) { this.Nome = Nome; } } Apenas para traçar um paralelo, o this em C# seria idêntico ao this em C++ e Java, idêntico ao me em Visual Basic e idêntico ao self em Delphi. 61 Frameworks de Desenvolvimento Um pouco de Concretismo e Abstracionismo Bom, vamos dar alguns exemplos mais avançados de como criar métodos em classes. Uma das coisas que mais massacram as almas dos programadores estabanados, quando eles se deparam com OOP, é a necessidade primordial de planejamento. Muita gente simplesmente senta na cadeira e começa a escrever, gerando um código que muitas vezes é complicado de entender e dar manutenção. Para quem vai desenvolver classes, um mínimo de planejamento é absolutamente indispensável no início. É como jogar xadrez. Se você não consegue pensar nos lances seguintes, o xeque-mate (e neste caso o seu rei será o sacrificado) pode aparecer de uma hora para outra. Baseado nessas premissas, não é exagero dizer que a OOP requer um nível muito alto de abstração por parte do programador. Um nível de abstração tão alto que ele pode simplesmente criar uma classe completamente vazia, oca, apenas para definir como será o seu formato final ou de seus descendentes. O C# suporta abstração em classes através de dois mecanismos: criação de classes e métodos abstratos e interfaces. Uma classe abstrata, como o próprio nome sugere, não traz uma implementação concreta. Ela serve para dar forma a um projeto de classe. Veja o código: abstract class EntradaDeDados { public abstract string Conteudo( ); public abstract string Exibir( ); } Normalmente, toda entrada de dados terá um conteúdo, o qual pode eventualmente ser exibido. Isso é uma premissa. Criar uma classe abstrata significa definir premissas. Nada é codificado concretamente na primeira geração da classe. As classes filhas (através de override) é que definirão a funcionalidade de cada método ou propriedade. Se você tentar instanciar uma classe abstrata, obterá um erro do compilador: EntradaDeDados x = new EntradaDeDados( ); _ Cannot create an instance of the abstract class or interface 'EntradaDeDados' 62 Frameworks de Desenvolvimento Na classe filha, você teria as implementações: class EntradaDeOcorrencias : EntradaDeDados { public override string Conteudo( ) 81 { // Implementação return ""; } public override string Exibir( ) { // Implementação return ""; } } Interfaces O recurso de criação de interfaces segue mais ou menos o mesmo princípio dos métodos abstratos: apenas define o formato de uma classe sem implementar seu código. Porém, há uma característica que torna as interfaces bastante diferentes de simples classes abstratas: elas fornecem subsídio para a criação de novas classes numa forma mais parecida com a montagem de um quebra-cabeça. O C# não suporta herança múltipla. Isso significa que uma classe filha só pode ter um única classe como pai. Interfaces permitem criar algo parecido com herança múltipla em C++. Por definição, em OOP, Interfaces são coleções de métodos abstratos relacionados semanticamente. Em bom português: peças de um quebra-cabeça que podem ser encaixadas conforme a conveniência de quem estiver jogando... Observe que, para efeito de padronização, é comum identificar os nomes das interfaces com I maiúsculo no início. Vamos a um exemplo: public interface ICodigoBarras 63 Frameworks de Desenvolvimento { void LeituraPadrao3of9( ); // Implicitamente abstrato void LeituraPadraoEAN13( ); void MetodoIdentico( ); } public interface IControleArmazenamento { void Localizador( ); void Categorizacao( ); void MetodoIdentico( ); } public class Produto : ICodigoBarras, IControleArmazenamento { // Aqui viriam as implementações public void LeituraPadrao3of9( ) { // Implementação } public void LeituraPadraoEAN13( ) { // Implementação } public void Localizador( ) { // Implementação } 64 Frameworks de Desenvolvimento public void Categorizacao( ) { // Implementação } // No caso de nomes coincidentes, implemente apenas uma vez public void MetodoIdentico( ) { // Implementação } } A definição da classe produto seria resultante da agregação de métodos definidos em duas interfaces diferentes. Você também pode agregar outros detalhes pertinentes à classe produto. Também pode criar novas classes baseadas em uma classe pai combinada com várias interfaces: public class Produto : Cadastro, ICodigoBarras, IcontroleArmazenamento Selando as classes Para selar este assunto de criação de classes com chave de ouro, a linguagem C# permite criar classes “seladas”. Por definição, classes seladas não permitem nenhum tipo de herança ou derivação. Simplesmente são classes que devem ser entendidas como terminadas. Exemplo de código de classe selada:, sealed class FimDePapo { public string UltimaPalavra; } 65 Frameworks de Desenvolvimento class AindaQueroMaisPapo : FimDePapo { // Erro de compilação: // cannot inherit from sealed class 'FimDePapo' } Ferramentas de apoio Se você se empolgar com a criação de objetos e de repente se vir às voltas com mil e uma classes, existem algumas ferramentas no editor do Visual Studio que tornarão a sua vida mais fácil. Para ter uma visão global dos objetos criados dentro da sua aplicação, clique com o botão direito do mouse dentro do editor e selecione “Synchronize Class View”. Você verá no canto superior direito da sua tela (essa posição pode variar) uma janela com todas as definições de classes, mais ou menos com o seguinte formato: 66 Frameworks de Desenvolvimento Ao clicar duas vezes sobre um método, propriedade ou classe, o editor posicionará o cursor dentro do código correspondente. Uma vez dentro do código, se não estiver interessado em visualizar o código de classes que considera que já esteja corretamente implementados, você pode usar os sinais de + e – que ficam no canto da tela e “contrair” ou “expandir” os códigos das classes. Veja o exemplo com a classe Processador contraída, exibida na Figura 3.7. Tempo de vida dos objetos Até agora, temos falado de criar objetos, mas também é igualmente importante saber como e quando destruí-los. Vimos que para criar uma nova instância de uma classe basta usar a palavra new: x = new ClasseExemplo( ) O próprio runtime de .NET vai se encarregar de destruir o objeto quando ele não for mais necessário. A pergunta é: como ele sabe que um objeto não é mais necessário? Resposta simples e incompleta: o runtime libera a memória alocada pelo objeto quando não existe mais nenhuma referência ligada ao objeto dentro do contexto corrente. Exemplo: public static int Main( ) { QualquerClasse x = new QualquerClasse( ); ... Return 0; } // se x é a única referência para "QualquerClasse" // ela pode ser destruída quando termina seu escopo 67 Frameworks de Desenvolvimento Existem diversos aspectos referentes a Garbage Collection (coleta de lixo) que estão fora do escopo deste curso. Vamos nos deter em mostrar como programar a destruição de um objeto. É muito comum necessitarmos que determinados processos ocorram quando um objeto é finalizado. Os exemplos são muitos: liberação de recursos alocados como impressora ou porta serial, fechamento de arquivos etc. Assim como temos em C# os construtores, também temos os destrutores. Lembre-se de que a regra para a criação de um construtor é muito simples: basta criar um método com o mesmo nome da classe. No caso do destrutor, basta também criar um método com o mesmo nome da classe, mas precedido por um til (~). Exemplo: class Teste { ~Teste( ) { // Aqui você escreve o código de liberação de recursos // Base.Finalize( ) é chamado automaticamente em C# 68 Frameworks de Desenvolvimento } } Evidentemente, se você achar conveniente que a liberação de recursos seja feita em algum momento que não necessariamente seja o da destruição do objeto, você é inteiramente livre para criar algum método que faça isso. Tratamento de exceções O tradicional termo tratamento de erros é agora chamado de tratamento de exceções. E veremos que faz mais sentido. Em C#, o tratamento de exceções é feito de maneira muito elegante e simples.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 idéia é válida para todo o ambiente .NET e foi batizada como SEH (Structured Exception Handling). A idéia básica é mais ou menos a seguinte: todo objeto que representa uma exceção é derivado de System.Exception. Essa classe possui os seguintes membros: Exceções podem ser disparadas pelos mais diversos elementos. Podem ser disparadas a partir de erros do sistema operacional, de erros de dispositivos, de inconsistências de dados. Também podemos criar nossas próprias exceções. Vamos observar um exemplo de como disparar uma exceção e como tratá-la com a nossa classe de Cadastramento e validação do CPF: 69 Frameworks de Desenvolvimento public class Cadastro { // Propriedades public string CPF; public string NomeCliente; public Cadastro( string fCPF, string fNome ) { // Inicialização CPF = fCPF; NomeCliente = fNome; if ( CPF.Length != 11 ) { // THROW é a palavra chave para gerar a exceção throw new Exception( "CPF Inválido" ); } } } Colocamos uma validação no construtor, de forma que se forem passados menos de 11 caracteres, o CPF é automaticamente considerado incorreto. Ao instanciar essa classe com um parâmetro inválido, a classe irá disparar a exceção e o resultado na tela deverá ser uma tela similar a esta: 70 Frameworks de Desenvolvimento Observe que um dos detalhes importantes está na sentença “unhandled exception”. Traduzindo: exceção não-tratada. A exceção foi disparada pela palavra chave throw e é preciso que haja um bloco de código preparado para manipular a classe de erro retornada.O formato de tratamento de uma exceção é o seguinte: try { // Código sujeito a exceções } catch( TipoExcecao1 e ) { // Tratamento para exceção tipo 1 } catch( TipoExcecao2 e ) { // Tratamento para exceção tipo 2 } catch { // Tratamento para qualquer tipo de exceção } 71 Frameworks de Desenvolvimento finally { // Trecho que deve sempre ser executado, havendo ou não exceção } A estrutura try..catch requer que exista pelo menos um bloco catch vazio. Os blocos de finally e catch(exception) são opcionais. Nossa classe Cadastro deveria ser instanciada da seguinte forma: try { Cadastro teste = new Cadastro( "123", "De Oliveira Quatro" ); } catch { MessageBox.Show( "Impossível instanciar o objeto Cadastro" ); } Como você deve ter observado no exemplo, entretanto, não existe uma pista clara de qual foi o motivo que realmente fez a instanciação do objeto. Para termos um controle mais efetivo, poderíamos usar o catch com uma exceção customizada. Vamos criar uma exceção que será usada especificamente para o caso de encontrar um CPF inválido e colocar isso dentro de um contexto com mais outras possibilidades de erro. O esboço do código ficaria desta forma: // Criação das classes public class eCPFInvalido : Exception{ } public class Cadastro { // Campos public string CPF; 72 Frameworks de Desenvolvimento public string NomeCliente; public Cadastro( string fCPF, string fNome ) { // Inicialização NomeCliente = fNome; if ( CPF.Length != 11 ) { throw new eCPFInvalido( ); } else { CPF = fCPF; } } } // Código que irá testar o tratamento de exceções // Insira-o dentro do evento click de um botão, // por exemplo try { int x; int z = 0; Cadastro teste = new Cadastro( "123", "De Oliveira Quatro" ); x = 3 / z; // Teste pôr esta linha antes da anterior } catch( eCPFInvalido Erro ) // Experimente inverter a ordem dos catches 73 Frameworks de Desenvolvimento { MessageBox.Show( "CPF Inválido" ); } catch( Exception Erro ) { MessageBox.Show( "Impossível instanciar o objeto Cadastro: " + Erro.Message ); } finally { MessageBox.Show( "Sempre executado, haja erro ou não." ); } Observe uma série de detalhes pertinentes ao exemplo anterior: O catch( Exception Erro) é o trecho que captura toda e qualquer exceção. Ele é praticamente igual a um catch vazio conforme demonstrado antes, por uma razão muito simples: Exception é pai de todas as exceções. Qualquer exceção sempre se encaixará em Exception por causa disso. A razão pela qual usamos Exception em vez de um catch vazio foi a intenção de exibir a mensagem enviada pelo sistema na ocorrência do erro. O bloco finally é executado sempre, haja erro ou não. Neste caso, ele é executado DEPOIS do bloco catch que capturar a exceção. Se você tem o costume de programar em Delphi, vai notar que essa ordem é invertida em C#. Qualquer erro ocorrido dentro um bloco try desvia o fluxo de execução para o catch correspondente e NÃO retorna o fluxo de execução para o ponto que originou o erro ou a linha seguinte. Em outras palavras, não existe nada semelhante ao Resume, tão popular no VB, por exemplo. A ordem dos fatores altera (e muito) os resultados. Se você tentar, por exemplo, as inversões sugeridas pelos comentários no exemplo anterior, vai perceber que, no caso de alterar a ordem dos catches, você receberá um erro do compilador: “A previous catch clause already catches all exceptions of this or a super type (‘System.Exception’)” (Uma cláusula catch anterior já captura todas as exceções deste tipo ou de um tipo superior “System.Exception”). No caso de inverter as linhas de código, você vai fazer com que diferentes catches sejam executados. 74 Frameworks de Desenvolvimento Apenas um– e somente um – bloco catch é executado para cada tratamento de exceção (try). No nosso estudo de caso, também daremos seguimento a este assunto e desenvolveremos controles de exceções mais sofisticados. Conversão de tipos (Typecasting) Quase todas as linguagens de programação possuem recursos para compatibilização de dados. No caso da OOP, isso é um tópico que requer um pouco mais de cuidados do que uma simples conversão de números em strings e vice-versa. Em muitos casos, um objeto pode ser passado como parâmetro e um método pode estar habilitado a lidar com classes bastante diferentes. Primeiro vamos examinar em um sistema de herança linear, baseado no conceito de “ser”: Eis aqui agora um diagrama demonstrando o conceito de “ter”: 75 Frameworks de Desenvolvimento A pergunta-chave para explicar o tópico de type cast de objetos seria: como posso criar um método que seja capaz de receber um objeto como parâmetro, não importa qual seja e, sabendo que alguns desses objetos têm o método “gravar” e outros não, por exemplo, como fazer para que meu código seja inteligente o suficiente para saber com qual objeto está lidando e realizar a ação da maneira mais adequada? Ok, vamos por partes. Seja qual for o caso, teremos de fazer uso de dois operadores de typecast: is e as.O operador is permite que você verifique se o objeto é de um tipo específico e retorna verdadeiro ou falso, de acordo com o resultado do teste. Exemplo: if ( p is Intel286 ) { // instruções } Já o operador as permite usar uma classe como se ela fosse outra. Evidentemente, esse tipo de “personalidade trocada” pode dar confusão e deve ser usada com cuidado. Como regra geral, podemos dizer que quanto mais próximo for o parentesco entre duas classes, maiores as chances de ter um typecast correto. Quanto mais distante, maior a possibilidade de erros no seu typecast. Mas lembre- se que parentes próximos também podem brigar, portanto é mais do que conveniente planejar bem um typecast! Veja um exemplo: (p as Intel286).ModoProtegido = true; Nesse caso, estamos fazendo um typecast no objeto p para usá-lo como um processador Intel286. Se o typecast falhar, um objeto com valor nulo será retornado e uma exceção será gerada. Vamos analisar os dois casos ilustrados no início deste tópico. O primeiro caso seria mais simples, por se tratar de herança linear pura e simples. Bastaria criar um método que recebesse como parâmetro a classe mais superior, no caso, a classe Processador. Veja o código: class Processador { public double Megahertz; 76 Frameworks de Desenvolvimento public int AnoFabricacao; } class Intel8086 : Processador { public Intel8086( ) { Megahertz = 4.77; AnoFabricacao = 1981; } } class Intel286 : Intel8086 { public bool ModoProtegido = false; public Intel286( ) { Megahertz = 20.0; AnoFabricacao = 1984; } } Observe que a partir da geração do 286, foi adicionado um novo campo indicando que o processador dispõe de um recurso chamado “Modo Protegido” e cujo valor padrão é false. Caso queiramos ativar o modo protegido de um processador através de uma classe externa, poderíamos escrever uma classe com o seguinte formato: class VerificarModoProtegido { public void AtivarModoProtegido( Processador p ) 77 Frameworks de Desenvolvimento { if ( p is Intel286 ) { // observe o type cast nesta linha: (p as Intel286).ModoProtegido = true; MessageBox.Show( "Modo protegido foi ativado" ); } if ( p is Intel8086 ) { MessageBox.Show( "Modo protegido não disponível" ); } } } Vamos agora escrever um código que acionaria tudo de vez. Observe que esse código possui um “truque”: Intel8086 p8086 = new Intel8086( ); Intel386 p386 = new Intel386( ); VerificarModoProtegido Modo = new VerificarModoProtegido( ); // É fácil prever a saída desta linha: Modo.AtivarModoProtegido( p8086 ); // Mas o que você acha que vai acontecer nesta aqui? Modo.AtivarModoProtegido( p386 ); A primeira linha, obviamente, responderá que o processador não possui modo protegido. Porém, já que nossa classe prevê apenas processadores 8086 e 286, como ela irá se comportar recebendo como parâmetro um 386? Resposta: ela acionará ambos os “ifs”. Ou seja, ela 78 Frameworks de Desenvolvimento ativará o modo protegido, informará que o mesmo foi ativado e logo em seguida dirá que o processador não possui modo protegido! Nossa classe anterior, portanto, teria de ser reescrita e ficaria mais consistente desta forma: class VerificarModoProtegido { public void AtivarModoProtegido( Processador p ) { if ( p is Intel286 ) { // observe o type cast nesta linha: (p as Intel286).ModoProtegido = true; MessageBox.Show( "Modo protegido foi desativado" ); } else if ( p is Intel8086 ) { MessageBox.Show( "Modo protegido não disponível" ); } } } Você conseguiu enxergar a diferença? Todo e qualquer detalhe em programação pode ser crucial! É muito fácil deixar “bugs” no código... Agora que sabemos como tratar objetos em herança linear, como proceder em relação a objetos que não necessariamente têm “parentesco” entre si? Eis aqui o grande detalhe: todos os objetos têm parentesco, sim. Todos eles são filhos de System.Object. Não precisa fazer exame de DNA, pode acreditar nisso! Veja este exemplo de código: 79 Frameworks de Desenvolvimento public void QualquerObjeto( System.Object o ) { // Diferentes comparações e typecasts podem ser usados } Resumindo, o mesmo raciocínio aplicado a herança linear também se aplica a objetos não lineares; afinal, todos os objetos são descendentes de um mesmo ramo. Basta que o parâmetro que receberá os objetos seja definido como System.Object. Resumo Neste módulo você foi exposto a uma série de conceitos e técnicas. Dominar a Programação Orientada a Objetos dentro dos atuais ambientes de desenvolvimento é absolutamente essencial. Você aprendeu todos os conceitos essenciais de OOP (Polimorfismo, Encapsulamento, Herança) e viu de que forma eles são implementados e como se aplicam em C#. Aprendeu também a diferença entre orientação a eventos e orientação a objetos. Vimos também como construir classes, métodos e propriedades. Outras informações importantes como tratamento de exceções, interfaces, e type casting completaram o repertório básico de informações sobre OOP de que você precisa para programar de maneira consistente nessa tecnologia. 80 Frameworks de Desenvolvimento Unidade 7 Segurança em Aplicações .NET O Common Language Runtime e o Framework .NET fornecem muitas classes e serviços que permitem aos programadores escrever código para aplicações seguras. Esses componentes permitem aos administradores customizar o acesso que a aplicação terá a recursos protegidos. O framework também fornece classes e serviços que facilitam o uso de criptografia e segurança por definição de papéis de usuário. Utilização de Strong Names Strong Name é uma tecnologia baseada em criptografia introduzida pela plataforma .NET e traz consigo muitas possibilidades para as aplicações .NET. Uma delas é o aumento da segurança no desenvolvimento de aplicações. Imagine que você precise criar um conjunto de componentes que gerenciam a área de recursos humanos de uma determinada empresa. Entre esses componentes, vamos ter um chamado Salario, que dado o nome do funcionário, valor base do salário e a quantidade de horas extras que o funcionário realizou no mês, retorná o valor líquido que a empresa deverá pagar a ele. Para ilustrar isso, temos um projeto do tipo Class Library, chamado de RegrasDeNegocio. Dentro deste projeto, criamos uma classe chamada Salario, que possuirá apenas o método chamado de Calcular, que receberá os parâmetros mencionados acima. Traduzindo tudo isso em código, teremos algo como: public class Salario { public decimal Calcular(string nomeDoFuncionario, decimal valorBase, decimal horasExtras) { return valorBase + horasExtras; } } Note que não há nenhuma complexidade envolvida para não desviarmos o foco. Como sabemos, este projeto dará origem à uma DLL, que poderá ser utilizada por várias aplicações que rodam dentro da empresa. Como já era de se esperar, vamos referenciá-la em um projeto chamado AplicacaoDeRH, que utilizará esse componente recém criado para calcular os salários de seus respectivos funcionários. Com isso, o código de consumo na aplicação final 81 Frameworks de Desenvolvimento é algo parecido com isso: Console.WriteLine(new Salario().Calcular("Julio Cesar", 1000.00M, 250.00M)); Dá mesma forma que antes, vamos manter a simplicidade aqui. Depois deste software (EXE + DLL) instalado na máquina do responsável pelo RH da empresa, eu chego até o diretório físico onde ele está instalado. Copio esses dois arquivos para minha máquina para começar a analisar como esse componente e aplicação foram desenvolvidos. Como sabemos, dentro de qualquer assembly .NET, temos apenas código IL, que é um código ainda decompilável. Podemos utilizar o Reflector para isso e, consequentemente, visualizar tudo o que eles possuem (classes, métodos, parâmetros, etc.). A partir de agora, vamos explorar a vulnerabilidade. Como eu conheço toda a estrutura que o componente (DLL) tem, nada impede de eu criar um projeto no Visual Studio .NET, com o mesmo nome, com os mesmos tipos e os mesmos parâmetros. Com isso, eu vou manipular o corpo do método Calcular, verificando se o funcionário que está sendo calculado o salário sou eu, e se for, multiplico a quantidade de horas extras por 2, para que eu possa ganhar um valor maior do que realmente deveria. public class Salario { public decimal Calcular(string nomeDoFuncionario, decimal valorBase, decimal horasExtras) { if (nomeDoFuncionario == "Julio Cesar") horasExtras *= 2; return valorBase + horasExtras; } } Depois de compilado, isso dará origem à uma - nova - DLL com o mesmo nome. De alguma forma, eu chego até o computador do responsável pelo RH da empresa, e substituo fisicamente a DLL anterior por essa DLL que acabamos de desenvolver, e que viola a regra de negócio, pois manipula o resultado para beneficiar um funcionário específico. Com isso, quando o responsável pelo RH for calcular o meu salário, ele me pagará duas vezes o valor das minhas horas extras. Se ele confia cegamente no software que dá o resultado, estou sendo beneficiado, a empresa prejudicada e dificilmente alguém encontrará o problema. A importância do StrongName Todo e qualquer assembly possui algumas características que ajudam ao runtime determinar a sua identidade. A identidade de qualquer assembly .NET é composta por quatro 82 Frameworks de Desenvolvimento informações, a saber: nome, versão, cultura e uma chave pública. O nome nada mais é que o nome do assembly, desconsiderando a sua extensão. A versão é o número em que o projeto se encontra, e que por padrão é 1.0. Já a cultura determina se o assembly é sensitivo à alguma cultura e, finalmente, a chave pública, qual falaremos mais adiante. Podemos utilizar várias ferramentas para conseguir visualizar essas informações. No nosso caso, se abrirmos dentro do Reflector o componente que criamos inicialmente, aquele que possui o código legal, poderemos comprovar essas características que ajudam a identificar o assembly: RegrasDeNegocio, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null Quando criamos a assembly ilegal, com o código que multiplica a quantidade de horas extras por dois, poderemos notar que o resultado será idêntico, ou seja, não há nada que diferencie a identidade dos dois assemblies. Sendo assim, a substituição física é o suficiente para comprometer a regra de cálculo de salário, já que para a aplicação que a consome, acaba sendo o mesmo componente. Se repararmos melhor, entre as quatro informações que compõem a identidade do assembly, uma delas é chamada de PublicKeyToken, e que está nula (não definida). É justamente nela que está a solução para o nosso problema. Dentro do .NET Framework existem dois tipos de assemblies: os que são fracamente nomeados e os que são fortemente nomeados. Aqueles que são fracamente nomeados (que é o padrão para todos os projetos .NET), são estruturalmente idênticos, possuindo o mesmo formato e os mesmos tipos definidos, mas como está, dá margem para esse tipo de problema. Isso acontece porque quando efetuamos a referência do componente RegrasDeNegocio.dll na aplicação AplicacaoDeRH.exe, o .NET injeta no manifesto da AplicacaoDeRH, informações pertinentes ao assembly de regras de negócio. Se analisarmos o manifesto do EXE, veremos a seguinte informação: 83 Frameworks de Desenvolvimento Na imagem acima, podemos verificar que dentro da AplicacaoDeRH há uma referência para o componente RegrasDeNegocio, que inclui o nome e a versão dele. Como até então o componente ilegal possui a mesma identidade, a aplicação consome ele sem maiores problemas, não sabendo que se trata de um componente malicioso. Para resolver o nosso problema, vamos recorrer aos assemblies fortemente nomeados (strong names). A única e principal diferença em relações aos assemblies fracamente nomeados, é a atribuição de uma chave única, que não se repetirá em nenhum lugar do mundo. A Microsoft criou esse recurso, desde a primeira versão do .NET Framework, que utiliza um par de chaves (pública e privada) para garantir a unicidade do assembly. Antes de ver como elas funcionam, vamos primeiramente entender como gerá-la. O .NET Framework fornece uma ferramenta chamada SN.exe, que é responsável por gerar e manipular essas chaves. Para gerar a nossa chave, podemos executar o seguinte comando (através do prompt do Visual Studio .NET): SN -k MinhasChaves.snk O arquivo gerado possui as duas chaves, a pública e a privada e você pode nomear o arquivo da forma que quiser. Com o par de chaves gerado, podemos criar um segundo arquivo para acomodar somente a chave pública, mas em um primeiro momento, isso é desnecessário. A ideia aqui é somente visualizarmos o que temos como chave pública: SN -p MinhasChaves.snk MinhaChavePublica.snk Para visualizarmos a chave pública, podemos utilizar o seguinte comando: SN -tp MinhaChavePublica.snk Public key is 00240000048000009400000006020000002400005253413100040000010001 00f1589e575d9c20 cc36a0fb7245d74c8d69ddc26a0c92ebee5e65dba7c94a6583701176cc5a8f d795e11d7e366c49 a19f3ae28509fa8961e6eca103353fe98168a402dc35001b98d9d5325f6121 bde11bc698f268a3 e7e338b950b565be26e371c2550dfaee54f9ef8993dc476f60b2ab5ad69d5a e832ddd7e35e43ad 6daafae2 Public key token is 0b8510fcd7fd739a Só que o arquivo snk por si só não funciona. Você precisa vinculá-lo ao assembly qual deseja assinar. Para isso, você pode abrir o arquivo AssemblyInfo.cs, e adicionar o atributo AssemblyKeyFileAttribute, que recebe o caminho físico até o arquivo que contém o par de chaves, e que no nosso exemplo é MinhasChaves.snk. [assembly: AssemblyKeyFile(@"C:\MinhasChaves.snk")] Ao compilar o assembly com a chave vinculada, veremos que a identidade do assembly já mudará. Ao vincular a chave criada acima no nosso componente RegrasDeNegocio, a identidade irá aparecer da seguinte forma: 84 Frameworks de Desenvolvimento RegrasDeNegocio, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0b8510fcd7fd739a A única e essencial diferença é que agora a propriedade PublicKeyToken reflete exatamente a nossa chave pública, que está contida no arquivo MinhasChaves.snk vinculado ao componente. A partir de agora, as aplicações que referenciarem o componente RegrasDeNegocio, guardarão além do nome e versão do mesmo, a chave pública que o identifica. Depois dessas alterações, se visualizarmos o manifesto da aplicação AplicacaoDeRH, teremos o seguinte resultado: Com isso, qualquer pessoa maliciosa que tente refazer o assembly, por mais que ela se atente a criar toda a estrutura de tipos e métodos, definir o mesmo nome de assembly, e ainda, assinar com um outro strong name, ela jamais conseguirá reproduzir a mesma identidade e, consequentemente, não conseguirá mais alterar o componente que está instalado no cliente. É importante dizer que fisicamente, a substituição ainda poderá ocorrer, mas quando a aplicacação AplicacaoDeRH tentar acessar algum recurso do assembly RegrasDeNegocio, uma exceção será disparada, informando que o assembly solicitado não corresponde aquele que foi inicialmente referenciado. Observação: Toda essa segurança pode continuar vulnerável se você deixar o arquivo com a chave privada em mãos erradas. Se a pessoa maliciosa conseguir ter acesso a esse arquivo, ela irá gerar o assembly idêntico como ela já fazia, mas ao invés de criar um novo par de chaves para assinar o assembly, ela utilizará o mesmo que você utilizou para assinar o seu, que é o verdadeiro, e com isso todo o problema volta a acontecer. Para finalizar, vamos entender como todo esse mecanismo funciona e como o runtime do .NET Framework assegura isso. Quando geramos a chave a partir do utilitário SN.exe, um par de chaves é adicionado no arquivo MinhasChaves.snk. Quando compilamos o projeto com esse arquivo vinculado a ele, o .NET gera um hash do componente utilizando o algoritmo SHA1 e assina esse hash com a chave privada. O resultado deste processo é adicionado no próprio assembly, incluindo também a sua chave pública, que está matematicamente relacionada à chave privada. A imagem abaixo ilustra esse processo: 85 Frameworks de Desenvolvimento Como vimos acima, quando o componente é referenciado na aplicação que o utiliza, a chave pública também é adicionada à aplicação. Durante a execução, o .NET Framework irá aplicar o mesmo algoritmo de hash no conteúdo do componente (DLL) e dará origem à um novo hash e a chave pública embutida na aplicação que consome aquele componente, será utilizada para extrair o conteúdo (já "hasheado") que está embutido na DLL do componente. Para determinar se a DLL é a mesma ou não, o resultado do hash deve ser igual, do contrário, a DLL foi substituída e, felizmente, uma exceção será disparada, evitando assim de consumir um componente ilegal. A imagem abaixo ilustra esse processo: Ao assinar uma aplicação/componente com um strong name, podemos tirar proveito de várias funcionalidades, como por exemplo, o refinamento de segurança, a instalação no GAC, que por sua vez possibilita a centralização, execução lado a lado de múltiplas versões de um mesmo componente, etc. Mas um dos principais benefícios fornecidos por ele, é a unicidade do componente, evitando que alguém consiga reproduzí-lo e, consequentemente, colocar em risco a execução e a confiabilidade das aplicações que a consomem. SQL Injection A Injeção de SQL, mais conhecida através do termo americano SQL Injection, é um tipo de ameaça de segurança que se aproveita de falhas em sistemas que interagem com bases de dados via SQL. A injeção de SQL ocorre quando o atacante consegue inserir uma série de instruções SQL dentro de uma consulta (query) através da manipulação das entrada de dados de uma aplicação. Para nos prevenirmos contra esse tipo de ataque, o ASP.NET fornece o objeto SqlCommand, junto com a estrutura SqlParameter. Juntos, eles fazem a validação e ajustes nos dados que o usuário envia e que venham a compor o comando SQL. Exemplo de utilização: 86 Frameworks de Desenvolvimento // cria um novo comando SQL SqlCommand cmd = new SqlCommand("SELECT * FROM usuario WHERE usuario=@usuario AND senha=@senha", conn); // cria os parametros do comando SqlParameter[] parametro = new SqlParameter[2]; parametro[0] = new SqlParameter("usuario", txtUsuario.Text); parametro[1] = new SqlParameter("senha", txtSenha.Text); // substitui parametros no comando cmd.Parameters.AddRange(parametro); 87 Frameworks de Desenvolvimento Unidade 8 Acessando Dados com ADO O ADO.NET é um conjunto de classes que você pode utilizar para manipular dados, e especificamente desenhado para gerenciar conexões em um ambiente desconectado. Além de ser uma boa opção para aplicações web, o ADO.NET utiliza o formato XML para transmissão de dados entre a aplicação e o banco de dados. É composto de duas camadas, uma conectada e outra desconectada. Camada Desconectada A camada desconectada consiste em um número de classes para armazenamento de dados e relações. A classe DataSet é a classe principal de aramazenamento de dados em cachê de memória. Contém uma coleção ou mais de objetos DataTable que são compostos de linhas e colunas de dados, chave primária, chave estrangeira, constraints, e informações sobre os relacionamentos dos dados nos objetos DataTable. Você pode imaginar a classe DataSet como uma representação em memória do banco de dados. Camada Conectada A camada conectada é responsável pelo tráfego de dados entre a aplicação e a fonte de dados. A camada conectada é específica para cada banco de dados, como o SQL Server ou arquivo XML. A camada conectada é também conhecida como provedora de dados e o ADO.NET já vem com alguns provedores de dados prontos para uso, incluindo: SQL Server .NET Framework Data Provider. Para acesso a bases SQL Server 7.0 e versões posteriores. OLE DB .NET Framework Data Provider. Para acessar qualquer fonte OLE DB que você tenha um provedor OLE DB. ODBC .NET Framework Data Provider. Para acessar qualquer fonte ODBC para qual você prossua um driver ODBC. Um grande número de terceiros,incluindo IBM e ORACLE, fornecem implementações de Data Providers para o Framework .NET. 88 Frameworks de Desenvolvimento ADO Object Model Camada Desconectada A camada desconectada é independente da fonte de dados é pode ser usada para gerenciamento da informação em memória. Em função disso, pode ser usada com múltiplas fontes de dados, ou dados XML, ou para gerenciar dados locais da aplicação. Opcionalmente, você pode conectar a alguma fonte de dados usando a camada conectada. É composta das seguintes classes, encontradas no namespace System.Data: Tipo Descrição Constraint Representa uma restrição que rege um ou mais objetos DataColumn. Uma constraint é uma regra que é usada para manter a integridade dos dados em uma tabela. A classe abstrata Constraint tem duas classes derivadas que são usadas para chaves primárias e/ou colunas de valores únicos utilizando a classe UniqueConstraint. A classe base abstrata Constraint também contem a ação a ser executada sobre os dados de uma tabela filha quando os dados de uma tabela paid é apagada usando a classe ForeignKeyConstraint. DataColumn Representa uma simples coluna de um DataTable, múltiplos objetos DataColumn representam a estrutura de um DataTable. DataRelation Representa uma relação pai/filho entre dois objetos DataTable. Um objeto DataRelation é unido a um numero igual de objetos DataColumn de dois objetos DataTable diferentes. O bjeto DataRelation permite a navegação entre tabelas relacionadas. DataRow Representa um registro de dado em um objeto DataTable. DataSet Esse é o container geral de objetos que representa um objeto database na memória,consistindo de um ou mais objetos DataTable e zero ou mais objetos DataRelation. Esse classe é serializavel. DataTable Representa uma tabela de dados em memória. A estrutura é composta de um ou mais objetos DataColumn, e a informação é salva em objetos DataRow. Essa classe é serializavel e pode ser transportada via rede. 89 Frameworks de Desenvolvimento DataTableReader Obtem o conteúdo de um mais objetos DataTable na forma de um ou mais read-only, forward-only result sets. DataView Representa uma visão customizada de um DataTable, que pode ser utilizado para ordenar, filtrar, procurar, editar, e navegar. Camada Conectada A camada conectada, fornece um link entre a fonte de dados e a camada desconectada. Os tipos básicos, encontrados no namespace System.Data.Common estão listados abaixo. A camada conectada inclui as classes DataAdapter, Connection, Command e DataReader. Que são Data Providers desenhados para manipulação de dados e para rápida acessos leitura de dados, forward-only, read-only.O objeto Commandpermite acesso a comandos do banco de dados, permite retornar dados ao banco, modificar informação, executar stored procedures, e enviar ou receber parâmetros. A classe DataReader fornece um stream de dados de alta performance. E a classe DataAdapter fornece a ponte entre o objeto DataSet e a fonte de dados. A classe DataAdapter utiliza o objeto Command para executar comandos SQL na fonte de dados tanto para caregar o DataSet como para reconciliar as mudanças que foram feitas no objeto DataSet, de volta para banco. DBCommand Representa um comando SQL ou stored procedure a ser executada no banco de dados. Esse tipo é normalmente dividido em quatro diferente grupos, um para seleção de dados, um para inserção, um para atualização e um quarto para exclusão. DbConnection Representa a conexão à fonte de dados e se conecta a ela a partir de uma connection string. DbDataAdapter Representa uma conexão a um banco de dados e a um conjunto de comandos SQL que são usados para preencher um DataSet e/ou um DataTable, e atualizar a fonte de dados.Esse objeto é um mediador entre cliente e servidor, e usa um objeto de conexão e comando para executar suas tarefas. DbDataReader Lê um stream de registros de um data source (forward-only, readonly). Os tipos listados são tipos abstratos e precisam ser herdados. Você precisa utilizar os seguintes tipos para implementações específicas de banco de dados: 90 Frameworks de Desenvolvimento Para uso com SQL Server .NET Data Provider, você precisa das classes SqlDataAdapter, SqlDataReader, SqlCommand, e SqlConnection, encontrados no namespace System.Data.SqlClient. Para uso com OLE DB .NET Data Provider, você tem OleDbDataAdapter, OleDbDataReader, OleDbCommand, e OleDbConnection, todas sob o namespace System.Data.OleDb. Para uso com ODBC .NET Data Provider, você tem OdbcDataAdapter, OdbcDataReader, OdbcCommand e OdbcConnection, todas sob o namespace System.Data.Odbc. Para uso com Oracle .NET Data Provider, você em OracleDataAdapter, OracleDataReader, OracleCommand, and OracleConnection, todas sob o namespace System.Data.OracleClient. Você pode utilizar essas classes para os bancos Oracle versão 8.1.7 ou mais recentes. Observação: você não pode misturar os tipos da camada conectada. Todos os tipos utilizados para gerenciar dados em uma determinada fonte, devem vir do mesmo .NET Data Provider. Contectando a um Banco de Dados Criando um Banco de Dados Crie um novo projeto em File > New Web Site, selecione o template ASP.NET Web Site, de o nome de WebSite3, utiliza a linguagen “Visual C#”, e clique em Ok. No Solution Explorer, sobre a pasta App_Data, clique em “Add New Item...” , selecione “Sql Server Database”, utilize o nome Database.mdf, e clique em Add. Na aba Server Explorer ( View >Server Explorer ), abra expanda o ramo Database.mdf e clique com o botão direito sobre o item Tables: 91 Frameworks de Desenvolvimento Clique com o botão direito, selecione “Add New Table”, e insira a seguinte definição: Clique com o botão direito sobre a linha id, e selecione a opção Set Primary Key, e na aba Properties marque a opção IsIdentity com Yes: 92 Frameworks de Desenvolvimento Clique em File > Save Table, nomeie a tabela como Contatos e clique Ok. Criando um Formulário de Inserção de Dados Na aba Solution Explorer, clique sobre a página Default.aspx e abra para edição no modo Source. Criaremos um formulário de entrada de dados para os campos nome, email, telefone, e um botão salvar que executará a entrada dos dados. <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Inserção de Dados</title> </head> <body> <form id="form1" runat="server"> <div> <table style="width: 100%;"> <tr> <td>Nome: </td> <td> <asp:TextBox ID="txtNome" runat="server"></asp:TextBox> </td> 93 Frameworks de Desenvolvimento </tr> <tr> <td> EMail: </td> <td> <asp:TextBox ID="txtEMail" runat="server"></asp:TextBox> </td> </tr> <tr> <td> Telefone: </td> <td> <asp:TextBox ID="txtTelefone" runat="server"></asp:TextBox> </td> </tr> <tr> <td> </td> <td> <asp:Button ID="btnSalvar" runat="server" Text="Salvar" /> </td> </tr> </table> </div> </form> </body> </html> No modo de Design, clique duas vezes sobre o botão Salvar do formulário e inclua os namespaces na página: using System.Data.SqlClient; O código para inserção: 94 Frameworks de Desenvolvimento using using using using using using using using using using using using System; System.Configuration; System.Data; System.Linq; System.Web; System.Web.Security; System.Web.UI; System.Web.UI.HtmlControls; System.Web.UI.WebControls; System.Web.UI.WebControls.WebParts; System.Xml.Linq; System.Data.SqlClient; public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void btnSalvar_Click(object sender, EventArgs e) { // inicializa conexão ao banco // C:\sites\WebSite3\App_Data\Database.mdf // altere conforme a localização da sua aplicação SqlConnection conn = new SqlConnection(@"Data Source=.\SQLEXPRESS;AttachDbFilename=C:\sites\WebSite3\App_Dat a\Database.mdf;Integrated Security=True;User Instance=True"); conn.Open(); // cria um novo comando SQL SqlCommand cmd = new SqlCommand( "INSERT INTO Contatos (nome, email, telefone) VALUES ( @nome, @email, @telefone )", conn); // cria os parametros do comando SqlParameter[] parametro = new SqlParameter[3]; parametro[0] = new SqlParameter("nome", txtNome.Text); parametro[1] = new SqlParameter("email", txtEMail.Text); parametro[2] = new SqlParameter("telefone", txtTelefone.Text); // substitui parametros no comando cmd.Parameters.AddRange(parametro); // executa comando SQL no banco cmd.ExecuteNonQuery(); // fecha conexao 95 Frameworks de Desenvolvimento conn.Close(); Response.Redirect("Default.aspx"); } } Salve e execute a página em Debug > Start Debugging, e insira alguns registros. OBSERVAÇÃO: para obter a string de conexão do banco, na aba Server Explorer, clique com o botão direito sobre o banco de dados, selecione Properties, e copie a string que se encontra na propriedade Connection String. Criando uma Listagem de Dados Para a listagem utilizaremos o controle de dados DataGrid, que recebe um DataReader como fonte de dados. No solution Explorer, sobre a localização da aplicação, clique com o botão direito e selecione a opção “Add New Item”, template Web Form, nomeie o mesmo como listagem.aspx, linguagem C#,e marque a opção “Place code in a separate file”. Abra o arquivo listagem.aspx para edição, no modo Source, e insira um controle GridView a partir da aba Data da Toolbox, e altere o nome do mesmo para gvListagem. <%@ Page Language="C#" AutoEventWireup="true" CodeFile="listagem.aspx.cs" Inherits="listagem" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Listagem</title> </head> <body> <form id="form1" runat="server"> <div> <asp:GridView ID="gvListagem" runat="server"> </asp:GridView> </div> </form> </body> </html> 96 Frameworks de Desenvolvimento Clique com o botão direito sobre o código e selecione “View Code” e insira o seguinte código no evento Page_Load, tomando cuidado de importar o namespace “using System.Data.SqlClient;”. using using using using using using using using using using using using using System; System.Collections; System.Configuration; System.Data; System.Linq; System.Web; System.Web.Security; System.Web.UI; System.Web.UI.HtmlControls; System.Web.UI.WebControls; System.Web.UI.WebControls.WebParts; System.Xml.Linq; System.Data.SqlClient; public partial class listagem : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { // inicializa conexão ao banco // C:\sites\WebSite3\App_Data\Database.mdf // altere conforme a localização da sua aplicação SqlConnection conn = new SqlConnection(@"Data Source=.\SQLEXPRESS;AttachDbFilename=C:\sites\WebSite3\App_Dat a\Database.mdf;Integrated Security=True;User Instance=True"); conn.Open(); // cria um novo comando SQL SqlCommand cmd = new SqlCommand("SELECT * FROM Contatos", conn); // cria um novo DataAdapter apartir do comando SQL SqlDataAdapter adapter = new SqlDataAdapter(cmd); // cria um DataSet para leitura dos dados DataSet ds = new DataSet(); // preenche o DataSet adapter.Fill(ds); // liga o DataSet a GridView 97 Frameworks de Desenvolvimento gvListagem.DataSource = ds; gvListagem.DataBind(); // fecha a conexão conn.Close(); } } Salve e execute a página em Debug > Start Debugging. 98 Frameworks de Desenvolvimento Unidade 9 Depurando Aplicações no Visual Studio Quando você desenvolve aplicações, você pode encontrar diversos tipos diferentes de erros. Antes de solucioná-los você tem que identificar o tipo de erro e tratá-lo apropriadamente. Existem 3 tipos de erros que você tem que lidar ao desenvolver um aplicativo: erro de sintaxe, erros de runtime, e erros de semântica. Erros de Sintaxe (Syntax Errors) Os erros de sintaxe precisam ser solucionados para que o compilador possa compilar a aplicação. Eles se referem a estrutura e as regras associadas aos comandos da linguagem e aplicação. Por exemplo, se você tentar utilizar o seguinte comando: if( this.isPostBack) { // bloco de instruções } E esquecer de fechar o par de parênteses ( ) ou chaves { }, um erro de sintaxe ocorre. O Visual Studio alerta você sobre esses erros indicando no código enquanto você desenvolve. Erros em tempo de execução (runtime) São erros que ocorrem quando a aplicação está sendo executada. São também conhecidos como exceções. Por exemplo, se uma aplicação espera que um determinado arquivo esteja disponível em algum servidor de uma rede, mas ele não é encontrado pela aplicação, será gerado um erro em tempo de execução (exception). Esse tipo de erro deve ser gerenciado pela própria aplicação. Erros de Semântica São erros mais difíceis de localizar pois sua aplicação irá executar e não será emitido nenhuma notificação. Mesmo assim, sua aplicação não executa a tarefa esperada. Por exemplo, seu programa exibe um formulário de entrada com os dados de um cliente e são salvos sem erro nenhum. Mas ao visualizar novamente a informação, alguns dados não aparecem corretamente ou não foi salvo. Isso significa que a semântica da aplicação não está correta. 99 Frameworks de Desenvolvimento A Classe Debug A classe Debug oferece métodos e propriedades que podem ajudar você a depurar seu código exibindo informaçõese verificando seu código com declarações no código. Usando a classe Debug, você tem a garantia de um código robusto sem afetar a performance e tamanho do código do seu produto na release final. Isso por que os códigos de depuração da classe não são compilados com o programa na versão Release da aplicação. A classe Debug é disponível globalmente na aplicação importando o namespace System.Diagnostics. Não há necessidade de instanciar a classe, ela não tem construtores, e você não pode herdar a mesma pois é do tipo sealed. Métodos da Classe Debug Método Descrição Debug.Write Escreve a string que você determinar, ou escreve a saída de um método ToString de um objeto . Debug.WriteLine Escreve uma linha de texto de sua escolha, ou escreve a saída do método toString de um Objeto. Debug.WriteIf Para escrever uma string condicionalmente. Debug.Print Para gerar a saída de uma linha de texto formatado. Debug.Assert Para exibir uma mensagem se uma determinada condição for falsa. Habilitando a Depuração Você deve habilitar a depuração caso você deseje depurar a sua aplicação. Isso é feito no arquivo web.config. Dentro do mesmo você precisa localizar o elemento compilation e alterar o atributo debug para true. <configuration> <system.web> <compilation debug="true"> ... </system.web> <configuration> 100 Frameworks de Desenvolvimento Adicionando BreakPoints O Visual Studio nos fornece o recurso de BreakPoints, que são nada mais do que pontos de parada da execução de uma aplicação. Você pode colocar ou remover em qualquer ponto do seu código C# um Break Point pressionando a tecla F9, ou a partir do menu Debug > Toggle Breakpoint, ou ainda sobre a linha de código clicando o botão direito opção Breakpoint. Ou ainda, clicando sobre a bolinha vermelha que marca o Break Point. Executando a Aplicação em Modo de Depuração Para executar sua aplicação em modo de debug, você deve pressionar F5 ou clicar em Start Debugging no menu Debug. Se a depuração não estiver habilitada, você será perguntado se quer habilitar o modo de depuração pela caixa de diálogo abaixo. Basta clicar em Ok para habilitar a depuração. Isso irá modificar o arquivo web.config. Execução Passo a Passo Quando o depurador encontra uma linha com um breakpoint, a execução é suspensa. Você pode então executar sua aplicação passo a passo e examinar a lógica de sua aplicação. 101 Frameworks de Desenvolvimento Pressione F10 ou clique em Step Over no menu Debug, se você quer que a linha atual seja executada sem entrar no código da instrução que está sendo executada, como um método na mesma classe ou em uma classe externa. A execução irá parar na próxima linha de código. Pressione F11 ou clique na opção Step Into do menu Debug se você quiser entrar no código da instrução da linha atual, se houver algum. Por default, propriedades são normalmente não usam esse tipo de execução por não conter código sobre elas. Usando a Janela de Depuração O Depurador do Visual Studio oferece diversas janelas, coletivamente conhecidas como variable windows que exibem informações sobre as variáveis enquanto você está depurando. Cada janela tem uma grid de três colunas: Name, Value e Type. A coluna nome mostra os nomes das variáveis adicionadas automaticamente nas janelas Auto e Locals. Na janela Watch, a coluna Name é onde você adiciona suas próprias variáveis ou expressões. As colunas Value e Type exibem o valor e o tipo de variável ou resultado correspondente. Você pode editar o valor da variável na coluna Value. Exibindo a Janela Window No menu Debug, selecione Windows e então o nome da janela que você quer abrir. Você pode escolher Autos, Locals, Watch ou Watch1 até Watch4. Para exibir esses itens, o depurador deve estar sendo executado ou no modo break. Depuração Remota Para você executar depuração remota, você deve usar o programa Remote Debugging Monitor ou msvsmon.exe, que é uma aplicação à qual o Visual Studio se conecta. Por default, o Remote Debugging Monitor roda como uma aplicação Windows, você pode configurá-lo para rodar como serviço caso queira que ele seja executado em um servidor remoto. Você pode executar o assistente a partir do menu Start, pasta Tools. Depois de iniciar o depurador remoto, você deve configurar os ambientes em ambas as máquinas. Você também deve levar em consideração, as questões de seguranças relacionadas ao acesso remoto. Tracing Tracing é o processo de receber informação sobre a execução da aplicação em tempo de execução. É uma maneira de monitorar a sua aplicação. Essa informação pode lhe auxiliar a identificar problemas ou analisar performance, de uma maneira que não afeta a aplicação.Tracing normalmente é utilizado em ambiente de produção em que não é possível executar depuração. 102 Frameworks de Desenvolvimento Você pode utilizar o tracing para: Ter um diagnóstico das informações de uma requisição a uma página ASP.NET Escrever instruções de depuração diretamente no código. Descobrir o código no qual a sua aplicação percorreu. Classe TraceContext É a classe usada para escrever mensagens em uma página ou no trace log em memória. Métodos da Classe TraceContext: Write : escreve uma mensagem de trace no log Warn : similar ao método Write, porém as mensagens aparecem em vermelho. Ambos os métodos possuem 3 overloads, todos os 3 possuem uma mensagem de tracing, e adicionalmente um nome de categoria, e um parâmetro to tipo Exception que indica que você pode informar um erro capturado ou uma exceção definida pelo usuário. Exemplo: Trace.Warn("Atenção"); Trace.Write("Mensagem de Trace"); Trace.Write("Categoria", "Mensagem de Trace"); Trace.Write("Categoria", "Mensagem de Trace", new Exception("mensagem da exceção")); Saída: O parâmetro trace permite você classificar a saída do Trace por categoria alterando o código: <%@ Page ... Trace="True" TraceMode="SortByCategory" %> 103 Frameworks de Desenvolvimento Alternativamente você também pode utilizar SortByTime. Você pode utilizar a propriedade IsEnable do TraceContext para alterar o estado do tracing dinamicamente ou verificar seu estado: // habilitando o tracing no código Trace.isEnabled = true; // verificando seu estado if (Trace.IsEnabled) { Trace.Write("Tracing está habilitado!"); } Habilitando o Tracing em uma Aplicação Web Você pode utilizar o seguinte código para ativar o Tracing a nível de página: <%@ Page Language="C#" Trace="true" ... %> Ou a nível de aplicação, alterando o arquivo web.config: <configuration> ... <system.web> <trace enabled=”true” /> ... </system.web> </configuration> Dados do Trace As informações do trace a nível de página são adicionadas ao final da página .aspx em que estiver habilitado. Categorias do Trace Os dados do Trace são divididos em diversas categorias conforme listado: 104 Frameworks de Desenvolvimento Categoria Descrição Request Details Informação sobre a requisição: identificação da sessão (ID), tempo da requisição, tipo de requisição, e status. Trace Information Saídas padrão e customizadas de trace. A coluna From First(s) contem o tempo total desde a execução até o trace acontecer, e a coluna From Last(s) mostra o tempo restante. Os tempos aqui são importantes para se ter um perfil e para identificar os métodos que mais demoram para responder. Control Tree Lista de todos os itens que estão na página, junto com o tamanho de cada uma. É uma representação da ASP.NET Control Tree. Mostra cada controle, seu ID único, tipo, números de bytes que utiliza para ser renderizado, e o número de bytes que ocupa na ViewState e ControlState. Session State Informação sobre os valores armazenados na sessão (Session), se houver algum. Application State Informação sobre os valores armazenados na Application, se houver algum. Headers Collection Lista de requisição e header de resposta. Form Collection Lista de controles e seus valores, do formulário que foi enviado. Querystring Collection Lista de valores passados na URL. Server Variables Lista de toda as variáveis do servidor e seus valores. 105 Frameworks de Desenvolvimento Unidade 10 Compilando, Testando e Distribuindo Aplicações .NET Depois de pronta sua aplicação precisa ser distribuida para todos que querem ou precisam usá-la. Para usar a sua aplicação, o único requisito para o computador do cliente é que tenha instalado o .NET Framework. Este pode ser instalado diretamente do site da Microsoft, ou ser disponibilizado com sua aplicação no pacote de instalação. Ele também é disponível através do Windows Update e futuramente deve vir junto com todos os sistemas operacionais da Microsoft. Então, tendo o .NET Framework instalado na máquina, para sua aplicação funcionar basta você copiar o arquivo executável para a máquina em questão e executá-lo. No entanto essa prática não é muito recomendada, é mais elegante criar um pacote de instalação para instalar o programa, permitindo assim também sua possivel desinstalação através do Painel de Controle, Adicionar/Remover programas. Criando um Projeto de Instalação Para esse exemplo, utilizaremos o projeto da Calculadora desenvolvido nessa apostila. Com seu projeto aberto, no menu File clique aponte em New e clique em New Project. Na janela New Project, em Project Type, selecione Other Project types > Setup and Deployment Projects. 106 Frameworks de Desenvolvimento Repare em Templates nas seguites opções: Setup Project: cria um programa de instalação que usa o Windows Installer. Web Setup Project: usa o Windows Installer e um Servidor Web para disponibilidar a aplicação. Merge Module Project: cria um arquivo de instalação do tipo msm que pode ser usado por outras aplicações. Cab Project: usado quando você precisa de arquivos de instalação com tamanho especifico. Se por exemplo sua aplicação tem 30MB e você quer disponibiliza-la na internet, pode criar 3 arquivos de 10MB para facilitar o download. Smart Device CAB Project: usado para criação de projeto instalação para dispositivos móveis. Setup Wizard: é um assistente, através dele você pode criar os tipos citados acima só que de maneira simplificada. Selecione Setup Wizard. Em Solution, selecione Add to Solution como a imagem: 107 Frameworks de Desenvolvimento Isso adiciona o projeto a solução que esta aberta no Visual Studio, se estiver marcado Create new Solution, o projeto aberto é fechado e é criado um novo projeto. Em Name você pode dar uma nome para seu projeto de instalação. Por hora, mantena o nome padrão e clique em OK. O Assistente será inicializado. 108 Frameworks de Desenvolvimento Clique em Next. 109 Frameworks de Desenvolvimento Nesse passo você deve informar o tipo de aplicação, se Windows ou Web. Nesse caso, mantenha a opção Windows application selecionada. Clique em Next. 110 Frameworks de Desenvolvimento Selecione as opções Content Files from Calculadora e Primary output Files from Calculadora. Isso inclui na instalação arquivos EXE e DLL do projeto. Se tiver arquivos adicionais para serem adicionados ao projeto como o arquivo do banco de dados do Access ou algum TXT com informações sobre o projeto clique em Add para adicionar o arquivo no passo 4 do assistente. 111 Frameworks de Desenvolvimento Clique em Next para prosseguir. 112 Frameworks de Desenvolvimento Clique em Finish. O Visual Studio adiciona um novo projeto na solução chamado Setup1, como você pode ver no Solution Explorer. O File System Editor também aparece como mostra a figura. 113 Frameworks de Desenvolvimento Você usa o File System Editor para adicionar arquivos no seu projeto de instalação e criar os atalhos do menu Iniciar e Desktop para sua aplicação. Clique em Setup1 na janela Solution Explorer e veja as propriedades disponíveis na janela Properties. A propriedade Author e Manufacturer são geralmente preenchidas com o nome da companhia que desenvolve o software, seus valores também são usados para construir o caminho padrão onde será instalado o programa. Uma vez instalado o programa a propriedade Author é exibida nas informações de suporte em Adicionar/Remover Programas no Painel de Controle. A propriedade Title é o nome do programa de instalação e a propriedade Version permite especificar informações sobre a versão do seu programa de instalação. As propriedades Product Code contém informações geradas pelo Visual Studio que permitem identificar individualmente distribuições do seu programa. Atenção, estas propriedades dizem respeito ao programa de instalação e não a aplicação Formularios. Na janela Solution Explorer, clique com o botão direito do mouse sobre Setup1 e clique e em Properties. É exibida a caixa de diálogo Setup1 Property Pages. Esta caixa de diálogo permite que você altere algumas das opções escolhidas no assistênte (Setup Wizard) caso necessário e customize algumas configurações adicionais que não estavam disponíveis no assistente. 114 Frameworks de Desenvolvimento A caixa Output File Name especifica o nome do arquivo da instalação. Geralmente ele contém a extensão .msi por ser usado pelo Windows Installer. A caixa de seleção Package Files contém 3 opções: As Loose Uncompressed Files: cria arquivos descompactados dentro da pasta de instalação. In Setup File: é a que cria o arquivo msi. Cabinet Files(s): gera os arquivos cab na pasta de instalação. A caixa de seleção Compression permite escolher a otimização da compactação dos arquivos de intalação, a maior parte dos programadores escolhe Optimized for Size, que prioriza diminuir o tamanho dos arquivos de instalação. Clique em OK. Em Build > Configuration Manager, altere a opção Debug para Release: 115 Frameworks de Desenvolvimento Quando estamos criando nossa aplicação é importante que esta caixa de seleção esteja marcada com Debug para que possamos usar as ferramentas de depuração de erros do Visual Studio. Com o Debug marcado o programa é compilado com informações extras para permitir a depuração dos erros, essas informações não são necessárias quando estamos disponibilizando nossa aplicação e também ocasionam perda de performance na nossa aplicação. Na janela Solution Explorer, clique com o botão direito do mouse em Setup1, selecione View, clique em Launch Condition. É aberta a página Launch Condition. Na janela Properties a propriedade InstallUrl é setada com uma página na Internet que possibilita o download e instalação do .NET Framework, como sabe, necessário para sua aplicação funcionar. 116 Frameworks de Desenvolvimento Por padrão sempre que é inicializado a instalação é verificado se o .NET Framework esta instalado na máquina em questão ou não. Se não estiver ele usa essa propriedade, a InstallUrl para fazer o download e instalação do arquivo Dotnetfx.exe, que instala o .NET Framework. Antes de iniciar o download e instalação da Internet ele emite uma mensagem, o contéudo desta mensagem está na propriedade Message. Você pode disponibilizar o .NET Framework junto com sua instalação, para que não seja necessário baixar o arquivo da internet. Para isso você muda a propriedade InstallUrl para o caminho da sua instalação e se desejar muda a propriedade Message para um mensagem mais apropriada. Será necessário copiar então o arquivo Dotnetfx.exe para a pasta de instalação do seu programa ou para a pasta que você indicar em InstallUrl. Vamos deixar como está, para fazer o download se necessário. Na janela Solution Explorer, clique com o botão direito sobre Setup1, depois clique em Build. No Windows Explorer localize a pasta do projeto Setup1, ela foi especificada quando você criou o projeto e entre na pasta Release. Eis os arquivos necessários para a instalação do seu projeto, você pode copiados em um CD para disponibilizar a instalação do seu programa, como citado anteriormente você pode copiar para esta pasta também o arquivo Dotnetfx.exe para disponibilizar junto a instalação do .NET Framework. 117 Frameworks de Desenvolvimento 118