Quase todas as linguagens de programação possuem recursos para compatibili- zaçã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”:
Figura 3.9
Eis aqui agora um diagrama demonstrando o conceito de “ter”:
Figura 3.10 90 Processador 8086 80286 80386 80486 Pentium Máquina Pentium Memória RAM Disco Rígido Drive Disquete
A pergunta-chave para explicar o tópico detype castde 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 “gra- var” 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 ope- radores 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. Evidente- mente, 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 lem- bre-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 objetoppara 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; public int AnoFabricacao; }
class Intel8086 : Processador {
{
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 indi- cando 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 proces- sador através de uma classe externa, poderíamos escrever uma classe com o se- guinte formato:
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 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 ativará o modo protegido, informará que o mesmo foi ativado e logo em seguida dirá que o processador não possui modo protegido!
Lembre-se, em OOP, quando tratamos de herança linear, o “ser” assume um caráter de família mesmo. Um 386 é um processador. Para chegar ao 386, foi preciso passar por duas gerações. Pela lógica de herança da classe, um 386 também “é” um 286 e um 8086.
Nossa classe anterior, portanto, teria de ser reescrita e ficaria mais consisten- te 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 progra- maçã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:
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 93
ramo. Basta que o parâmetro que receberá os objetos seja definido como System.Object.
Resumo
Neste capítulo você foi exposto a uma série de conceitos e técnicas. Dominar a Programação Orientada a Objetos dentro dos atuais ambientes de desenvolvi- mento é absolutamente essencial.
Você aprendeu todos os conceitos essenciais de OOP (Polimorfismo, Encap- sulamento, Herança) e viu de que forma eles são implementados e como se apli- cam 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 in- formaçõ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.
4
Namespaces,
Assemblies e
documentação de
sistemas usando XML
Introdução
Até o momento, temos falado acerca da arquitetura da plataforma .NET e sobre a sintaxe da linguagem C#. Já fizemos referência aos conceitos de assembly e na- mespace e neste capítulo iremos estudá-los em maior profundidade, explorando suas formas de implementação e uso e ainda abordando também a documenta- ção do código-fonte em .NET usando XML.
Namespaces
Namespaces são a forma lógica de organizar o código-fonte em .NET. Toda a bi- blioteca de classes da .NET está estruturada em uma hierarquia de namespaces que tem como base o namespaceSystem, onde se encontram as classes básicas da .NET. Fisicamente, os namespaces são armazenados em assemblies, que, por sua vez, são armazenados no sistema de arquivos como DLLs (bibliotecas de ligação dinâmica) ou como arquivos executáveis (.exe) que correspondem a aplicações
Quando uma aplicação é organizada logicamente usando namespaces, este deve possuir pelo menos uma classe que implemente o método Main( ), para aplicações console, ouWinMain( ), para aplicações Windows. Por sua vez, uma biblioteca de classes é implementada usando namespaces, mas nenhuma das suas classes membros implementa o métodoMain( )ouWinMain( ), e elas são usadas para implementar código reutilizável.
Assim como as classes contêm membros, os namespaces também, os quais podem ser dos seguintes tipos:
m Outro namespace: namespaces aninhados m Classes: tipo class
m Estruturas: tipo struct m Enumerados: tipo enum m Interfaces: tipo interface m Delegados: tipo delegates