Você está na página 1de 768

Microsoft

Microsoft*

Visual C#'2010
John Sharp

COLEO MICROSOFT
HALVORSON, M.
Microsoft Visual Basic 2008 Passo a Passo
HALVORSON, M.
Microsoft Visual Basic 2010 Passo a Passo
HOTEK, M.
Microsoft SQL Server 2008 Passo a Passo
JACOBSON, MISNER & HITACHI CONSULTING
Microsoft SQL Server 2005 Analysis Services Passo a Passo
KIRIATY & COLS.
Introduo ao Windows 7 para Desenvolvedores
MISNER & HITACHI CONSULTING
Microsoft SQL Server 2005 Reporting Services Passo a Passo
SHARP, J.
Microsoft Visual C# 2008 Passo a Passo
SHEPHERD, G.
Microsoft ASP.NET 3.5 Passo a Passo
SOLID QUALITY LEARNING
Microsoft SQL Server 2005 Fundamentos de Bancos de Dados
Passo a Passo
SOLID QUALITY LEARNING
Microsoft SQL Server 2005 Tcnicas Aplicadas Passo a Passo
STANEK, W.
Microsoft Exchange Server 2010: Guia de Bolso do Administrador
STANEK, W.
Microsoft SQL Server 2008: Guia de Bolso do Administrador
STANEK, W.
Microsoft Windows Server 2003: Guia de Bolso do Administrador
STANEK, W.
Microsoft Windows XP Professional: Guia de Bolso do
Administrador - 2.ed
STANEK, W.
Windows 7: Guia de Bolso do Administrador
STANEK, W.
Windows Server 2008: Guia Completo
STANEK, W.
Windows Server 2008: Guia de Bolso do Administrador
STANEK, W.
Windows Vista: Guia de Bolso do Administrador

O autor
John Sharp um importante tecnlogo na Content Master, empresa de consultoria e criao tcni
ca integrante do CM Group Ltd. Especialista em desenvolvimento de aplicativos no Microsoft .NET
Framework e em outras tecnologias, John j produziu inmeros tutoriais, publicaes tcnicas e
apresentaes sobre sistemas distribudos, SOA e Web services, linguagem C# e questes de intero
perabilidade. Ele tem contribudo para o desempenho de vrios cursos para o Microsoft Training (foi
coautor do primeiro curso de programao em C# para a Microsoft) e tambm autor de diversos
livros populares, como o Microsoft Visual C# 2008 Passo a Passo.

S531m

Sharp, John.
Microsoft Visual C# 2010 : passo a passo / John Sharp ;
traduo: Teresa Cristina Felix de Sousa, Edson Furmankiewicz ;
reviso tcnica: Daniel Antonio Callegari. - Porto Alegre :
Bookman, 2011.
780 p. : il. ; 25 cm.
ISBN 978-85-7780-849-6
1. Computao - Desenvolvimento de programas. I. Ttulo.
CDU 004.413Visual C#

Catalogao na publicao: Ana Paula M. Magnus - CRB 10/2052

Microsoft

John Sharp

Microsoft*

Visual C#2010
Passo a Passo
R e v is o t cn ica :
Daniel Antonio Callegari
Doutor em Cincia da Computao
Professor da PUC-RS e profissional certificado Microsoft

Obra originalmente publicada sob o ttulo

Microsoft Visual C# 2010 Step by Step, autoria deJohn Sharp


ISBN 9780735626706
Original English language copyright 2010, O'Reilly Media, Inc.
Traduo em lngua portuguesa 2011 Bookman Companhia Editora Ltda., uma diviso da Artmed
Editora SA. Esta traduo vendida e publicada com permisso da O'Reilly Media, Inc., proprietria ou
controladora dos direitos de publicao e venda da obra.
Portuguese language translation copyright 2011 Bookman Companhia Editora Ltda., a Division of
Artmed Editora S.A.This translation is published and sold by permission of O'Reilly Media,Inc., which
owns or controls of all rights to publish and sell the same.
Traduo [Microsoft Visual C# 2008 Passo a Passo):Edson Furmankiewicz
Atualizao de contedo [Microsoft Visual C# 2010 Passo a Passo): Teresa Cristina Felix de Sousa
Capa: VS Digital, arte sobre capa original
Leitura final: Aline Grodt
Editora snior - Bookman: Arysinha Affonso
Editora responsvel por esta obra: Elisa Viali
Projeto e editorao: Techbooks
Microsoft, Microsoft Press, Excel, IntelliSense, Internet Explorer, Jscript, MS, MSDN, SOL Server, Visual
Basic, Visual C#, Visual C++, Visual Studio, Win32, Windows e Windows Vista so exemplos comer
ciais notrios ou marcas comerciais registradas da Microsoft Corporation nos Estados Unidos e/ou em
outros pases. Outros nomes de produto e de empresa mencionados aqui podem ser marcas comerciais
de seus respectivos proprietrios.
Os exemplos de empresas, organizaes, produtos, nomes de domnio, endereos de correio eletrnico,
logotipos, pessoas, lugares e eventos retratados aqui so fictcios. Nenhuma associao com qualquer
empresa, organizao, produto, nome de domnio, endereo de correio eletrnico, logotipo, pessoa,
lugar ou evento reais foi intencional ou deve ser inferida.
Este livro expressa vises e opinies do autor. As informaes contidas neste livro so fornecidas sem
qualquer garantia legal, expressa ou implcita. Os autores, a Microsoft Corporation, e seus revendedores ou
distribuidores no sero responsveis por quaisquer danos causados ou alegadamente causados direta ou
indiretamente por este livro.

Reservados todos os direitos de publicao, em lngua portuguesa,


ARTMED EDITORA S.A.
(BOOKMAN COMPANHIA EDITORA uma diviso da ARTMED EDITORA S. A.)
Av. Jernimo de Orneias, 670 - Santana
90040-340 - Porto Alegre - RS
Fone: (51) 3027-7000 Fax: (51) 3027-7070
proibida a duplicao ou reproduo deste volume, no todo ou em parte, sob
quaisquer formas ou por quaisquer meios (eletrnico, mecnico, gravao, fotocpia,
distribuio na Web e outros), sem permisso expressa da Editora.
Unidade So Paulo
Av. Embaixador Macedo Soares, 10.735 - Pavilho 5 - Cond. Espace Center
Vila Anastcio - 05095-035 - So Paulo - SP
Fone: (11) 3665-1100 Fax: (11) 3667-1333
SAC 0800 703-3444
IMPRESSO NO BRASIL

PRINTED IN BRAZIL

Agradecimentos
Diz um ditado popular que os pintores da Forth Railway Bridge, uma grande ponte da era Vitoriana
que cruza a Foz do Rio Forth, ao norte de Edinburgo, tm emprego vitalcio. Segundo a lenda, so
necessrios muitos anos para pint-la de uma extremidade outra e, ao final do servio, j hora de
comear tudo de novo. No tenho certeza se a demora se deve s agruras do clima da Esccia ou
durabilidade da tinta aplicada, embora minha filha insista em dizer que, na verdade, os membros da
Cmara Municipal de Edinburgo ainda no decidiram qual a melhor paleta de cores para a ponte.
s vezes, acho este livro parecido com a empreitada escocesa. Ouando finalmente concluo a obra, a
Microsoft anuncia outra atualizao excelente do Visual Studio e do C#, e meus amigos da Microsoft
Press me perguntam: quais so seus planos para a prxima edio? . No entanto, ao contrrio do
trabalho na Forth Railway Bridge, escrever uma nova verso deste texto sempre agradvel, com
muito mais possibilidades criativas do que tentar descobrir novas maneiras de segurar um pincel.
H sempre novidades, alm de uma tecnologia inovadora para conhecer. Nesta edio, abordarei os
novos recursos do C# 4.0 e do .NET Framework 4.0, extremamente teis para construir aplicativos
para hardware cada vez mais potentes. Mesmo que parea interminvel, esta atividade sempre
compensadora e gratificante.
Em um projeto como este, boa parte da satisfao vem de trabalhar com um grupo de pessoas ta
lentosas e motivadas da Microsoft Press, com os desenvolvedores da Microsoft dedicados ao Visual
Studio 2010 e com quem faz a reviso de todos os captulos e apresenta sugestes de melhorias.
Gostaria de citar principalmente Rosemary Caperton e Stephen Sagman, que se empenharam incan
savelmente para manter o projeto nos trilhos; Per Blomqvist, que revisou (e corrigiu) cada captulo; e
Roger Leblanc, que teve a rdua tarefa de editar o manuscrito e converter minha prosa em um texto
compreensvel. Uma meno especial para Michael Blome, que me deu acesso ao software e respon
deu minha avalanche de perguntas sobre a Task Parallel Library (TPL). Vrios colaboradores da
Content Master dedicaram um bom tempo revisando e testando o cdigo dos exerccios agradeo
a Mike Sumsion, Chris Cully, James M illar e Louisa Perry. Tambm agradeo a Jon Jagger, coautor da
primeira edio deste livro, nos idos de 2001.
Por ltimo, mas no menos importante, agradeo minha famlia. Minha esposa Diana uma ma
ravilhosa fonte de inspirao. Ao escrever o Captulo 28 sobre a Task Parallel Library (TPL), tive um
branco e pedi a ela que me explicasse, em suas prprias palavras, os mtodos de barreira. Ela me
lanou um olhar de deboche e deu uma resposta que, embora anatomicamente correta se eu esti
vesse sendo submetido a uma cirurgia, indicava que ou eu elaborara mal a minha pergunta, ou ela
interpretara mal o que eu perguntara! James j est grande e logo saber o quanto vai ter de traba
lhar se quiser manter o estilo de vida que Diana e eu gostaramos de ter em nossa velhice. Francesca
tambm cresceu, e parece ter aprimorado sua estratgia para conseguir tudo o que quer; basta, para
isso, olhar para mim com seus olhos lnguidos e brilhantes, e sorrir.
Por fim, d-lhe Gills!
John Sharp

Sumrio geral
Parte

Apresentando o Microsoft Visual C # e o Microsoft


Visual Studio 2010

Bem -vindo ao C # ............................................................................... 35

Trabalhando com variveis, op eradores e expresses . . . 59

Escrevendo m todos e aplicand o e s c o p o ...............................79

Utilizando instrues de d e c is o ..............................................105

Utilizando atribuio com posta e instrues


de ite rao ......................................................................................... 123

G erenciando erros e e x ce e s................................................... 141

Parte

ii Entendendo a linguagem C#

Criando e gerenciando classes e o b j e t o s ............................ 161

Entendendo valores e re fe r n cia s........................................... 183

Criando tipos-valor com en u m eraes e estruturas . . . . 205

10

Utilizando arrays e c o le e s ......................................................223

11

Entendendo arrays de p a r m e tr o s .........................................251

12

Trabalhando com h e ra n a ...........................................................263

13

Criando interfaces e definindo classes a b s t r a t a s .............285

14

Utilizando a coleta de lixo e o gerenciam ento


de r e c u r s o s .......................................................................................311

parte

ui Criando componentes

15

Im plem entando propriedades para acessar cam po s . . .3 2 7

16

Utilizando in d e x a d o re s................................................................347

Sumrio

17

Interrom pendo o fluxo do program a e


tratando e v e n to s............................................................................ 361

18

A presentand o g e n r ic o s ............................................................. 385

19

Enum erando c o le e s .................................................................. 413

20

C on su ltan d o d ados na m em ria utilizando


expresses de c o n s u lt a ................................................................427

21

Sobrecarga de o p e r a d o r e s ........................................................ 451

Parte iv

Construindo Aplicativos W PF

22

A presen tan d o o W ind ow s Presentation Foundation . . .4 75

23

O btendo a entrada do u s u r io ................................................ 509

24

Realizando v a lid a e s .................................................................. 541

Parte v

Gerenciando dados

25

Consultando inform aes em um banco de dados . . . . 567

26

Exibindo e editando dados com o Entity Fram ew ork


e vinculao de d a d o s .................................................................. 597

Parte vi

Construindo solues profissionais com o Visual


Studio 2010

27

Introduo Task Parallel L ib r a r y ........................................... 631

28

Realizando acesso a dados em p a r a le lo ...............................681

29

Criando e utilizando um Web S e r v ic e ....................................715

A pndice
Interoperabilidade com linguagens d in m ic a s..................749
ndice

................................................................................................................ 757

Sumrio
parte I

A p r e s e n t a n d o o M ic r o s o f t V is u a l C # e o M ic r o s o f t
V is u a l S t u d io 2 0 1 0

B e m - v in d o a o C # ............................................................................... 35
Comeando a programar com o ambiente do Visual Studio 2010............ 35
Escrevendo seu primeiro programa............................................................40
Utilizando namespaces.............................................................................. 46
Criando um aplicativo grfico....................................................................49
Referncia rpida do Captulo 1 ................................................................58

T r a b a lh a n d o c o m v a r i v e is , o p e r a d o r e s e e x p r e s s e s . . . 59
Entendendo instrues.............................................................................. 59
Utilizando identificadores.......................................................................... 60
Identificando palavras-chave..............................................................60
Utilizando variveis.................................................................................... 61
Nomeando variveis.......................................................................... 62
Declarando variveis.......................................................................... 62
Trabalhando com tipos de dados prim itivos............................................. 63
Variveis locais no atribudas............................................................64
Exibindo valores de tipos de dados primitivos...................................64
Utilizando operadores aritmticos..............................................................68
Operadores e tip o s ............................................................................ 69
Examinando operadores aritmticos................................................. 70
Controlando a precedncia................................................................73
Utilizando a associatividade para avaliar expresses.........................74
A associatividade e o operador de atrib uio ...................................74
Incrementando e decrementando variveis............................................... 75
Prefixo e sufixo .................................................................................. 76
Declarando variveis locais implicitamente tipadas...................................76
Referncia rpida do Captulo 2 ................................................................78

E s c r e v e n d o m t o d o s e a p lic a n d o e s c o p o ...............................79
Criando mtodos........................................................................................ 79
Declarando um mtodo......................................................................80
Retornando dados de um mtodo..................................................... 81
Chamando m todos..........................................................................83
Especificando a sintaxe de chamada de m todo...............................83

10

Sumrio

Aplicando esco p o ...................................................................................... 85


Definindo o escopo local....................................................................86
Definindo o escopo de classe............................................................. 86
Sobrecarregando m tod os............................................................... 87
Escrevendo mtodos.................................................................................. 88
Utilizando parmetros opcionais e argumentos nomeados...................... 96
Definindo parmetros opcionais....................................................... 97
Passando argumentos nom eados..................................................... 98
Resolvendo ambiguidades com parmetros opcionais e
argumentos nomeados................................................................... 98
Referncia rpida do Captulo 3 ............................................................. 104

Utilizando instrues de d e cis o ..............................................105


Declarando variveis booleanas............................................................... 105
Utilizando operadores booleanos........................................................... 106
Entendendo operadores de igualdade e relacionais........................ 106
Entendendo operadores lgicos condicionais.................................107
Curto-circuito.................................................................................. 108
Resumindo a precedncia e a associatividade dos operadores........ 108
Utilizando instrues if para tomar decises........................................... 109
Entendendo a sintaxe da instruo i f ............................................... 109
Utilizando blocos para agrupar instrues.......................................110
Instrues if em cascata....................................................................111
Utilizando instrues s w it c h ....................................................................116
Entendendo a sintaxe da instruo sw itch .......................................117
Seguindo as regras da instruo sw itch ........................................... 118
Referncia rpida do Captulo 4 ............................................................. 122

Utilizando atribuio com posta e instrues


de ite rao

123

Utilizando operadores de atribuio composta.......................................123


Escrevendo instrues w h ile ....................................................................124
Escrevendo instrues f o r ........................................................................129
Entendendo o escopo da instruo f o r ........................................... 130
Escrevendo instrues d o ........................................................................131
Referncia rpida do Captulo 5 ..............................................................140

Sumrio

G erenciando erros e e x c e e s................................................... 141


Lidando com erros.................................................................................... 141
Testando o cdigo e capturando as excees......................................... 142
Excees no tratadas......................................................................143
Utilizando mltiplas rotinas de tratamento c a t c h ...........................144
Capturando mltiplas excees....................................................... 145
Utilizando aritmtica verificada e no verificada de nmeros inteiros. . . 150
Escrevendo instrues verificadas................................................... 150
Escrevendo expresses verificadas................................................... 151
Lanando excees.................................................................................. 153
Utilizando um bloco fin a lly ......................................................................156
Referncia rpida do Captulo 6 ..............................................................158

Parte ii

E n t e n d e n d o a lin g u a g e m C #
Criando e gerenciando classes e o b je t o s

161

Entendendo a classificao...................................................................... 161


O objetivo do encapsulamento................................................................162
Definindo e utilizando uma classe............................................................162
Controlando a acessibilidade....................................................................164
Trabalhando com construtores....................................................... 165
Sobrecarregando construtores........................................................166
Entendendo dados e mtodos s ta tic ....................................................... 174
Criando um campo com partilhado................................................. 175
Criando um campo static utilizando a palavra-chave c o n s t ............ 176
Classes estticas.............................................................................. 176
Classes annimas.............................................................................. 179
Referncia rpida do Captulo 7 ..............................................................181

Entendendo valores e re fe r n cia s........................................... 183


Copiando variveis de tipo-valor e classes............................................... 183
Entendendo valores nulos e tipos nullable............................................... 188
Utilizando tipos nullable..................................................................189
Entendendo as propriedades dos tipos n u lla b le.............................190
Utilizando parmetros re f e o u t ................................................................191
Criando parmetros r e f ....................................................................191
Criando parmetros o u t ..................................................................192

11

12

Sumrio

Como a memria do computador organizada.....................................194


Utilizando a pilha e o h e a p ............................................................. 196
A classe S y ste m .O b je ct ............................................................................197
Boxing.......................................................................................................197
Unboxing.................................................................................................. 198
Casting de dados seguro..........................................................................200
O operador is .................................................................................... 200
O o p erado ras.................................................................................. 200
Referncia rpida do Captulo 8 ............................................................. 203

Criando tipos-valor com enum eraes e estruturas . . . . 205


Trabalhando com enumeraes................................................................205
Declarando uma enum erao......................................................... 205
Utilizando uma enumerao........................................................... 206
Escolhendo valores literais de enum erao.....................................207
Escolhendo o tipo subjacente de uma enumerao........................ 208
Trabalhando com estruturas....................................................................210
Declarando uma estrutura............................................................... 212
Entendendo as diferenas entre estrutura e classe.......................... 213
Declarando variveis de estrutura................................................... 214
Entendendo a inicializao de estruturas.........................................215
Copiando variveis de estru tu ra..................................................... 219
Referncia rpida do Captulo 9 ............................................................. 222

10

Utilizando arrays e c o le e s ......................................................223


O que um array?.................................................................................... 223
Declarando variveis de a r r a y ......................................................... 223
Criando uma instncia de array........................................................ 224
Inicializando variveis de a r r a y ....................................................... 225
Criando um array implicitamente tipado......................................... 226
Acessando um elemento individual de um array.............................227
Iterando por um array......................................................................227
Copiando a rra y s .............................................................................. 229
Utilizando arrays multidimensionais............................................... 230
Utilizando arrays para jogar c a rta s ................................................. 231
O que so classes de coleo?..................................................................238
A classe de coleo A rrayList ........................................................... 240
A classe de coleo Queue ................................................................242
A classe de coleo Stack ..................................................................242
A classe de coleo H ashtable ......................................................... 243

Sumrio

A classe de coleo S o rte d L ist ......................................................... 245


Utilizando inicializadores de coleo............................................... 246
Comparando arrays e co le es....................................................... 246
Utilizando classes de coleo para jogar cartas...............................246
Referncia rpida do Captulo 1 0 ............................................................250

11

Entendendo arrays de p a r m e tr o s .........................................251


Utilizando argumentos de arrays..............................................................252
Declarando um array param s ............................................................253
Utilizando params o b je c tl] ..............................................................255
Utilizando um array param s ..............................................................256
Comparando arrays de parmetros e parmetros opcionais.................. 258
Referncia rpida do Captulo 11 ............................................................261

12

Trabalhando com h e ra n a ...........................................................263


O que h e ra n a ?.................................................................................... 263
Utilizando a herana................................................................................ 264
Chamando construtores da classe base........................................... 266
Atribuindo classes............................................................................ 267
Declarando mtodos n e w ................................................................269
Declarando mtodos virtuais............................................................270
Declarando mtodos o v e rrid e ......................................................... 271
Entendendo o acesso p r o t e c te d ..................................................... 274
Entendendo mtodos de extenso......................................................... 279
Referncia rpida do Captulo 1 2 ........................................................... 283

13

Criando interfaces e definindo classes a b s t r a t a s .............285


Entendendo interfaces.............................................................................. 285
Definindo uma interface..................................................................286
Implementando uma in terfa ce....................................................... 287
Referenciando uma classe por meio de sua interface...................... 288
Trabalhando com mltiplas interfaces............................................. 289
Implementando explicitamente uma interface.................................289
Restries das interfaces..................................................................291
Definindo e utilizando interfaces..................................................... 291
Classes abstratas...................................................................................... 301
Mtodos ab strato s..........................................................................302
Classes selad as........................................................................................ 303
Mtodos selados.............................................................................. 303
Implementando e utilizando uma classe abstrata...........................304
Referncia rpida do Captulo 1 3 ........................................................... 309

13

14

Sumrio

14

U tilizando a coleta de lixo e o gerenciam ento


de r e c u r s o s

311

O tempo de vida de um o b je to ............................................................... 311


Escrevendo destrutores....................................................................312
Por que utilizar o coletor de lix o ? ................................................... 314
Como funciona o coletor de lix o ? ................................................... 315
Recomendaes................................................................................ 316
Gerenciamento de recursos......................................................................316
Mtodos de descarte........................................................................317
Descarte seguro quanto a excees................................................. 317
A instruo using ..............................................................................318
Chamando o mtodo Dispose a partir de um destrutor.................. 320
Implementando descarte seguro quanto a excees.............................. 321
Referncia rpida do Captulo 1 4 ........................................................... 324

Parte ui

15

C r ia n d o c o m p o n e n t e s

Im plem entando propriedades para acessar cam pos . . . 327


Implementando encapsulamento com m todos.....................................328
O que so propriedades?..........................................................................329
Utilizando propriedades................................................................. 331
Propriedades somente-leitura......................................................... 332
Propriedades somente-gravao..................................................... 332
Acessibilidade de propriedades....................................................... 333
Entendendo as restries de uma propriedade.......................................334
Declarando propriedades de interface..................................................... 336
Utilizando propriedades em um aplicativo W indows...................... 337
Gerando propriedades au to m ticas....................................................... 338
Inicializando objetos com propriedades................................................. 340
Referncia rpida do Captulo 1 5 ........................................................... 345

16

Utilizando in d e x a d o re s................................................................347
O que um indexador?............................................................................ 347
Um exemplo que no utiliza indexadores....................................... 347
O mesmo exemplo utilizando indexadores.....................................349
Entendendo os mtodos de acesso do indexador...........................351
Comparando indexadores e arrays................................................... 352
Indexadores em interfaces........................................................................354
Utilizando indexadores em um aplicativo Windows.................................355
Referncia rpida do Captulo 1 6 ............................................................360

Sumrio

17

Interrom pendo o fluxo do program a e


tratando e v e n to s

361

Declarando e utilizando delegates............................................................361


0 cenrio da fbrica autom atizada................................................. 362
Implementando a fbrica sem utilizar delegates.............................362
Implementando a fbrica utilizando um delegate...........................363
Utilizando delegates........................................................................ 365
Expresses lambda e delegates................................................................370
Criando um mtodo adaptador....................................................... 371
Utilizando uma expresso lambda como um adaptador................ 371
A forma das expresses la m b d a ..................................................... 372
Ativando notificaes por meio de eventos............................................. 374
Declarando um e v e n to ....................................................................374
Fazendo a inscrio em um e v e n to ................................................. 375
Entendendo eventos de interface W P F ................................................... 377
Utilizando eventos............................................................................ 378
Referncia rpida do Captulo 1 7 ............................................................383

18

Apresentando g e n ric o s ............................................................. 385


O problema com o b je c ts .......................................................................... 385
A soluo dos genricos.......................................................................... 387
Classes genricas versus generalizadas........................................... 389
Genricos e restries......................................................................390
Criando uma classe genrica....................................................................390
A teoria das rvores binrias............................................................390
Construindo uma classe de rvore binria com genricos.............. 393
Criando um mtodo genrico..................................................................402
Definindo um mtodo genrico para criar uma rvore binria . . . .403
Varincia e interfaces genricas................................................................405
Interfaces cova ria n te s ......................................................................407
Interfaces contravariantes................................................................409
Referncia rpida do Captulo 1 8 ............................................................412

19

Enum erando c o le e s .................................................................. 413


Enumerando os elementos em uma coleo........................................... 413
Implementando manualmente um enum erador.............................415
Implementando a interface lEnum erable ......................................... 419
Implemente um enumerador utilizando um iterador...............................421
Um iterador sim p les........................................................................421
Definindo um enumerador para a classe Tree<Tltem>
por meio de um ite ra d o r............................................................. 423
Referncia rpida do Captulo 1 9 ............................................................426

15

16

Sumrio

20

C onsultand o dados na m em ria utilizando


expresses de c o n s u lt a

427

0 que a Language Integrated Q uery?................................................... 427


Utilizando a LINQ em um aplicativo C # ................................................... 428
Selecionando dados..........................................................................430
Filtrando dados................................................................................ 432
Ordenando, agrupando e agregando dado s...................................433
Juno de dado s..............................................................................436
Utilizando operadores de co n su lta................................................. 437
Consultando dados em objetos Tree< Tltem > .................................439
LINQ e avaliao postergada........................................................... 444
Referncia rpida do Captulo 20 ........................................................... 449

21

Sobrecarga de o p e r a d o r e s ........................................................451
Entendendo os operadores......................................................................451
Restries dos operadores................................................................452
Operadores sobrecarregados........................................................... 452
Criando operadores sim tricos....................................................... 454
Entendendo a avaliao da atribuio composta.....................................456
Declarando operadores de incremento e decremento............................ 457
Comparando operadores em estruturas e classes...................................458
Definindo pares de operadores............................................................... 458
Implementando operadores....................................................................459
Entendendo os operadores de converso............................................... 466
Fornecendo converses predefinidas............................................... 466
Implementando operadores de converso
definidos pelo u surio..................................................................467
Criando operadores simtricos, uma retomada do assunto............ 468
Escrevendo operadores de converso............................................. 469
Referncia rpida do Captulo 21 ........................................................... 472

Parte iv

22

C o n s tr u in d o A p lic a t iv o s W P F

A presentando o W indow s Presentation Foundation . . .4 75


Criando um aplicativo W P F ......................................................................475
Construindo o aplicativo W P F ......................................................... 476
Adicionando controles ao form ulrio..................................................... 490
Utilizando controles W P F ..................................................................490
Alterando as propriedades dinamicamente.....................................498

Sumrio

Tratando eventos em um formulrio W P F ............................................... 502


Processando eventos no Windows Fo rm s....................................... 503
Referncia rpida do Captulo 22 ............................................................508

23

O btendo a entrada do u s u r io ................................................ 509


Diretrizes e estilos de menu......................................................................509
Menus e eventos de menu........................................................................ 510
Criando um m en u ............................................................................ 510
Tratando eventos de m e n u ..............................................................516
Menus de atalh o ...................................................................................... 523
Criando menus de atalho..................................................................523
Caixas de dilogo comuns do Windows................................................... 527
Utilizando a classe SaveFileDialog ................................................... 527
Aprimorando a capacidade de resposta de um aplicativo W P F .............. 530
Referncia rpida do Captulo 23 ........................................................... 540

24

Realizando v a lid a e s .................................................................. 541


Validando os dados.................................................................................. 541
Estratgias para validar a entrada do usurio.................................541
Um exemplo - order tickets (solicitar ingressos)
para eventos.......................................................................................... 542
Realizando a validao com vinculao de d a d o s ...........................543
Alterando o ponto em que a validao ocorre.................................559
Referncia rpida do Captulo 24 ............................................................564

Parte v

25

G e r e n c ia n d o d a d o s

Consultando inform aes em um banco de dados . . . . 567


Consultando um banco de dados por meio do ADO.NET.........................567
O banco de dados Northwind......................................................... 568
Criando o banco de dados................................................................568
Utilizando ADO.NET para consultar informaes de pedidos.......... 570
Consultando um banco de dados usando LINQ to S Q L ...........................581
Definindo uma classe de entidade................................................... 581
Criando e executando uma consulta LINQ to S Q L ...........................583
Busca adiada e busca im ed iata....................................................... 585
Fazendo juno de tabelas e criando relaes.................................586
Busca adiada e imediata, uma retomada do assunto...................... 590
Definindo uma classe DataContext personalizada...........................591
Utilizando a LINQ to SQL para consultar informaes de pedidos . .591
Referncia rpida do Captulo 25 ........................................................... 596

17

18

Sumrio

26

E x ib in d o e e d it a n d o d a d o s c o m o E n t it y F r a m e w o r k
e v in c u la o d e d a d o s .................................................................. 597
Utilizando vinculao de dados com Entity Framework...........................598
Utilizando vinculao de dados para modificar dados............................ 615
Atualizando dados existentes......................................................... 615
Tratando atualizaes conflitantes................................................... 616
Adicionando e excluindo dados....................................................... 619
Referncia rpida do Captulo 26 ........................................................... 628

Parte v i

C o n s t r u in d o s o lu e s p r o f is s io n a is c o m

o V is u a l

S tu d io 2 0 1 0
27

In t r o d u o Task P a r a lle l L i b r a r y

631

Por que fazer multitarefa por meio de processamento paralelo.............. 632


O surgimento do processador multincleo.....................................633
Implementando multitarefa em um
aplicativo.............................................................................................. 634
Tarefas, threads e Th readP o ol ......................................................... 635
Criando, executando e controlando tarefas.....................................636
Utilizando a classe Task para implementar paralelismo.................. 640
Abstraindo tarefas com a classe P a ra lle l.........................................649
Retornando um valor de uma ta re fa ............................................... 656
Utilizando tarefas e threads
de interface do usurio em conjunto................................................... 660
Cancelando tarefas e tratando excees................................................. 664
Mecnica do cancelamento cooperativo.........................................665
Tratando excees de tarefas com a classe AggregateException . . .673
Utilizando continuaes com tarefas canceladas e com falhas . . . .677
Referncia rpida do Captulo 27 ........................................................... 678
28

R e a liz a n d o a c e s s o a d a d o s e m p a r a l e l o ...............................681
Utilizando a PLINQ para paralelizar o acesso declarativo
a d a d o s ................................................................................................ 682
Utilizando a PLINQ para melhorar o desempenho
ao iterar sobre uma coleo......................................................... 682
Especificando opes para uma consulta P L IN Q ............................ 687
Cancelando uma consulta PLINQ..................................................... 688

Sumrio

Sincronizando acessos imperativos e simultneos a d a d o s .....................688


Bloqueando dados............................................................................ 691
Primitivas de sincronizao na Task Parallel Lib rary.........................693
Primitivas de cancelamento e sincronizao...................................700
Classes de coleo concorrentes..................................................... 700
Utilizando uma coleo concorrente e um bloqueio
para implementar o acesso a dados thread-safe...........................702
Referncia rpida do Captulo 2 8 ............................................................713

29

Criando e utilizando um Web s e r v ic e ....................................715


O que um Web s e rv ic e ?........................................................................716
O papel do Windows Communication Foundation.........................716
Arquiteturas de Web Service....................................................................716
Web Services S O A P .......................................................................... 717
Web Services REST............................................................................ 719
Construindo Web services........................................................................ 720
Criando o Web Service SOAP Productlnformation...........................721
Web Services SOAP, clientes e proxies............................................. 729
Consumindo o Web Service SOAP Productlnformation.................. 730
Criando o Web service REST ProductDetails..................................... 736
Consumindo o Web service REST ProductDetails .............................743
Referncia rpida do Captulo 29 ............................................................748

Apndice
Interoperabilidade com linguagens d in m ic a s

749

O que o Dynamic Language R u n tim e?................................................. 750


A palavra-chave dynamic .......................................................................... 751
Exemplo: IronPython................................................................................ 752
Exemplo: Iron R ub y.................................................................................. 754
Resumo.....................................................................................................756

ndice

................................................................................................................757

19

Introduo
0 Microsoft Visual C# uma linguagem poderosa e simples, voltada principalmente para os desen
volvedores que criam aplicativos com o Microsoft .NET Framework. Ela herda grande parte dos me
lhores recursos do C++ e Microsoft Visual Basic e pouco das inconsistncias e anacronismos, resul
tando em uma linguagem mais limpa e lgica. O C# 1.0 foi lanado em 2001. O advento do C# com
o Visual Studio 2005 introduziu vrios recursos novos importantes na linguagem, como iteradores
genricos e mtodos annimos. O C# 3.0, lanado com o Visual Studio 2008, acrescentou mtodos
de extenso, expresses lambda e, o mais famoso de todos os recursos, a Language Integrated Query
(LINQ). A incorporao mais recente da linguagem, o C# 4.0, oferece aprimoramentos que melhoram
sua interoperabilidade com outras linguagens e tecnologias. Esses recursos abrangem o suporte
para argumentos nomeados e opcionais; o tipo dynamic (dinmico), o qual indica que o tempo de
execuo da linguagem deve implementar a ligao tardia para um objeto; e a varincia, que resolve
algumas questes relacionadas ao modo como as interfaces genricas so definidas. O C# 4.0 tira
proveito da verso mais recente do .NET Framework, tambm na verso 4.0. As incluses no .NET
Framework mais importantes so as classes e os tipos que constituem a Task Parallel Library (TPL).
Com a TPL, agora voc pode construir, de modo rpido e fcil, aplicativos altamente escalonveis
para processadores multincleo. O suporte para Web services e Windows Communication Founda
tion tambm foi ampliado; atualmente, possvel construir servios que seguem o modelo REST,
assim como o esquema SOAP mais tradicional.
O ambiente de desenvolvimento do Microsoft Visual Studio 2010 facilita o uso de todos esses recur
sos poderosos, e os diversos novos assistentes e melhorias includos na verso 2010 podem aumen
tar consideravelmente a sua produtividade como desenvolvedor.

Para quem este livro?


Este livro destinado a desenvolvedores que desejam aprender os conceitos bsicos da programao
com o C# utilizando o Visual Studio 2010 e o .NET Framework verso 4.0. Nele, voc aprender os
recursos da linguagem C# e os utilizar para criar aplicativos que so executados no sistema opera
cional Microsoft Windows. Ao concluir esta obra, voc ter um entendimento completo do C# e o ter
utilizado para produzir aplicativos do Windows Presentation Foundation, acessar bancos de dados
do Microsoft SQL Server pelo ADO.NET e pelo LINQ, construir aplicativos geis e escalonveis por
meio da TPL, e criar Web services REST e SOAP no WCF.

22

Introduo

Encontrando o melhor ponto de partida


Este livro foi projetado para ajud-lo a desenvolver habilidades em vrias reas essenciais. Voc
pode utiliz-lo se for iniciante em programao ou se estiver migrando de outra linguagem, como C,
C+ + , Java ou Visual Basic. Consulte a tabela a seguir para encontrar seu melhor ponto de partida.

Se voc est
Comeando em programao orientada a objetos

Siga estes passos


1. Instale os arquivos de exerccios conforme
descrito na prxima seo, "Exemplos de
cdigo".
2. Siga os captulos nas Partes I, II e III sequen
cialmente.
3. Complete as Partes IV, V e VI de acordo com
seu nvel de experincia e interesse.

Familiarizado com linguagens de programao


procedurais como C, mas iniciante em C#

1. Instale os arquivos de exerccios conforme


descrito na prxima seo, "Exemplos de
cdigo". Folheie os cinco primeiros captulos
para obter uma viso geral do C# e Visual
Studio 2010 e, em seguida, concentre-se nos
Captulos 6 a 21.
2. Complete as Partes IV, V e VI conforme seu
nvel de experincia e interesse.

Migrando de uma linguagem orientada a objetos


como C++ ou Java

1. Instale os arquivos de exerccios conforme


descrito na prxima seo, "Exemplos de
cdigo".
2. Folheie os sete primeiros captulos para obter
uma viso geral do C# e Visual Studio 2010
e, em seguida, concentre-se nos Captulos 8
a 21.
3. Leia as Partes IV e V para obter informaes
sobre como criar aplicativos Windows e utili
zar um banco de dados.
4. Leia a Parte VI para obter informaes sobre
como criar aplicativos escalonveis e Web
services.

Introduo

Se voc est

23

Siga estes passos

Migrando do Visual Basic 6

1. Instale os arquivos de exerccios conforme


descrito na prxima seo, "Exemplos de
cdigo".
2. Siga os captulos nas Partes I, II e III sequen
cialmente.
3. Leia a Parte IV para obter informaes sobre
como criar aplicativos Windows.
4. Leia a Parte V para obter informaes sobre
como acessar um banco de dados.
5. Leia a Parte IV para obter informaes sobre
como criar aplicativos escalonveis e Web
services.
6. Leia as sees de Referncia rpida no final
dos captulos para obter informaes sobre
questes especficas do C# e construes do
Visual Studio 2010.

Consultando o livro depois de fazer os exerccios

1. Utilize o ndice ou o sumrio para localizar as


informaes sobre assuntos especficos.
2. Leia as sees de Referncia rpida no final
de cada captulo para encontrar uma reviso
sucinta da sintaxe e das tcnicas apresenta
das no captulo.

Convenes e recursos deste livro


Este livro usa algumas convenes para tornar as informaes legveis e fceis de compreender.
Antes de comear, leia a lista a seguir, que explica as convenes que voc ver ao longo da obra e
indica recursos teis que voc talvez queira utilizar.

Convenes
Cada exerccio uma srie de tarefas e cada tarefa apresentada como uma sequncia de etapas
numeradas (1, 2 e assim por diante). Um marcador redondo () indica um exerccio que tem
apenas uma etapa.
As notas marcadas como dica fornecem informaes adicionais ou mtodos alternativos para
completar uma etapa com sucesso.
As notas marcadas como importante alertam para informaes que precisam ser verificadas
antes de continuar.
Textos que voc deve digitar aparecem em negrito.

24

Introduo

Um sinal de adio (+) entre dois nomes de tecla significa que voc deve pressionar essas teclas
ao mesmo tempo. Por exemplo, Pressione Alt+Tab quer dizer que a tecla Alt deve ser pressio
nada ao mesmo tempo que a tecla Tab.

Outros recursos
Quadros ao longo do livro fornecem informaes mais abrangentes sobre o exerccio. Os qua
dros podem conter informaes bsicas, dicas de projeto ou recursos relacionados ao que est
sendo discutido.
Cada captulo termina com uma seo chamada Referncia rpida, que contm lembretes sobre
como executar as tarefas aprendidas no captulo.

Software de pr-lanamento
Este livro foi escrito e testado com o Visual Studio 2010 Beta 2. Revisamos e testamos nossos exem
plos com a ltima verso do software. Entretanto, possvel que voc detecte pequenas diferenas
entre a sua verso e os exemplos, texto e capturas de tela deste livro.

Requisitos de hardware e software


Voc precisar dos seguintes hardware e software para completar os exerccios deste livro:
Microsoft Windows 7 Home Premium, Windows 7 Professional, Windows 7 Enterprise ou W in
dows 7 Ultimate Edition. Os exerccios tambm funcionaro no Microsoft Windows Vista com o
Service Pack 2 ou posterior.
Microsoft Visual Studio 2010 Standard, Visual Studio 2010 Professional ou Microsoft Visual C#
2010 Express e Microsoft Visual Web Developer 2010 Express.
Microsoft SOL Server 2008 Express (fornecido com todas as edies do Visual Studio 2010,
Visual C# 2010 Express e Visual Web Developer 2010 Express).
Processador de 1,6 GHz ou mais rpido. Os Captulos 27 e 28 exigem um processador dual-core
ou superior.
1 GB de RAM fsica disponvel para um processador X32, 2 GB para um processador X64.
Monitor de vdeo (com resoluo de 1024 x 768 ou mais alta) com pelo menos 256 cores.
Unidade de CD-ROM ou DVD-ROM.
Mouse Microsoft ou dispositivo indicador compatvel.
Voc tambm precisar ter acesso de administrador ao seu computador para configurar o SOL Server
2008 Express Edition.

Introduo

25

Exemplos de cdigo
0 CD que acompanha este livro contm exemplos de cdigo que voc deve usar para realizar os exer
ccios. Assim, voc no desperdiar tempo para criar arquivos que no so relevantes execuo do
exerccio. Os arquivos e as instrues passo a passo tambm permitem aprender exercitando, de um
modo fcil e eficaz, adquirindo e lembrando novas habilidades.

Instalando os exemplos de cdigo


Siga estes passos para instalar os exemplos de cdigo e o software necessrio no computador a fim
de us-los com os exerccios.
1. Remova o CD localizado no final do livro e o insira na unidade de CD-ROM.

2. Leia o contrato de licena para o usurio final. Se concordar com os termos, selecione a opo
para aceitar e clique em Next.
Um menu com opes relacionadas ao livro aparece.
3. Clique em Instalar Exemplos de Cdigo.
4. Siga as instrues que aparecem. Os exemplos de cdigo so instalados no seu computador no
seguinte local:

Documentos\Microsoft PressWisual CSharp Step by Step

Utilizando os exemplos de cdigo


Todos os captulos explicam quando e como usar os exemplos de cdigo. Ouando for o momento de
usar um exemplo de cdigo, o livro listar as instrues sobre como abrir os arquivos.
Para quem gosta de conhecer todos os detalhes, segue uma lista dos projetos e das solues do Visual
Studio 2010 contendo exemplos de cdigo, agrupada pelas pastas em que voc pode localiz-los. Em
diversos casos, os exerccios fornecem arquivos provisrios e verses completas dos mesmos proje
tos, que voc pode utilizar como referncia. Os projetos concludos so armazenados em pastas com
o sufixo - Completed .

26

Introduo

Projeto

Descrio

Captulo 1
TextHello

Guia voc passo a passo ao longo do processo de criao


de um programa simples exibindo uma saudao baseada
em texto.

WPFHello

Exibe a saudao em uma janela, utilizando o Windows


Presentation Foundation.

Captulo 2
PrimitiveDataTypes

Demonstra como declarar variveis de cada um dos tipos


primitivos, como atribuir valores a essas variveis e como
exibi-los em uma janela.

MathsOperators

Apresenta os operadores aritmticos (+ - * / %).

Captulo 3
Methods

Reexamina o cdigo do projeto anterior e investiga como


so empregados os mtodos para estruturar o cdigo.

DailyRate

Ensina a escrever e executar seus prprios mtodos e a


inspecionar passo a passo as chamadas de mtodo utili
zando o depurador do Visual Studio 2010.

DailyRate usando parmetros opcionais

Mostra como definir um mtodo que aceita parmetros


opcionais e como cham-lo por meio de argumentos
nomeados.

Captulo 4
Selection

Mostra como utilizar uma instruo if em cascata para


implementar uma lgica complexa, como comparar a
equivalncia de duas datas.

SwitchStatement

Utiliza uma instruo switch para converter caracteres em


suas representaes XML.

Captulo 5
WhileStatement

Demonstra uma instruo while que l o contedo de


cada linha de um arquivo-fonte e o exibe em uma caixa
de texto em um formulrio.

DoStatement

Esse projeto utiliza uma instruo do para converter um


nmero decimal em sua representao octal.

Introduo

Projeto

27

Descrio

Capitulo 6
MathsOperators

Revisita o projeto MathsOperators do Captulo 2, "Traba


lhando com variveis, operadores e expresses" e mostra
como vrias excees no tratadas podem fazer o progra
ma falhar. As palavras-chave f/y e catch tornam o aplicati
vo mais robusto, evitando que ocorram mais falhas.

Capitulo 7
Classes

Aborda os fundamentos da definio de suas prprias


classes, incluindo construtores pblicos, mtodos e cam
pos privados. Alm disso, mostra como criar instncias
de classe utilizando a palavra-chave new e como definir
mtodos e campos estticos.

Capitulo 8
Parameters

Investiga a diferena entre os parmetros por valor e os


parmetros por referncia e demonstra como utilizar as
palavras-chave ref eout.

Capitulo 9
StructsAndEnums

Define um tipo de estrutura para representar uma data de


calendrio.

Capitulo 10
Cards Using Arrays

Mostra como utilizar arrays para modelar mos de cartas


em um jogo de cartas.

Cards Using Collections

Ilustra como reestruturar o programa de jogo de cartas


para utilizar colees em vez de arrays.

Capitulo 11
ParamsArrays

Demonstra como utilizar a palavra-chave params para


criar um nico mtodo que aceite todos os argumentos

int.
Capitulo 12
Vehicles

Cria uma hierarquia simples de classes de veculos uti


lizando herana. Tambm demonstra como definir um
mtodo virtual.

ExtensionMethod

Mostra como produzir um mtodo de extenso para o


tipo int, fornecendo um mtodo que converte um valor
inteiro de base 10 em uma base numrica diferente.
(Continua)

28

Introduo

Projeto

Descrio

Captulo 13
Drawing Using Interfaces

Implementa parte de um pacote de desenho grfico.


0 projeto utiliza interfaces para definir os mtodos que as
formas de desenho expem e implementam.

Drawing

Estende o projeto Drawing Using Interfaces para fatorar a


funcionalidade comum de objetos de forma em classes.

Captulo 14
UsingStatement

Rev uma parte do cdigo do Captulo 5, "Utilizando atri


buio composta e instrues de iterao", revelando que
o cdigo no seguro quanto a excees e mostrando
como torn-lo seguro com uma instruo using.

Captulo 15
WindowProperties

Apresenta um aplicativo Windows simples que utiliza


diversas propriedades para exibir o tamanho da sua janela
principal. As atualizaes so exibidas automaticamente
enquanto o usurio redimensiona a janela.

AutomaticProperties

Mostra como criar propriedades automticas para uma


classe e as utiliza para inicializar instncias da classe.

Captulo 16
Indexers

Utiliza dois indexadores: um procura o nmero de telefo


ne de uma pessoa quando um nome fornecido, e o ou
tro procura o nome de uma pessoa quando um nmero
de telefone fornecido.

Captulo 17
Clock Using Delegates

Exibe um Relgio Internacional (World Clock) que indica a


hora local, assim como as horas em Londres, Nova York e
Tquio. 0 aplicativo utiliza delegates para iniciar e inter
romper as exibies do relgio.

Clock Using Event

Esta verso do aplicativo World Clock usa eventos para


iniciar e parar a exibies do relgio.

Capitulo 18
BinaryTree

Mostra como empregar genricos para criar uma estru


tura segura para tipos que possa conter elementos de
qualquer tipo.

BuildTree

Demonstra como utilizar genricos para implementar


um mtodo typesafe que possa receber parmetros de
qualquer tipo.

BinaryTreeTest

Agente de teste que cria instncias do tipo Tree definido


no projeto BinaryTree.

Introduo

Projeto

29

Descrio

Captulo 19
BinaryTree

Mostra como implementar a interface genrica

IEnumerator<T> para criar um enumerador para a classe


genrica Tree.
IteratorBinaryTree

Utiliza um iterador para gerar um enumerador para a


classe genrica Tree.

EnumeratorTest

Agente de teste que testa o enumerador e o iterador da


classe Tree.

Captulo 20
QueryBinaryTree

Mostra como utilizar consultas LINQ para recuperar da


dos de um objeto de rvore binria.

Captulo 21
ComplexNumbers

Define um novo tipo que modela nmeros complexos e


implementa operadores comuns para esse tipo.

Captulo 22
BelIRingers

Aplicativo Windows Presentation Foundation que de


monstra como definir estilos e utilizar controles WPF
bsicos.

Captulo 23
BelIRingers

Extenso do aplicativo criado no Captulo 22, "Apresen


tando o Windows Presentation Foundation", mas com
menus suspensos e menus pop-up adicionados interfa
ce do usurio.

Captulo 24
OrderTickets

Demonstra como implementar regras de negcio para


validar a entrada do usurio em um aplicativo WPF utili
zando, por exemplo, informaes de clientes.

Captulo 25
ReportOrders

Mostra como acessar um banco de dados utilizando c


digo ADO.NET. 0 aplicativo recupera as informaes da
tabela Orders no banco de dados Northwind.

LINQOrders

Ilustra como utilizar o LINQ para SQL, para acessar um


banco de dados e recuperar informaes da tabela Orders
no banco de dados Northwind.
(Continua)

30

Introduo

Projeto

Descrio

Capitulo 26
Suppliers

Demonstra como utilizar a vinculao de dados com um


aplicativo WPF para exibir e formatar dados recuperados
de um banco de dados em controles em um formulrio
WPF. 0 aplicativo tambm permite ao usurio modificar
informaes na tabela Products no banco de dados
Northwind.

Capitulo 27
GraphDemo

Gera e exibe um grfico complexo em uma forma WPF.


Utiliza um nico thread para efetuar os clculos.

GraphDemo Using Tasks

Verso do projeto GraphDemo que cria vrias tarefas para


efetuar os clculos do grfico simultaneamente.

GraphDemo Using Tasks that Reurn Results

Verso estendida do projeto GraphDemo Using Tasks que


demonstra como retornar dados de uma tarefa.

GraphDemo Using the Parallel Class

Verso do projeto GraphDemo que usa a classe Parallel


para abstrair o processo de criao e gerenciamento de
tarefas.

GraphDemo Canceling Tasks

Demonstra como implementar o cancelamento para


interromper tarefas de modo controlado, antes de sua
concluso.

ParallelLoop

Fornece um exemplo de quando voc no deve utilizar a


classe Parallel para criar e executar tarefas.

Capitulo 28
CalculatePI

Utiliza um algoritmo de amostragem estatstica para cal


cular uma aproximao de PI. Usa tarefas paralelas.

PLINQ

Apresenta alguns exemplos de como utilizar o PLINQ para


consultar dados por meio de tarefas paralelas.

Capitulo 29
ProductlnformationService

Implementa um Web Service SOAP construdo por meio


do WCF. 0 Web Service expe um mtodo que retorna
informaes de preos de produtos do banco de dados
Northwind.

ProductDetailsService

Implementa um Web Service REST construdo por meio do


WCF. 0 Web Service fornece um mtodo que retorna os
detalhes de um produto especificado do banco de dados
Northwind.

ProductDetailsContract

Contm os contratos de servios e dados implementados


pelo Web Service ProductDetailsService.

ProductClient

Ensina a criar um aplicativo WPF que consome um Web


Service, e mostra como chamar os mtodos Web nos Web
services ProductlnformationService e ProductDetailsSer
vice.

Introduo

31

Desinstalando os exemplos de cdigo


Siga estes passos para remover os exemplos de cdigo do computador.
1. No Painel de Controle, em Programas e Recursos, clique em Desinstalar um programa.
2. A partir da lista programas atualmente instalados, selecione Microsoft Visual Basic 2010 Passo
a Passo.
3. Clique em Desinstalar.
4. Siga as instrues que aparecem para remover os exemplos de cdigo.

Contedo adicional online


Materiais novos ou atualizados que complementam este livro sero publicados no site Microsoft
Press Online Developer Tools. O tipo de material inclui atualizaes no contedo do livro, artigos,
links para contedo suplementar, errata, exemplos de captulo e muito mais. Este site est disponvel
em www.microsoft.com/learning/books/online/developer, e atualizado periodicamente.

Suporte para este livro*


Todo esforo foi feito para garantir a preciso deste livro e do contedo do CD que o acompanha.
medida que correes ou alteraes forem detectadas, elas sero adicionadas a um artigo do M i
crosoft Knowledge Base.
A Microsoft Press fornece suporte aos seus livros e CDs neste site:

http://www.microsoft.com/learning/support/books/.

Perguntas e comentrios
Se voc tem comentrios, perguntas ou ideias referentes ao livro ou ao CD, ou perguntas que no
foram respondidas ao visitar os sites acima, envie-as por e-mail (em ingls) para a Microsoft Press:

mspinput@microsoft. com
Suporte a produtos Microsoft no oferecido pelo endereo citado acima.

* N. de E.: Comentrios sobre a edio brasileira desta obra podem ser encam inhados para secretariaeditorial@ grupoaeditoras.
com.br.

M ic ro so ft V isu a l C # 2 0 1 0 P asso a P asso

Parte I

Apresentando o Microsoft
Visual C# e o Microsoft Visual
Studio 2010
"

"

"

Captulo 1:

Bem-vindo ao C#

... .... .....................................

.................................................................

"

35

Captulo 2:

Trabalhando com variveis, operadores e expresses..........

59

Captulo 3:

Escrevendo mtodos e aplicando esco p o ............................

79

Captulo 4:

Utilizando instrues de deciso.......................................... 105

Captulo 5:

Utilizando atribuio composta e instrues de iterao . . . 123

Captulo 6:

Gerenciando erros e excees.............................................. 141

"

Captulo 1

Bem-vindo ao C#
Neste captulo, voc vai aprender a:
Utilizar o am biente de program ao do Microsoft Visual Studio 2010.
Criar um aplicativo console em C#.
Explicar o objetivo dos namespaces.
Criar um aplicativo grfico simples em C#.

O Microsoft Visual C# a mais poderosa linguagem orientada para componentes da Microsoft. O C#


desempenha um papel importante na arquitetura do Microsoft .NET Framework, sendo comparado,
algumas vezes, funo que o C desempenhou no desenvolvimento do UNIX. Se voc j conhece
uma linguagem como C, C+ + ou Java, notar que a sintaxe do C# , para a sua tranquilidade,
muito familiar. Se est acostumado a programar em outras linguagens, deve conseguir assimilar
rapidamente a sintaxe e o modo de trabalhar no C#; basta aprender a colocar as chaves e os ponto e
vrgulas no lugar. Este o livro certo para ajud-lo!
Na Parte I, voc aprender os fundamentos do C#. Descobrir como declarar variveis e como utilizar
operadores aritmticos como o sinal de adio (+) e o sinal de subtrao (-) para manipular os valo
res em variveis.Ver como escrever mtodos e passar argumentos para eles. Alm disso, aprender
a utilizar as instrues de seleo como j/e as instrues de iterao como while. Por fim, entender
como o C# utiliza as excees para manipular os erros de maneira simples e fcil. Esses tpicos so
uma introduo ao C# e, a partir dela, voc vai progredir para recursos mais avanados, da Parte II
at a Parte VI.

Comeando a programar com o ambiente do Visual


Studio 2010
O Visual Studio 2010 um ambiente de programao rico em recursos que contm a funcionalidade
necessria para criar projetos grandes ou pequenos em C#. Voc pode inclusive construir projetos
que combinem mdulos de diferentes linguagens, como C+ + , Visual Basic e F#. No primeiro exerc
cio, voc ir abrir o ambiente de programao do Visual Studio 2010 e aprender a criar um aplicativo
de console.

36

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Crie um aplicativo de console no Visual Studio 2010


Se voc estiver utilizando o Visual Studio 2010 Standard ou o Visual Studio 2010 Professional,
siga estes passos para iniciar o Visual Studio 2010:
1. No Microsoft Windows, clique no boto Iniciar, aponte para Todos os Programas e, em
seguida, aponte para o grupo de programas Microsoft Visual Studio 2010.
2. No grupo de programas Microsoft Visual Studio 2010, clique em Microsoft Visual Studio

2 010 .
O Visual Studio 2010 inicia, como mostrado a seguir:

Se voc estiver utilizando o Visual C# 2010 Express, na barra de tarefas Microsoft Windows,
clique no boto Iniciar, aponte para Todos os Programas e clique em Microsoft Visual C# 2010

Express.
O Visual C# 2010 Express inicia, como mostrado a seguir:

Captulo 1

i J _J .J A A h -J ^ - P* l -

Nota

Bem-vindo ao C#

37

-j :

Na primeira execuo do Visual C# 2010, ser exibida uma caixa de dilogo solicitando

que voc escolha as configuraes padro do ambiente de desenvolvimento. Selecione Expert

Settings na lista e clique no boto Start Visual Studio. Aps alguns instantes, o IDE do Visual
C# 2010 exibido.

Nota

Para no ser repetitivo, eu apenas escrevo "Inicie o Visual Studio" quando voc precisa

abrir o Visual Studio 2010 Standard, o Visual Studio 2010 Professional ou o Visual C# 2010 Ex
press. Alm disso, a menos que dito explicitamente, todas as referncias ao Visual Studio 2010
se aplicam ao Visual Studio 2010 Standard, ao Visual Studio 2010 Professional e ao Visual C#
2010 Express.

Se voc estiver utilizando o Visual Studio 2010 Standard ou o Visual Studio 2010 Professional,
siga estes passos para criar um novo aplicativo de console.
1. No menu File, aponte para New e clique em Project.
A caixa de dilogo New Project abre. Ela lista os templates que voc pode utilizar como
ponto de partida para construir um aplicativo. A caixa de dilogo categoriza os templates
de acordo com a linguagem de programao que voc est utilizando e o tipo de aplicativo.
2. No painel da esquerda, em Installed Templates, clique em Visual C#. No painel central,
verifique se a caixa de combinao posicionada no incio do painel exibe o texto .NETFramework 4.0 e depois clique no cone Console Application. Talvez seja necessrio fazer uma
rolagem no painel central para visualizar esse cone.

38

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

___ l l j

NET Framework 4_____ -j Sort by: |Default__________

Recent Templates

Empty ASP.NET Web Application Visual C#

Installed Templates

Silverlight Application

Silverlight Navigation ApplicationVisual C#

Windows
Web
i> Office
Cloud Service
Reporting

Silverlight Class Library

i SharePoint
Silverlight
Test
WCF
Workflow
i Database
I Other Languages

Solution Nam!

Visual C#

ASP.NET Web Service ApplicationVisual C#

WPF Application

WPF Browser Application

Visual C#

Console Application

Visual C#

WCF Service Application

Visual C#

3 2

location:

pj I

Console Application
Type: Visual C#

Visual C#

*Visual C#

time:

Search Installed
stalled Tempi;

m Aprt
I

ojectfor creating a command-line


application
appli

Visual C#

TextHello
I C:\Users\John\Documents\MicrosoftPress\Visual C# Step By Step\Cha|
TextHello

[/j Create directory for solution

3. No campo Location, se voc estiver usando o Windows Vista, digite CAUsersVyeuiVo/n^l


Documentos\Microsoft PressW isual CSharp STep B y Step\Chapter 1. Se estiver uti
lizando o Windows 7, digite CAUsersVS^WiVcvw^Meus Documentos\Microsoft Press\
V isu al CSharp Step B y Step\Chapter 1. Substitua o texto SeuNome nesses caminhos
pelo seu nome de usurio do Windows.

4. No campo Name, digite TextHello.


5. Certifique-se de que a caixa de seleo Create directoryjor solution est selecionada e clique
em OK.
Se voc estiver utilizando o Visual C# 2010 Express, siga estes passos para criar um novo apli
cativo de console.
1. No menu File, clique em New Project.
2. Na caixa de dilogo New Project, no painel central, clique no cone Console Application.
3. No campo Name, digite TextHello.

Captulo 1 Bem-vindo ao C#

39

4. Clique em OK.
Por padro, o Visual C# 2010 Express salva as solues na pasta C:\User\SeuNome\AppData\Local\Temporary Projects. Ao salvar uma soluo, voc pode especificar outro local.
5. No menu File, clique em Save TextHello As.
6. Na caixa de dilogo Save Project, no campo Location, informe a pasta M icrosoft Press\
Visual CSharp Step B y Step\Chapter 1, na pasta Documentos.
7. Clique em Save.

O Visual Studio cria o projeto utilizando o template Console Application e exibe o cdigo bsico para
o projeto, como na ilustrao:

TextHello - Microsoft Visual Studio


file

Edit

^ew

I .J 3 * _ J * - J

Eroject

guild

4 * 1 J

Debug

Djta

Iools

j|TextHello.Program

Mnamespace TextHello

>

telp

Solution Explorer
FH 'v - r r &
Mi JJ l

? X

3 TextHello
& B
Properties
> o
M
References
App.config
j|| Program.cs

! using System.Collections.Generic;
| using System.Linq;
[using System.Text;

Window

*j ^Main(string[] argj)

"BIusing System;

[{
class Program
l < static void Main(string[]

Tejt

j Debug

>

Project File
The name of the file containing build,
configuration, and other information about the .

A barra de menus na parte superior da tela fornece acesso aos recursos que voc utilizar no am
biente de programao. Voc pode usar o teclado ou o mouse para acessar os menus e os comandos,
exatamente como faz em todos os programas baseados em Windows. A barra deferramentas est
localizada abaixo da barra de menus e oferece botes de atalho para executar os comandos utiliza
dos com mais frequncia.
O painel Code and Text Editor, que ocupa a parte principal do IDE, exibe o contedo dos arquivos-fonte. Em um projeto multiarquivo, quando voc edita mais de um arquivo, cada arquivo-fonte
tem uma guia prpria com seu nome. Voc pode clicar na guia para trazer o arquivo-fonte nomeado
para o primeiro plano na janela Code and Text Editor. O painel Solution Explorer (no lado direito da
caixa de dilogo) exibe os nomes dos arquivos associados ao projeto, entre outros itens. Voc pode
clicar duas vezes em um nome de arquivo no painel Solution Explorer para trazer esse arquivo-fonte
para o primeiro plano na janela do Code and Text Editor.

40

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Antes de escrever o cdigo, examine os arquivos listados no Solution Explorer, criados pelo Visual
Studio 2010 como parte do seu projeto:

Solution 'TextHello'

o arquivo de soluo de nvel superior, e h apenas um por aplicati


vo. Se utilizar o Windows Explorer para examinar a pasta Documentos\Microsoft\VisualCSharp
Step By Step\Chapter l\TextHello, voc ver que o nome real desse arquivo TextHello.sln. Cada
arquivo de soluo contm referncias a um ou mais arquivos de projeto.

TextHello

o arquivo de projeto do C#. Cada arquivo de projeto faz referncia a um ou mais


arquivos que contm o cdigo-fonte e outros itens do projeto. Todos os cdigos-fonte de um
mesmo projeto devem ser escritos na mesma linguagem de programao. No Windows Explorer,
esse arquivo se chama TextHello.csproj e est armazenado na pasta \Microsoft PressWisual
CSharp Step By Step\Chapter l\TextHello\TextHello.

Properties

uma pasta do projeto TextHello. Se for expandida, voc ver que ela contm
um arquivo chamado AssemblyInfo.es. Esse um arquivo especial que voc pode utilizar para
adicionar atributos a um programa, como o nome do autor, a data em que o programa foi escri
to, etc. Voc pode especificar atributos adicionais para modificar a maneira como o programa
executado. Aprender a utilizar esses atributos est alm do escopo deste livro.

References

uma pasta que contm as referncias ao cdigo compilado que seu aplicativo
pode utilizar. Quando o cdigo compilado, ele convertido em um cdigo assembly e recebe
um nome exclusivo. Desenvolvedores utilizam assemblies para empacotar cdigos teis que
escreveram, podendo distribu-los para outros desenvolvedores que queiram utiliz-los nos seus
aplicativos. Muitos dos recursos que voc utilizar ao escrever os aplicativos propostos por este
livro usam os cdigos assembly fornecidos pela Microsoft com o Visual Studio 2010.

App.conf ig

o arquivo de configurao de aplicativo. possvel especificar durante a execuo


as configuraes que seu aplicativo pode utilizar para modificar seu comportamento, como a verso
do .NET Framework a ser utilizada para executar o aplicativo. Voc conhecer outros detalhes sobre
este aplicativo nos captulos posteriores deste livro.

Program.cs

um arquivo-fonte do C# exibido na janela Code and Text Editor quando o


projeto criado pela primeira vez. Voc escrever seu cdigo para o aplicativo de console nesse
arquivo. Ele contm um cdigo que o Visual Studio 2010 fornece automaticamente, o qual ser
examinado a seguir.

Escrevendo seu primeiro programa


O arquivo Program.cs define uma classe chamada Program que contm um mtodo chamado Main.
Todos os mtodos devem ser definidos dentro de uma classe. Voc aprender mais sobre classes no
Captulo 7, Criando e gerenciando classes e objetos . O mtodo Main especial - ele designa o ponto
de entrada do programa e deve ser um mtodo esttico. (Veremos mtodos em detalhes no Captulo 3,
Escrevendo mtodos e aplicando escopo , e o Captulo 7 descreve os mtodos estticos.)

Captulo 1 Bem-vindo ao C#

Importante

41

O C# uma linguagem que diferencia maisculas de minsculas. Voc deve escre

ver Main com um

M maisculo.

Nos exerccios a seguir, voc ir escrever um cdigo para exibir a mensagem Hello World no con
sole; compilar e executar seu aplicativo de console Hello World; e aprender como os namespaces so
utilizados para dividir elementos do cdigo.

Escreva o cdigo utilizando o M icrosoft IntelliSense


1. Na janela Code and Text Editor que exibe o arquivo Program.cs, coloque o cursor no mtodo
Main logo aps a chave de abertura { e pressione Enter para criar uma nova linha. Nessa nova
linha, digite a palavra Console, que o nome de uma classe predefinida. Ao digitar a letra C no
incio da palavra Console, uma lista IntelliSense aparecer. Essa lista contm todas as palavras-chave vlidas do C# e os tipos de dados vlidos nesse contexto. Voc pode continuar digitando
ou rolar pela lista e clicar duas vezes no item Console com o mouse. Como alternativa, depois
de digitar Con, a lista IntelliSense voltar automaticamente para o item Console e voc poder
pressionar as teclas Tab ou Enter para selecion-la.
O cdigo Main deve ser semelhante a este:
static void Main(string[] args)

{
Console

2. Digite um ponto logo aps Console. Outra lista IntelliSense aparece exibindo os mtodos, pro
priedades e campos da classe Console.
3. Role para baixo pela lista, selecione WriteLine e ento pressione Enter. Voc tambm pode con
tinuar a digitar os caracteres W, r, i, t, e, L at WriteLine estar selecionado e ento pressionar
Enter.
A lista IntelliSense fechada, e a palavra WriteLine adicionada ao arquivo-fonte. Main deve
se parecer com isto:
static void Main(string[] args)

{
Console.Wri teLi ne

}
4. Digite um parntese de abertura, (. Outra dica do IntelliSense aparece.
Essa dica exibe os parmetros que o mtodo WriteLine pode receber. De fato, WriteLine um m
todo sobrecarregado, ou seja, a classe Console contm mais de um mtodo chamado WriteLine

42

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

- na verdade ela fornece 19 verses diferentes desse mtodo. Cada verso do mtodo WriteLine
pode ser utilizada para emitir diferentes tipos de dados (o Captulo 3 descreve mtodos sobre
carregados em mais detalhes). Main deve se parecer com isto:
static void Main(string[] args)

{
Console .WriteLi ne(

Dica Voc pode clicar nas setas para cima e para baixo na dica para rolar pelas diferentes
sobrecargas de WriteLine.

5. Digite um parntese de fechamento, seguido por um ponto e vrg u la,;.

Main deve se parecer com isto:


static void Main(string[] args)

{
Console.WriteLineO;

}
6. Mova o cursor e digite a string H ello W orld , incluindo as aspas entre os parnteses esquerdo
e direito depois do mtodo WriteLine.

Main deve se parecer com isto:


static void Main(string[] args)

{
Console.WriteLine("Hello World");

Dica

Adquira o hbito de digitar pares de caracteres correspondentes, como (e) e { e }, antes

de preencher seus contedos. fcil esquecer o caractere de fechamento se voc esperar para
digit-lo depois de inserir o contedo.

c o n e s In te lliS e n s e
Quando voc digita um ponto depois do nome de uma classe, o IntelliSense exibe o nome
de cada membro dessa classe. esquerda de cada nome de membro h um cone que repre
senta o tipo de membro. Os cones mais comuns e seus tipos so:

cone

Significado
mtodo (Captulo 3)
propriedade (Captulo 15, "Implementando propriedades para acessar campos")

(continua)

Captulo 1

cone

Bem-vindo ao C#

43

Significado
classe (Captulo 7)

estrutura (Captulo 9, "Criando tipos-valor com enumeraes e estruturas")

enumerao (Captulo 9)

m
0

interface (Captulo 13, "Criando interfaces e definindo classes abstratas")


delegate (Captulo 17, "Interrompendo o fluxo do programa e tratando eventos")

mtodo de extenso (Captulo 12, "Trabalhando com herana")

%
Outros cones IntelliSense aparecero medida que voc digitar o cdigo em contextos diferentes.

1. No menu Build, clique em Build Solution.


Essa ao compila o cdigo C#, resultando em um programa que pode ser executado. A janela
Output aparece abaixo da janela Code and Text Editor.

Dica

Se a janela Output no aparecer no menu View, clique em Output para exibi-la.

44

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Nessa janela, voc deve ver mensagens semelhantes s seguintes indicando como o programa est
sendo compilado.
------ Build started: Project: TextHello, Configuration: Debug x86 ---CopyFileToOutputDi rectory:
TextHello -> C:\Users\John\Meus Documentos\Microsoft Press\Visua1 CSharp Step By Step
\Chapter l\TextHel1o\TextHello\bi n\Debug\TextHello.exe
========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ========

Qualquer erro que voc cometer aparecer na janela Error List. A imagem a seguir mostra o que
acontece se voc esquecer de digitar as aspas de fechamento depois do texto Hello World na instru
o WriteLine. Observe que um nico erro s vezes pode causar vrios erros de compilador.

TextHello -Microsoft Visual Studio


Eile

dit

! .j l *

iew

Rroject

guild

ebug

-Ji3A \A-J -3> !

Djta

Iools

V - 43

Tejt

Window

M M tnw i

m m

Help

Debug

-j j x

[fJ

Program.cs

30.
" using
using
; using
i using

j ] Solution TextHello (1 project)


TextHello
r> h Properties
t>
References
App.config
Program.cs

System;
System.Collections.Generic;
System.Linq;
System.Text;

*3

~ namespace TextHello

class Program

static void Main(string[] args)

{
>
O 3 Errors |

Console. Ur iteLine(t tJg iiS lJiS E jjtli

0Warnings j

Description

m at

) 0 Messages
File

Line

Column

Project

O l

Newline in constant

Program.cs

12

31

TextHello

02

; expected

Program.es

12

45

TextHello

03

) expected

Program.cs

12

45

TextHello

Dica

Voc pode clicar duas vezes em um item na janela Error List, e o cursor ser posicionado na

linha que causou o erro. Observe tambm que o Visual Studio exibe uma linha vermelha ondulada
sob qualquer linha de cdigo que no ser compilada quando voc a inserir.

Se voc seguiu as instrues anteriores cuidadosamente, no haver erro ou aviso algum, e o progra
ma dever ser compilado com sucesso.

<5>

Dica

No h necessidade de salvar o arquivo exatamente antes de compil-lo porque o comando

Build Solution faz o salvamento automtico.


Um asterisco aps o nome do arquivo na guia acima da janela Code and Text Editor indica que o
arquivo foi alterado aps ter sido salvo pela ltima vez.

Captulo 1 Bem-vindo ao C#

45

2. No menu Debug, clique em Start Without Debugging para compilar e executar o aplicativo.
Uma janela de comandos aberta e o programa executado. A mensagem Hello World exi
bida, e o programa espera o usurio pressionar uma tecla, como mostra a ilustrao a seguir:

3. Verifique se a janela de comandos que exibe a sada do programa tem o foco, e, em seguida,
pressione Enter.
A janela de comandos fechada, e voc retorna ao ambiente de programao do Visual Studio

2010 .
4. No Solution Explorer, clique no projeto TextHello (no na soluo) e depois no boto da barra de

ferramentas Show Ali Files, na barra de ferramentas Solution Explorer - esse o boto posicio
nado na extremidade esquerda na barra de ferramentas da janela Solution Explorer.
Show All Files
Solution Explorer

- i} X

Solution 'TextHello' (1 project)


TextHello

t> 1M
:

Properties

t>

References
[ijp App.config
c^ ) Program.cs

As entradas nomeadas bin e obj aparecem acima do arquivo Program.cs. Essas entradas cor
respondem diretamente s pastas chamadas bin e obj na pasta do projeto (Microsoft Press\
VisualCSharp STep By Step\Chapter l\TextHello\TextHello). O Visual Studio as cria quando voc
compila seu aplicativo, e estas contm a verso executvel do programa e alguns outros arqui
vos utilizados para compilar e depurar o aplicativo.
5. No Solution Explorer, expanda a entrada bin.
Outra pasta chamada Debug exibida.

46

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Nota

Voc tambm ver uma pasta chamada Release.

6. No Solution Explorer, expanda a pasta Debug.


Quatro outros itens chamados TextHello.exe, TextHello.pdb, TextHello.vshost.exe e TextHello.
vshost.exe. manifest aparecem.
O programa compilado o arquivo TextHello.exe, que executado quando voc clica em Start
Without Debugging no menu Debug. Os outros dois arquivos contm informaes que so utili
zadas pelo Visual Studio 2010, se voc executar o programa no modo Debug (quando voc clica
em Start Debugging no menu Debug).

Utilizando namespaces
O exemplo que vimos at aqui o de um programa muito pequeno. Mas programas pequenos podem
crescer bastante. medida que o programa se desenvolve, duas questes surgem. Primeiro, mais
difcil entender e manter programas grandes do que programas menores. Segundo, mais cdigo
normalmente significa mais nomes, mais mtodos e mais classes. Conforme o nmero de nomes
aumenta, tambm aumenta a probabilidade de a compilao do projeto falhar porque dois ou mais
nomes entram em conflito (especialmente quando um programa tambm usa bibliotecas escritas por
outros desenvolvedores, os quais tambm utilizaram uma variedade de nomes).
Antigamente, os programadores tentavam resolver o conflito prefixando os nomes com algum tipo
de qualificador (ou conjunto de qualificadores). Essa soluo no boa porque no expansvel; os
nomes tornam-se maiores, e voc gasta menos tempo escrevendo o software e mais tempo digitando
(h uma diferena), e lendo e relendo nomes longos e incompreensveis.
Os namespaces ajudam a resolver esse problema criando um continer nomeado, para outros iden
tificadores, como classes. Duas classes com o mesmo nome no sero confundidas se elas estiverem
em namespaces diferentes. Voc pode criar uma classe chamada Creeting em um namespace chama
do TextHello, como mostrado a seguir:
namespace TextHello

{
class Creeting

{
}
}
Voc pode ento referenciar a classe Greeting como TextHello.Greeting nos seus prprios programas.
Se outro desenvolvedor tambm criar uma classe Greeting em um namespace diferente, como NewNamespace, e instal-la no seu computador, seus programas ainda funcionaro conforme o espera
do, pois usaro a classe TextHello.Greeting. Se quiser referenciar a classe Greeting do outro desenvol
vedor, voc dever especific-la como NewNamespace. Greeting.

Captulo 1 Bem-vindo ao C#

47

uma boa prtica definir todas as suas classes em namespaces, e o ambiente do Visual Studio 2010
segue essa recomendao utilizando o nome do seu projeto como o namespace de nvel mais alto. A
biblioteca de classes do .NET Framework tambm segue essa recomendao; toda classe no .NET Fra
mework est situada em um namespace. Por exemplo, a classe Console reside no namespace System.
Isso significa que seu nome completo , na verdade, System.Console.
Porm, se voc tivesse que escrever o nome completo de uma classe sempre que ela fosse utilizada,
seria melhor prefixar qualificadores ou simplesmente atribuir classe um nome globalmente nico
como SystemConsole, sem se incomodar com um namespace. Felizmente, possvel resolver esse
problema com uma diretiva using nos seus programas. Se voc retornar ao programa TextHello no
Visual Studio 2010 e examinar o arquivo Program.cs na janela Code and Text Editor, notar as se
guintes instrues no incio do arquivo:
using
usi ng
using
using

System;
System.Col 1ecti o n s .Generi c ;
System.Linq;
System.Text;

Uma instruo using adiciona um namespace ao escopo. No cdigo subsequente, no mesmo arquivo,
voc no tem mais que qualificar explicitamente os objetos com o namespace ao qual eles pertencem.
Os quatro namespaces mostrados contm classes utilizadas com tanta frequncia que o Visual Stu
dio 2010 adiciona essas instrues using automaticamente toda vez que voc cria um novo projeto.
Voc pode adicionar outras diretivas using na parte superior de um arquivo-fonte.
O exerccio a seguir demonstra o conceito dos namespaces com mais detalhes.

Experim ente os nomes longos


1. Na janela Code and Text Editor que exibir o arquivo Program.cs, comente a primeira diretiva
using na parte superior do arquivo, desta maneira:
// using System;

2. No menu Build, clique em Build Solution. A compilao falha e a janela Error List exibe a se
guinte mensagem de erro:
The name 'Console' does not exist in the current context.

3. Na janela Error List, clique duas vezes na mensagem de erro. O identificador que causou o erro
destacado no arquivo-fonte Program.cs.
4. Na janela Code and Text Editor, edite o mtodo Main para utilizar o nome completo System.

Console.
O cdigo Main deve ser semelhante a este:
static void Main(string[] args)

{
System.Console.WriteLineC'Hello World") ;

48

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Nota

Quando voc digita

System,

os nomes de todos os itens no namespace

System

so

exibidos pelo IntelliSense.

5. No menu Build, clique em Build Solution.

A compilao deve ser bem-sucedida desta vez. Se no for, certifique-se de que o cdigo Main
est exatamente como aparece no cdigo precedente e, em seguida, tente recompilar novamente.
6. Execute o aplicativo para verificar se ele ainda funciona clicando em Start Without Debugging
no menu Debug.

N a m e sp a c e s e a s s e m b lie s
Uma instruo using coloca em escopo os itens de um namespace, e voc no precisa qua
lificar completamente os nomes das classes no seu cdigo. As classes so compiladas em

assemblies. Um assembly um arquivo que normalmente tem a extenso de nome de ar


quivo dll, embora programas executveis com a extenso de nome de arquivo .exe tambm
sejam assemblies.
Um assembly pode conter muitas classes. As classes de biblioteca que o .NET Framework
Class Library abrange, como System .Console, so fornecidas nos assemblies instalados no
seu computador com o Visual Studio. Voc descobrir que a biblioteca de classes do .NET
Framework contm milhares de classes. Se todas fossem armazenadas nos mesmos assem
blies, estes seriam enormes e difceis de manter. (Se a Microsoft atualizasse um nico m
todo em uma nica classe, ela teria de distribuir toda a biblioteca de classes a todos os
desenvolvedores!)
Por essa razo, o .NET Framework Class Library dividido em alguns assemblies, agrupados
de acordo com a rea funcional a que as classes esto relacionadas. Por exemplo, h um
assembly "bsico" que contm todas as classes comuns, como System .Console, e h ou
tros assemblies que contm classes para manipular bancos de dados, acessar Web services,
compilar interfaces grficas com o usurio e assim por diante. Se quiser utilizar uma classe
em um assembly, voc dever adicionar ao seu projeto uma referncia a este. Ento, pode
adicionar instrues using ao seu cdigo, colocando em escopo os itens do namespace nes
se assembly.
Observe que no h necessariamente uma equivalncia 1:1 entre um assembly e um na
mespace; um nico assembly pode conter classes para mltiplos namespaces; e um nico
namespace pode abranger mltiplos assemblies. Isso parece muito confuso agora, mas voc
logo ir se acostumar.

Captulo 1 Bem-vindo ao C#

49

Ao utilizar o Visual Studio para criar um aplicativo, o template que voc seleciona inclui
automaticamente referncias aos assemblies adequados. Por exemplo, no Solution Explorer
do projeto TextHello, expanda a pasta References. Voc ver que um aplicativo Console auto
maticamente inclui referncias aos assemblies chamados M icrosoft.CSharp, System, System.

Core, System.Data, System. Data. DataExtensions, System .Xm l e System.Xm l.Linq. Voc pode
adicionar referncias para assemblies adicionais a um projeto clicando com o boto direito
do mouse na pasta References e em A d d Reference - voc executar essa tarefa nos prxi
mos exerccios.

Criando um aplicativo grfico


At aqui, voc usou o Visual Studio 2010 para criar e executar um aplicativo Console bsico. 0
ambiente de programao do Visual Studio 2010 tambm contm tudo que voc precisa para criar
aplicativos grficos baseados no Windows. Voc pode projetar a interface de usurio baseada em
formulrios de um aplicativo baseado em Windows de modo interativo. O Visual Studio 2010 ento
gera as instrues do programa para implementar a interface com o usurio que voc projetou.
0 Visual Studio 2010 fornece duas visualizaes de um aplicativo grfico: a visualizao de projeto
{design view) e a visualizao de cdigo (code view). Utilize a janela Code and Text Editor para modi
ficar e manter o cdigo e a lgica para um aplicativo grfico, e a janela Design View para organizar
sua interface com o usurio. Voc pode alternar entre as duas visualizaes sempre que quiser.
Nos exerccios a seguir, voc aprender a criar um aplicativo grfico utilizando o Visual Studio 2010.
Esse programa exibir um formulrio simples contendo uma caixa de texto em que voc pode inserir seu
nome e um boto que, quando clicado, exibe uma saudao personalizada em uma caixa de mensagem.

Se voc estiver utilizando o Visual Studio 2010 Standard ou o Visual Studio 2010 Professional,
siga estes passos para criar um novo aplicativo grfico:
1. No menu File, aponte para New e ento clique em Project. A caixa de dilogo New Project abre.

50

Parte I Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

2. No painel esquerda, em Installed Templates, clique em Visual C#.


3. No painel central, clique no cone WPFApplication.
4. Certifique-se de que o campo Location refere-se pasta \Microsoft PressWisual CSharp Step
By Step\Chapter 1, na pasta Documentos.

5. No campo Name, digite WPFHello.


6. No campo Solution, assegure-se de que Create new solution est selecionado.
Essa ao cria uma nova soluo para armazenar o projeto. A alternativa Add to Solution
adiciona o projeto soluo TextHello.
7. Clique em OK.
Se voc estiver utilizando o Visual C# 2010 Express, siga estes passos para criar um novo apli
cativo grfico.
1. No menu File, clique em New Project.
2. Se a caixa de mensagem New Project aparecer, clique em Save para salvar suas alteraes
no projeto TextHello. Na caixa de dilogo Save Project, verifique se o campo Location est
configurado como Microsoft PressWisual CSharp STep By Step\Chapter 1 em sua pasta Do
cumentos e ento clique em Save.
3. Na caixa de dilogo New Project, clique no cone WPF Application.
4. No campo Name, digite WPFHello.
5. Clique em OK.
O Visual Studio 2010 fecha seu aplicativo atual e cria o novo aplicativo WPF. Um formulrio
WPF vazio exibido na janela Design View, com outra contendo uma descrio XAML do formu
lrio, como mostrado na ilustrao a seguir:

Captulo 1 Bem-vindo ao C#

Dica

51

Feche as janelas Output e Error List para dar mais espao exibio da janela Design

View.

XAML significa Extensible Application Markup Language e uma linguagem tipo XM L utilizada
por aplicativos WPF para definir o layout de um formulrio e seu contedo. Se voc conhece XML,
a XAML dever lhe parecer familiar. Na realidade, voc pode definir completamente um formulrio
WPF escrevendo uma descrio XAML, se no quiser utilizar a janela de visualizao Design View
do Visual Studio ou se no tiver acesso ao Visual Studio; a Microsoft fornece um editor de XAML
chamado XAMLPad que instalado com o Windows Software Development Kit (SDK).
No prximo exerccio, voc vai utilizar a janela Design View para adicionar trs controles ao formu
lrio Windows e examinar alguns dos cdigos C# gerados automaticamente pelo Visual Studio 2010
para implementar esses controles.

Crie a interface do usurio


1. Clique na guia Toolbox que exibida esquerda do formulrio na janela Design View.
A Toolbox aparece, ocultando parcialmente o formulrio, e exibe os vrios componentes e con
troles que voc pode colocar em um formulrio Windows. Expanda a seo Common WPF Con
trole. Esta seo exibe uma lista de controles utilizados pela maioria dos aplicativos WPF. A
seo Ali Controls exibe uma lista mais extensa de controles.
2. Na seo Common WPF Controls, clique em Labei e arraste o controle do rtulo para a parte
visvel do formulrio.
Um controle labei (rtulo) adicionado ao formulrio (voc o mover para a localizao correta
mais adiante), e a Toolbox ocultada.

Dica

<8>

Se voc quiser que a Toolbox permanea visvel, mas no oculte qualquer parte do for

mulrio, clique no boto Auto Hide direita da barra de ttulo da Toolbox. (Ele se parece com
um pino.) A Toolbox aparece permanentemente no lado esquerdo da janela do Visual Studio
2010, e a janela Design View reduzida para acomod-la. (Talvez voc perca muito espao se
tiver uma tela com baixa resoluo.) Clicar no boto Auto Hide mais uma vez far a Toolbox
desaparecer novamente.

3. O controle labei no formulrio provavelmente no est exatamente onde voc quer. Voc pode
clicar e arrastar os controles que adicionou a um formulrio para reposicion-los. Utilizando
essa tcnica, mova o controle label para posicion-lo prximo ao canto superior esquerdo do
formulrio. (O local exato no importante para esse aplicativo.)

52

Parte I

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

4. No menu View, clique em Properties Window.


Se j estava aberta, a janela Properties aparece no canto inferior direito da tela, sob Solution
Explorer. Para especificar as propriedades dos controles, use o painel XAML, abaixo da janela
Design View. Entretanto, a janela Properties uma maneira mais prtica de modificar as pro
priedades dos itens em um formulrio assim como de outros itens em um projeto. Ela diferencia
letras maisculas de minsculas e exibe as propriedades do item selecionado. Se clicar na barra
de ttulo do formulrio exibido na janela Design View, voc ver que a janela Properties exibe as
propriedades do prprio formulrio. Se voc clicar no controle labei, a janela exibir as proprie
dades do rtulo. Se clicar em outro local no formulrio, a janela Properties exibir as proprieda
des de um item misterioso chamado grade (grid). Uma grade quase como um continer para
itens em um formulrio WPF, e voc pode utiliz-la, entre outras coisas, para indicar como os
itens no formulrio devem ser alinhados e agrupados.
5. Clique no controle labei no formulrio. Na janela Properties, localize a propriedade FontSize.
Mude a propriedade FontSize para 20 e ento, na janela Design View, clique na barra de ttulo
do formulrio.
O tamanho do texto no rtulo muda.
6. No painel XAML, abaixo da janela Design View,examine o texto que define o controle labei. Se
voc fizer uma rolagem at o final da linha, dever ver o texto FontSize = 20. Todas as al
teraes efetuadas na janela Properties constaro automaticamente nas definies do XAML e
vice-versa.
Sobrescreva o valor da propriedade FontSize no painel XAML, e altere-a novamente para 12. O
tamanho do texto no rtulo da janela Design View voltar ao anterior.
7. No painel XAML, examine as outras propriedades do controle labei.
As propriedades listadas no painel XAML so as nicas que no tm valores padro. Se voc
modificar quaisquer valores de propriedade na janela Properties, eles aparecero como parte da
definio do rtulo no painel.
8. Altere o valor da propriedade Content, de Labei para Please enter your name.
Observe que o texto exibido no rtulo sobre o formulrio muda, embora o rtulo continue muito
pequeno para mostr-lo corretamente.

Captulo 1 Bem-vindo ao C#

53

9. Na janela Design View, clique no controle labei. Posicione o mouse sobre a borda direita desse
controle. O perfil do mouse deve mudar para uma seta de duas pontas, indicando que voc pode
utilizar o mouse para redimensionar o controle. Clique o mouse e arraste a borda direita do
controle labei mais para a direita, at voc conseguir ver o texto completo do rtulo.
10. Clique no formulrio na janela Design View e exiba a Toolbox novamente.
11. Na Toolbox, clique e arraste o controle TextBox para o formulrio. Mova o controle da caixa de texto
para posicion-lo imediatamente abaixo do controle labei.

Dica

Ao arrastar um controle em um formulrio, indicadores de alinhamento aparecem au-

tomaticamente quando o controle torna-se alinhado vertical ou horizontalmente a outros con


troles. uma dica visual rpida para voc se certificar de que esses controles esto alinhados
corretamente.

12. Com o controle de caixa de texto ainda selecionado, na janela Properties, altere o valor da pro
priedade Name exibida na parte superior da janela para userName.

13. Exiba a Toolbox novamente, depois clique e arraste um controle Button para o formulrio. Posi
cione o controle button direita da caixa do controle caixa de texto no formulrio, de modo que
a parte inferior do boto fique alinhada horizontalmente com a parte inferior da caixa de texto.
14. Na janela Properties, altere a propriedade Name do controle button para ok. E altere a proprie
dade Content do Button para OK. Verifique se a legenda do controle button no formulrio muda.
15. Clique na barra de ttulo do formulrio MainWindow.xaml na janela Design View. Na janela
Properties, mude a propriedade Title para Hello.
16. Na janela Design View, observe que uma ala de redimensionamento (um pequeno quadrado)
aparece no canto inferior direito do formulrio quando selecionada. Mova o ponteiro do mouse
sobre a ala de redimensionamento. Quando o ponteiro virar uma seta de duas pontas diagonal,
clique e arraste-o para redimensionar o formulrio. Pare de arrastar e solte o boto do mouse
quando o espaamento em torno dos controles estiver igual.

I /

Importante

Clique na barra de ttulo do formulrio e no no contorno da grade dentro

do formulrio antes de redimension-lo. Se selecionar a grade, voc modificar o layout dos


controles no formulrio, mas no o tamanho do formulrio.

54

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

O formulrio deve ficar parecido com a figura a seguir:

y ? WPFHello -Microsoft Visual Studio


Eile

dit

iew

Project

j - j - j j i

I MainWindow.xaml

guild

* -j

ebug

Djta

->- f

Xooli

Tejt

Window
m

Help

oam p
3

ixAML

....

Solution WPFHello' (1 project)


WPFHello
i J i Properties
|> '
H
References
!> 0 App.xaml
ti
MainvVindow.xaml

raB

<Window x:Cla5S-|IPFHello.Mainlllindow,
=]
I " <Windo
xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentation"
*
xmlns:x-http://schemas.microsoft.ccm/winfx/2006/xaml"
Title-Hello Height-"154 Width-"295">
<Qrid>
<Label ContentPlease enter your name Height"28 HorizontalAlignm
<TextBox Height=23" HorizontalAlignment="Left" Margin=21,57,0,0 Ni

SnapsToDevicePixels
Style
Tablndex
Tag
Taskbaritemlnfo
Template

Window Window I1

17. No menu Build, clique em Build Solution e verifique se a compilao do projeto foi bem-sucedida.
18. No menu Debug, clique em Start Without Debugging para compilar e executar o aplicativo.
0 aplicativo deve ser executado, exibindo seu formulrio. Voc pode digitar seu nome na caixa
de texto e clicar em OK, mas nada acontece ainda. necessrio adicionar algum cdigo para
processar o evento Click para o boto OK, o que faremos em seguida.
19. Clique no boto Close {o X no canto superior direito do formulrio) para fechar o formulrio e
retornar ao Visual Studio.
Voc conseguiu criar um aplicativo grfico sem escrever uma nica linha de cdigo em C#. Esse apli
cativo ainda no faz muito (ser necessrio escrever algum cdigo), mas o Visual Studio gera uma
grande quantidade de cdigo que trata das tarefas de rotina que todos os aplicativos grficos devem
realizar, como abrir e exibir um formulrio. Antes de adicionar seu prprio cdigo ao aplicativo,
importante entender o que Visual Studio gerou.
No Solution Explorer, expanda o n de MainWindow.xaml. 0 arquivo MainWindow.xaml.es aparece.
Clique duas vezes no arquivo MainWindow.xaml.es, e o cdigo do formulrio exibido na janela
Code and Text Editor. Ele se parece com este:
using
using
using
using
using
using
using

System;
System.Col 1ections.Generic;
System.Linq;
System.Text;
System.Windows;
System.Windows.Controls;
System.Windows.Data;

Captulo 1 Bem-vindo ao C#

using
using
using
using
using
using

55

System.Windows.Documents;
System.Windows.Input;
System.Windows.Medi a;
System.Windows.Medi a .Imagi n g ;
System.Wi ndows.Navi gati o n ;
System.Windows.Shapes;

namespace WPFHello

{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partia! class MainWindow : Window

{
public MainWindowQ

{
InitializeComponentO;

}
}
}
Alm de muitas instrues using colocar em escopo alguns namespaces que a maioria dos aplica
tivos WPF utiliza, o arquivo contm apenas a definio de uma classe chamada MainWindow. H
um pouco de cdigo para a classe Main Window conhecido como construtor que chama um mtodo
denominado InitializeComponent, mas isso tudo. (Um construtor um mtodo especial com o
mesmo nome da classe. Ele executado quando criada uma instncia da classe que pode conter
um cdigo para inicializar a instncia. Discutiremos construtores no Captulo 7.) Na realidade, o
aplicativo contm muito mais cdigo, mas a maior parte dele gerada automaticamente com base na
descrio XAML do formulrio, e ocultada. Esse cdigo oculto realiza operaes como criar e exibir
o formulrio e tambm criar e posicionar os vrios controles no formulrio.
O objetivo desse cdigo, que voc pode ver nessa classe, adicionar seus prprios mtodos para tra
tar a lgica do seu aplicativo, como determinar o que acontece quando o usurio clica no boto OK.

Dica

Voc tambm pode exibir o arquivo do cdigo C# para um formulrio WPF clicando com o

boto direito do mouse em qualquer lugar na janela Design View e depois em View Code.

Voc deve estar querendo saber onde est o mtodo Main e como o formulrio ser exibido quando
o aplicativo for executado; lembre-se de que o Main define o ponto em que o programa inicia. Na So
lution Explorer, deve aparecer um outro arquivo-fonte chamado App.xaml. Se voc clicar duas vezes
nesse arquivo, ser exibida a descrio XAML desse item.

56

Parte I Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

H uma propriedade StartupUri no cdigo XAML que se refere ao arquivo MainWindow.xaml como
mostrado em negrito no exemplo de cdigo a seguir:
<Appli cati on x:Class="WPFHel1o.App"
xmlns="http://schemas.mi crosoft.com/wi nfx/2006/xaml/presentati on"
xmlns:x=http://schemas.mi crosoft.com.wi nfx/2006/xaml
StartupUri="MainWindow.xaml ">
<Appli cati on. Resou rces>
</Appli cati on.Resources
</Application>
Se voc clicar na guia Design, na parte inferior do painel XAML, a janela Design View para o App.
xaml ser exibida e apresentar o texto Intentionally left blank. The document root element is not
supported by the visual designer . Isso ocorre porque no possvel utilizar a janela Design View
para modificar o arquivo App.xaml. Clique na guia XAML para retornar ao painel XAML. Se voc
expandir o n App.xaml no Solution Explorer, ver que h tambm um arquivo Application.xaml.cs.
Ao clicar duas vezes nesse arquivo, voc descobrir que ele contm este cdigo:
using
usi ng
using
using
using
using

System;
System.Col1ecti ons.Generi c ;
System.Configuration;
System.Data;
System.Linq;
System.Windows;

namespace WPFHello
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}
Mais uma vez, h algumas instrues using, mas no muito alm disso, nem mesmo um mtodo
Main. De fato, Main est a, mas tambm est oculto. O cdigo para Main gerado com base nas
configuraes no arquivo App.xaml file; em particular, Main criar e exibir o formulrio especifica
do pela propriedade StartupUri. Se quiser exibir um formulrio diferente, edite o arquivo App.xaml.
o momento de voc mesmo escrever algum cdigo!

Escreva o cdigo para o boto OK


1. Clique na guia Main Window.xaml acima da janela Code and Text Editor para exibir MainWindow
na janela Design View.
2. Clique duas vezes no boto OK no formulrio.
O arquivo MainWindow.xaml.es aparece na janela Code and Text Editor, mas um novo mtodo
chamado ok_Click foi adicionado. O Visual Studio gera automaticamente o cdigo para chamar
esse mtodo sempre que o usurio clica no boto OK. Esse um exemplo de evento, e voc
aprender muito mais sobre como os eventos funcionam medida que avanar no livro.
3. Adicione o seguinte cdigo mostrado em negrito ao mtodo ok_Click:

Captulo 1 Bem-vindo ao C#

57

void ok_Click(object sender, RoutedEventArgs e)

{
MessageBox.Show("HeTlo " + userName.Text);

}
Esse o cdigo que ser executado quando o usurio clicar no boto OK. No se preocupe com
a sintaxe desse cdigo ainda (simplesmente certifique-se de copi-lo exatamente como mostra
do), pois voc aprender tudo sobre mtodos no Captulo 3. A parte interessante a instruo
MessageBox.Show. Essa instruo exibe uma caixa de mensagem contendo o texto Hello com
qualquer que seja o nome digitado pelo usurio na caixa de texto de nome de usurio no for
mulrio anexado.
4. Clique na guiaMain Wind.ow.xaml acima da janela Code and Text Editor para exibir MainWindow
na janela Design View novamente.
5. No painel inferior que exibe a descrio XAML do formulrio, examine o elemento Button, mas
tenha cuidado para no alterar nada. Observe que ele contm um elemento chamado Click que
se refere ao mtodo ok_Click
.
<Button Height="23" ... Click="ok_CHck"/>

6. No menu Debug, clique em Start Without Debugging para compilar e executar o aplicativo.

7. Quando o formulrio aparecer, digite seu nome na caixa de texto e ento clique em OK. Uma
caixa de mensagem de boas-vindas com seu nome aparece.

3Hello

'=>

II

Ifssl

Please enter your name


John

<*

--------------

Hello John

11

8. Clique em OK na caixa de mensagem, e ela ser fechada.


9. Feche o formulrio.
Neste captulo, voc viu como possvel utilizar o Visual Studio 2010 para criar, construir e executar
aplicativos. Voc criou um aplicativo de console que exibe sua sada em uma janela de console e um
aplicativo WPF com uma simples interface grfica do usurio.
Se voc quiser seguir o prximo captulo agora:
Mantenha o Visual Studio 2010 em execuo e v para o Captulo 2.
Se quiser sair do Visual Studio 2010:
No menu File, clique em Exit. Se vir uma caixa de dilogo Save, clique em Yes e salve o projeto.

58

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Referncia rpida do Captulo 1


Para

Faa isto

Criar um novo aplicativo de console utilizando o


Visual Studio 2010 Standard ou Professional

No menu File, aponte para New e, em seguida, clique em Project


para abrir a caixa de dilogo New Project. No painel esquerda,
em Installed Templates, clique em Visual C#. No painel central
clique em Console Application. Especifique um diretrio para os
arquivos do projeto na caixa Location. Digite um nome para o pro
jeto. Clique em OK.

Criar um novo aplicativo de console utilizando o


Visual C# 2010 Express

No menu File, clique em New Project para abrir a caixa de dilogo


New Project. Para o modelo, selecione Console Application. Esco
lha um nome para o projeto. Clique em OK.

Criar um novo aplicativo grfico utilizando o


Visual Studio 2010 Express ou Professional

No menu File, aponte para New e clique em Project para abrir a


caixa de dilogo New Project. No painel esquerda, em Installed
Templates, clique em Visual C#. No painel central, Clique em WPF
Application. Especifique um diretrio para os arquivos de projeto.
Na caixa Location, digite um nome para o projeto. Clique em OK.

Criar um novo aplicativo grfico utilizando o


Visual C# 2010 Express

No menu File, clique em New Project para abrir a caixa de dilogo


New Project. Para o modelo, selecione WPF Application. Escolha
um nome para o projeto. Clique em OK.

Compilar o aplicativo

No menu Build, clique em Build Solution.

Executar o aplicativo

No menu Debug, clique em Start Without Debugging.

Captulo 2

Trabalhando com variveis,


operadores e expresses
Neste captulo, voc vai aprender a:
Entender instrues, identificadores e palavras-chave.
Utilizar variveis para armazenar informaes.
Trabalhar com tipos de dados primitivos.
Utilizar operadores aritmticos, com o o sinal de adio (+) e o sinal de subtrao (-).
Incrementar e decrem entar variveis.

No Captulo 1, Bem-vindo ao C# , voc aprendeu a utilizar o ambiente de programao do Microsoft


Visual Studio 2010 para compilar e executar um programa Console e um aplicativo Windows Presen
tation Foundation (WPF). Este captulo apresenta os elementos de sintaxe e semntica do Microsoft
Visual C#, incluindo instrues, palavras-chave e identificadores. Voc estudar os tipos primitivos
que so compilados na linguagem C# e as caractersticas dos valores que cada tipo armazena. Tam
bm ver como declarar e utilizar as variveis locais (que s existem dentro de uma funo ou outra
pequena seo do cdigo), entender os operadores aritmticos que o C# fornece, descobrir como
utilizar operadores para manipular valores e aprender a controlar expresses que contm dois ou
mais operadores.

Entendendo instrues
Uma instruo um comando que executa uma ao. Voc combina instrues para criar mtodos
(para aprender mais sobre mtodos, ver Captulo 3, Escrevendo mtodos e aplicando o escopo).
Imagine um mtodo como uma sequncia nomeada de instrues. Main, que foi apresentado no
captulo anterior, um exemplo de mtodo. Instrues em C# seguem um conjunto bem definido
de regras que descrevem seu formato e sua construo. Estas so conhecidas coletivamente como
sintaxe. (Por outro lado, a especificao do que as instruesfazem conhecida coletivamente como
semntica.) Uma das regras de sintaxe mais simples e mais importantes do C# diz que voc deve
terminar todas as instrues com um ponto e vrgula. Por exemplo, sem seu ponto e vrgula de termi
nao, a instruo a seguir no ser compilada:
Consol e.WriteLine("Hello World");

60

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Dica

0 C# uma linguagem de "formato livre", assim, espaos em branco, como um caractere

de espao ou uma nova linha, no tm outro significado a no ser o de serem separadores. Ou seja,
voc pode dispor as instrues como quiser. Mas deve adotar um estilo consistente e simples de
layout e ater-se a ele para tornar seus programas mais fceis de ler e entender.

O truque para programar bem em qualquer linguagem aprender sua sintaxe e semntica e ento
utiliz-la de maneira natural e idiomtica. Essa abordagem facilita a manuteno dos seus progra
mas. Nos captulos deste livro, voc ver exemplos das instrues mais importantes do C#.

Utilizando identificadores
Identificadores so os nomes utilizados para identificar os elementos nos seus programas, como na
mespaces, classes, mtodos e variveis. (Discutiremos variveis em breve.) No C#, voc deve seguir
as regras de sintaxe abaixo ao escolher os identificadores:
Voc pode utilizar apenas letras (maisculas ou minsculas), dgitos e o caractere de sublinhado.
Um identificador deve iniciar com uma letra (ou um sublinhado).
Por exemplo, resultado, _placar, timeDeFutebol e plano so identificadores vlidos, enquanto resultado%, timeDeFutebol$ e 9plano no so.

Importante

O C# uma linguagem que diferencia maisculas de minsculas: timeDeFutebol e

TimeDeFutebol so identificadores diferentes.

Identificando palavras-chave
A linguagem C# reserva, para uso prprio, 77 identificadores, os quais no podem ser reutilizados
para outros propsitos. Eles so denominados palavras-chave, e cada um tem um significado espe
cfico. Exemplos de palavras-chave so class, namespace e using. Voc aprender o significado da
maioria das palavras-chave do C# ao longo da leitura deste livro. Elas esto listadas na tabela a
seguir.

abstract

do

in

protected

true

as

double

int

public

try

base

else

interface

readonly

typeof

bool

enum

internal

ref

uint

break

event

is

return

ulong

Captulo 2

Trabalhando com variveis, operadores e expresses

byte

explicit

lock

sbyte

unchecked

case

extern

long

sealed

unsafe

catch

false

namespace

short

ushort

char

finally

new

sizeof

using

checked

fixed

null

stackalloc

virtual

class

flo at

object

static

void

const

fo r

operator

string

volatile

continue

foreach

out

struct

while

decimal

goto

override

switch

default

if

params

this

delegate

implicit

private

throw

Dica

61

Na janela Code and Text Editor do Visual Studio 2010, as palavras-chave so pintadas de azul

quando digitadas.

O C# tambm utiliza os identificadores relacionados abaixo. Eles no so especficos ao C#, ou seja,


voc pode utiliz-los como identificadores em seus prprios mtodos, variveis e classes, mas isso
deve ser evitado sempre que possvel.

dynamic

join

set

from

let

value

get

orderby

var

group

partial

where

into

select

yield

Utilizando variveis
Uma varivel uma localizao da memria que armazena um valor, ou seja, uma caixa na mem
ria do computador que contm informaes temporrias. Voc deve atribuir a cada varivel em um
programa um nome no ambguo que a identifica de forma nica no contexto em que utilizada.
Um nome de varivel utilizado para referenciar o valor que ela armazena. Por exemplo, se quiser
armazenar o valor do custo de um item em uma loja, voc deve criar uma varivel chamada custo e
armazenar o custo do item nela. Se voc referenciar a varivel custo, o valor recuperado ser o custo
do item armazenado anteriormente.

62

Parte I Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Nomeando variveis
Adote uma conveno de nomes que torne claras as variveis definidas. A lista a seguir contm al
gumas recomendaes gerais:
No inicie um identificador com um sublinhado.
No crie identificadores cuja nica diferena seja entre maisculas e minsculas. Por exemplo,
no crie uma varivel chamada minhaVariavel e outra chamada MinhaVariavel para serem uti
lizadas ao mesmo tempo, porque ser muito fcil confundi-las.

Comece o nome com uma letra minscula.


Em um identificador com vrias palavras, comece a segunda palavra e as palavras subsequen
tes com uma letra maiscula (isso chamado de notao camelo ou camelCase).
No utilize a notao hngara (programadores em Microsoft Visual C+ + provavelmente j
conhecem a notao hngara. Se voc no souber o que , no se preocupe!).

Importante

Voc deve considerar as duas primeiras recomendaes anteriores obrigatrias por

que esto relacionadas para conformidade com a Common Language Specification (CLS). Se voc
deseja escrever programas que possam interoperar com outras linguagens, como o Microsoft Visual
Basic ,NET, deve obedecer a essas recomendaes.

Por exemplo, placar, timeDeFutebol, _placar e TimeDeFutebol so nomes de variveis vlidos, mas
apenas os dois primeiros so recomendados.

Declarando variveis
As variveis armazenam valores. O C# pode armazenar e processar muitos tipos diferentes de va
lores - inteiros, nmeros de ponto flutuante e sequncias de caractere (strings ), entre outros. Ao
declarar uma varivel, voc deve especificar o tipo de dado que ela armazenar.
Voc declara o tipo e o nome de uma varivel em uma instruo de declarao. Por exemplo, a ins
truo a seguir declara que a varivel chamada age (idade) armazena valores int (inteiros). Como
sempre, a instruo deve ser terminada com um ponto e vrgula.
int age;

Captulo 2 Trabalhando com variveis, operadores e expresses

63

0 tipo de varivel int o nome de um dos tipos primitivos do C# - inteiro, que, como o nome j diz,
um nmero inteiro. (Voc vai aprender sobre os diversos tipos de dados primitivos mais adiante
neste captulo.)

Aps ter declarado sua varivel, voc pode atribuir-lhe um valor. A instruo a seguir atribui o valor
de 42 a age. Observe que o ponto e vrgula requerido novamente.
age = 42;

O sinal de igual (=) o operador de atribuio, que atribui o valor que est sua direita varivel
que est sua esquerda. Depois dessa atribuio, a varivel age pode ser utilizada no seu cdigo
para referenciar o valor armazenado. A instruo a seguir escreve o valor da varivel age, 42, no
console:
Console.WriteLine(age);

Dica

Se voc deixar o ponteiro do mouse sobre uma varivel na janela Visual Studio 2010 Code

and Text Editor, uma dica de tela ser exibida informando o tipo de varivel.

Trabalhando com tipos de dados primitivos


O C# tem vrios tipos predefinidos denominados tipos de dados primitivos. A tabela a seguir lista
os mais utilizados no C# e o intervalo de valores que podem ser armazenados neles.

Tipo de dado

Descrio

Tamanho (em bits)

Intervalo

Exemplo de uso

int

Nmeros inteiros

32

-231 a 23' - 1

in t count;
count = 42;

long

Nmeros inteiros
(intervalo maior)

64

-263 a 263- 1

Tong w ait;
wait = 42L;

float

Nmeros de ponto
flutuante

32

1.5 x 1045a 3.4


x 1038

flo a t away;
away = 0.42F;

double

Nmeros de ponto
flutuante de
preciso dupla
(maior preciso)

64

5.0 x 10-324 a
1.7 x 10308

double trouble;
trouble = 0.42;

decimal

Valores monetrios

128

28 nmeros signifi
cativos

decimal coin;
coin = 0.42M;

64

Parte I Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Tipo de dado

Descrio

Tamanho (em bits)

Intervalo

Exemplo de uso

string

Sequncia de
caracteres

16 bits por caractere

No aplicvel

string
vest; vest =
"fortytwo";

char

Caractere nico

16

0 a 216 1

char grill;
grill = 'x';

bool

Booleano

Verdadeiro ou falso

bool teeth;
teeth = false;

Variveis locais no atribudas


Quando voc declara uma varivel, ela contm um valor aleatrio at que lhe seja atribudo um va
lor. Esse comportamento era uma grande fonte de erros nos programas C e C++ que criavam uma
varivel e a utilizavam acidentalmente como fonte de informaes antes de ela receber um valor. O
C# no permite utilizar uma varivel no atribuda. necessrio atribuir um valor a uma varivel
antes de us-la; caso contrrio, o programa no compilar. Essa exigncia chamada Regra de Atri
buio Definitiva. Por exemplo, as instrues a seguir geram um erro de tempo de compilao porque
a varivel age no foi atribuda:
int age;
Console.WriteLine(age); // erro de tempo de compilao

Exibindo valores de tipos de dados primitivos


No exerccio a seguir, voc vai utilizar um programa em C# chamado PrimitiveDataTypes para de
monstrar como os vrios tipos de dados primitivos funcionam.

Exiba os valores dos tipos de dados primitivos


1. Inicialize o Visual Studio 2010 se ainda no estiver em execuo.
2. Se estiver utilizando o Visual Studio 2010 Standard ou o Visual Studio 2010 Professional, no
menu File, aponte o cursor para Open e ento clique em Project/Solution.
Se voc estiver utilizando o Visual C# 2010 Express, no menu File, clique em Open Project.
A caixa de dilogo Open Project aparece.
3. V para a pasta \Microsoft PressWisual CSharp Step By Step\Chapter 2\PrimitiveDataTypes na
sua pasta Documentos. Selecione o arquivo de soluo PrimitiveDataTypes e clique em Open.
A soluo carregada, e o Solution Explorer exibe o projeto PrimitiveDataTypes.

Captulo 2

Nota

Trabalhando com variveis, operadores e expresses

65

Os nomes dos arquivos de soluo tm o sufixo .sln, como em PrimitiveDataTypes.sIn.

Uma soluo pode conter um ou mais projetos, cujos arquivos tm o sufixo .csproj. Se um
projeto for aberto em vez de uma soluo, o Visual Studio 2010 criar para ele, automatica
mente, um novo arquivo de soluo. Se a soluo for compilada, o Visual Studio 2010 salvar
automaticamente todos os arquivos novos ou atualizados, e voc ser solicitado a fornecer um
nome e um local para o novo arquivo de soluo.

4. No menu Debug, clique em Start Without Debugging.


Talvez sejam exibidos alguns avisos no Visual Studio que certamente podem ser ignorados.
(Voc os corrigir no prximo exerccio.) A seguinte janela de aplicativo ser exibida:

Primitive Data Types


Choose a data type

Sam ple value

int
long
float
double
decim al
string

5. Na lista Choose a data type, clique no tipo strng.


O valor forty two aparece na caixa Sample value.
6. Clique no tipo int na lista.
O valor to do" (a fazer) aparece na caixa Sample value, indicando que as instrues exibindo
um valor int ainda precisam ser escritas.
7. Clique em cada tipo de dado na lista. Confirme que o cdigo para os tipos double e bool ainda
no est implementado.
8. Clique em Quit para fechar a janela e parar o programa. O controle retorna ao ambiente de tra
balho do Visual Studio 2010.

Utilize tipos de dados primitivos no cdigo


1. No Solution Explorer, clique duas vezes em MainWindow.xaml.
O formulrio WPF para o aplicativo aparece na janela Design View.
2. Clique com o boto direito do mouse em qualquer lugar na janela Design View para exibir o
formulrio MainWindow.xaml e ento clique em View Code.
A janela Code and Text Editor abre exibindo o arquivo MainWindow.xaml.es.

66

Parte I Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

3. Na janela Code and Text Editor, localize o mtodo showDoubleValue.

Dica

Para localizar um item no seu projeto, no menu Edit, aponte para Find and Replace e

clique em Quick Find. Uma caixa de dilogo aberta e pergunta o que voc deseja pesquisar.
Digite o nome do item que est procurando e clique em Find Next. Por padro, a pesquisa
no diferencia maisculas de minsculas. Se voc quiser executar uma pesquisa que diferencie
letras maisculas de minsculas, clique no boto de adio, +, ao lado do rtulo Find Options
para exibir as opes adicionais e selecione a caixa de seleo Match Case. Se tiver tempo, voc
pode experimentar as outras opes.
Voc tambm pode pressionar Ctrl + F (pressione a tecla Control e, em seguida, pressione F)
para exibir a caixa de dilogo Quick Find em vez de utilizar o menu Edit. Da mesma forma, voc
pode pressionar Ctrl + H para exibir a caixa de dilogo Quick Replace.
Como alternativa ao uso da funcionalidade Quick Find, voc tambm pode localizar os m
todos em uma classe ao utilizar a caixa de lista suspensa de membros da classe, posicionada
acima da janela Code and Text Editor, direita. Essa lista exibe todos os mtodos na classe e as
variveis e outros itens que a classe contm. (Voc conhecer mais detalhes sobre esses itens
nos captulos posteriores.) Na caixa da lista suspensa, clique em showFloatValueO, e o cursor
saltar imediatamente para o mtodo showFloatValueO na classe.

O mtodo showFloatValue executado quando voc clica no tipoJloat na caixa de listagem. Esse
mtodo contm as trs instrues a seguir:
flo a t variab le;
v a r ia b le = 0 .4 2 F ;
v a l u e . T e x t = "0 .42 F";

A primeira instruo declara uma varivel chamada variable do tipoJloat.


A segunda instruo atribui o valor 0.42F variable ( o f u m tipo de sufixo especificando que
0.42 deve ser tratado como um valoiJloat. Se voc esquecer oF, o valor 0.42 ser tratado como
um double, e seu programa no compilar porque um valor de um tipo no pode ser atribudo
a uma varivel de outro tipo, sem escrever cdigo adicional - C# muito rgido nesse aspecto).
A terceira instruo exibe o valor dessa varivel na caixa de texto value no formulrio. Essa
instruo exige um pouco mais de ateno. A maneira como voc exibe um item em uma caixa
de texto configurando a propriedade Text. Observe que a propriedade de um objeto acessada
utilizando a mesma notao de ponto que vimos para executar um mtodo (lembra-se de Con
sole. WriteLine no Captulo 1?). Os dados adicionados propriedade Text devem ser uma string
(uma string includa entre aspas duplas) e no um nmero. Se voc tentar atribuir um nmero
propriedade Text, seu programa no compilar. Nesse programa, a instruo simplesmente

Captulo 2

Trabalhando com variveis, operadores e expresses

67

exibe o texto 0.42F na caixa de texto. Em um aplicativo do mundo real, voc adicionaria ins
trues que convertem o valor da varivel variable em uma string e ento o adicionaria pro
priedade Text. No entanto, necessrio entender um pouco mais sobre o C# e o Microsoft .NET
Framework antes de fazer isso (o Captulo 11, Entendendo arrays de parmetros, e o Captulo
21, Sobrecarga do operador , abrangem as converses de tipos de dados).
4. Na janela Code and Text Editor, localize o mtodo showIntValue. Ele se parece com este:
private void showIntValueO

{
value.Text = "to do";

}
O mtodo showIntValue chamado quando voc clica no tipo int na caixa de listagem.
5. Digite as duas instrues a seguir no incio do mtodo showIntValue, em uma nova linha depois
da chave de abertura, como mostrado em negrito no cdigo a seguir:
private void showIntValueC

{
int variable;
variable = 42;
value.Text = "to do"

}
6. A instruo original nesse mtodo altera a string to do" para 42".
O mtodo agora deve estar exatamente como este:
private void showIntValueO

{
int variable;
variable = 42;
value.Text = "42";

7. No menu Debug, clique em Start Without Debugging.


O formulrio aparece novamente.
8. Selecione o tipo int na lista Choose a data type. Confirme se o valor 42 est sendo exibido na
caixa de texto Sample value.

68

Parte I Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

9. Clique em Quit para fechar a janela e retornar para o Visual Studio.


10. Na janela Code and Text Editor, localize o mtodo showDoubleValue.
11. Edite o mtodo showDoubleValue exatamente como mostrado em negrito no seguinte cdigo:
private void showDoubleValueO

{
double variable;
variable = 0.42;
value.Text = "0.42";

}
12. Na janela Code and Text Editor, localize o mtodo showBoolValue.
13. Edite o mtodo showBoolValue exatamente como a seguir:
private void showBoolValue()

{
bool variable;
variable = false;
value.Text = "false";

}
14. No menu Debug, clique em Start Without Debugging.
15. Na lista Choose a data type, selecione os tipos int, double e bool. Em cada um dos casos, verifi
que se o valor correto exibido na caixa de texto Sample value.
16. Clique em Quit para parar o programa.

Utilizando operadores aritmticos


O C# suporta as operaes aritmticas que voc aprendeu no colgio: o sinal de mais (+) para adi
o, o sinal de menos (-) para subtrao, o asterisco (*) para multiplicao e a barra (/) para diviso.
Esses smbolos + , -, * e / so denominados operadores porque operam" em valores para criar novos
valores. No exemplo abaixo, a varivel moneyPaidToConsultant termina armazenando o produto de
750 (a taxa diria) e de 20 (o nmero de dias que o consultor trabalhou):
long moneyPaidToConsultant;
moneyPaidToConsultant = 750 * 20;

Captulo 2 Trabalhando com variveis, operadores e expresses

69

O p e ra d o re s e tip o s
Nem todos os operadores so aplicveis a todos os tipos de dados. Aqueles que podem ser utilizados
em um valor dependem do tipo do valor. Por exemplo, voc pode utilizar todos os operadores aritm
ticos em valores do tipo char, int, longjloat, double ou decimal, entretanto, com exceo do operador
de adio, +, os operadores aritmticos em valores do tipo string ou bool no podem ser utilizados.
Portanto, a instruo a seguir no permitida porque o tipo string no suporta o operador de subtra
o (por isso, no h sentido em subtrair uma string de outra):
// erro de tempo de compilao
Console.WriteLine("Cillingham" - "Forest Green Rovers");

Voc pode utilizar o operador + para concatenar valores de string, mas aja com cautela para no ob
ter resultados inesperados. Por exemplo, a seguinte instruo escreve 431" (no 44) no console:
Console.WriteLine("43" + "1");

Dica

O .NET Framework fornece um mtodo chamado !nt32.Parse que pode ser utilizado para

converter um valor de string em um inteiro se voc precisar realizar clculos aritmticos em valores
armazenados em strings.

Voc deve estar ciente de que o tipo de resultado de uma operao aritmtica depende do tipo de
operandos utilizados. Por exemplo, o valor da expresso 5.0 / 2.0 2.5; o tipo dos dois operandos
double, de modo que o tipo do resultado tambm double. (No C#, os nmeros literais com pontos
decimais so sempre double, noJloat, para manter o mximo de preciso possvel.) Mas o valor da
expresso 5/2 2. Nesse caso, o tipo de ambos os operandos int, assim, o tipo do resultado tambm
int. O C# sempre arredonda os valores para baixo em casos assim. A situao se torna um pouco
mais complicada se voc misturar os tipos de operandos. Por exemplo, a expresso 5 / 2.0 consis
te em um int e um double. O compilador do C# detecta a incompatibilidade e gera um cdigo que
converte o int em um double antes de executar a operao. O resultado da operao , portanto, um
double (2.5). Embora funcione, essa prtica considerada ruim.
O C# tambm suporta um operador aritmtico menos familiar: o operador resto ou mdulo, que
representado pelo sinal de porcentagem (%). O resultado tx % y o resto da diviso de x pory . Por
exemplo, 9 % 2 1, porque 9 dividido por 2 4, resto 1.

70

Parte I Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

E x a m in a n d o o p e r a d o r e s a r itm tic o s
O exerccio a seguir demonstra como utilizar os operadores aritmticos em valores int.

Trabalhe com operadores aritmticos


1. Abra o projeto MathsOperators, localizado na pasta \Microsoft PressWisual CSharp Step By Step\
Chapter 2\MathsOperators na sua pasta Documentos.
2. No menu Debug, clique em Start Without Debugging.
Um formulrio aparece na tela.
3. Digite 54 na caixa de texto do operando esquerdo.
4. Digite 13 na caixa de texto do operando direito.
Agora voc pode aplicar qualquer um dos operadores aos valores das caixas de texto.
5. Clique no boto - Subtraction e, em seguida, clique em Calculate.

Captulo 2

Trabalhando com variveis, operadores e expresses

71

0 texto na caixa Expression muda para 54 - 13, e o resultado 41 aparece na caixa Result, como
mostrado na figura a seguir:

" 'a

[ o i l s l|w3wl

Maths Operators

right operand

left operand

54

) + Addition
#

- Subtraction

* Multiolication

13

/ Division

Calculate

Expression

Result

% Rem ainder

j
5 4 -1 3
41

Quit

6. Clique no boto / Division e, em seguida, clique em Calculate.


O texto na caixa Expression muda para 54/13, e o nmero 4 aparece na caixa Result. Em uma
situao real, 54/13 uma dzima peridica; no entanto, aqui o C# est realizando uma diviso
de inteiro, e quando um inteiro divido por outro inteiro, a resposta que voc obtm um intei
ro, como explicado anteriormente.
7. Clique no boto %Remainder e ento em Calculate.
O texto na caixa de texto Expression muda para 54 % 13, e o nmero 2 aparece na caixa Result.
Isso acontece porque o resto, aps a diviso de 54 por 13, 2. (54 - ((54 / 13) * 13)) 2 se voc
arredondar para baixo em cada etapa - meus antigos professores de matemtica do colgio de
vem estar horrorizados por eu estar dizendo que (54 /13) * 13 no igual a 54!
8. Teste as outras combinaes de nmeros e operadores. Quando terminar, clique em Quit para
retomar ao ambiente de programao do Visual Studio 2010.
No prximo exerccio, voc examinar o cdigo do programa MathsOperators.

Examine o cdigo do programa MathsOperators


1. Exiba o formulrio MainWindow.xaml na janela Design View. (Clique duas vezes no arquivo
MainWindow. xaml na Solution Explorer.)
2. No menu View, aponte para Other Windows e clique em Document Outline.
A janela Document Outline exibida, mostrando os nomes e tipos de controles do formulrio.
A janela Document Outline uma maneira simples de localizar e selecionar controles em um
formulrio WPF complexo. Os controles so organizados hierarquicamente, comeando pela
Window que constitui o formulrio WPF. Como mencionado no captulo anterior, um formul-

72

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

rio WPF realmente contm um controle Grid, e os outros controles so colocados nesse Grid.
Quando voc clica em cada controle no formulrio, o nome do controle destacado na janela
Document Outline. De maneira semelhante, se selecionar um controle na janela Document Outli
ne, o controle correspondente selecionado na janela Design View. Se voc posicionar o mouse
sobre um controle na janela Document Outline, ser exibida uma imagem do controle (e todos
os controles filhos nele contidos).
3. No formulrio, clique nos dois controles TextBox em que o usurio digita os nmeros. Na janela
Document Outline, verifique se eles esto nomeados como IhsOperand e rhsOperand ( possvel
ver o nome de um controle nos parnteses direita do controle).
Quando o formulrio executado, a propriedade Text de cada um desses controles armazena os
valores que o usurio digita.
4. Na parte inferior do formulrio, verifique se o controle TextBox utilizado para exibir a expresso
que est sendo avaliada tem o nome expression e se o controle TextBox utilizado para exibir o
resultado do clculo tem o nome result.
5. Feche a janela Document Outline.
6. Exiba o cdigo de MainWindow.xaml.es na janela Code and Text Editor.
7. Na janela Code and Text Editor, localize o mtodo subtractValues. Ele se parece a este:
private void subtractValuesO

{
int Ihs = int.Parse(lhsOperand.Text);
int rhs = int.Parse(rhsOperand.Text);
int outeome;
outeome = Ihs - rhs;
expression.Text = IhsOperand.Text + - " + rhsOperand.Text;
result.Text = outeome.ToString();

}
A primeira instruo nesse mtodo declara uma varivel int chamada Ihs e a inicializa com o
inteiro que corresponde ao valor digitado pelo usurio na caixa de texto IhsOperand. Lembre-se
de que a propriedade Text de um controle de caixa de texto contm uma string, que precisa ser
convertida em um inteiro antes de ser atribuda a uma varivel int. O tipo de dados int fornece
o mtodo int.Parse, que faz precisamente isso.
A segunda instruo declara uma varivel int chamada rhs e a inicializa como o valor na caixa
de texto rhsOperand depois de convert-lo em um int.
A terceira instruo declara uma varivel int chamada outeome.
A quarta instruo subtrai o valor da varivel rhs do valor da varivel Ihs, e o resultado atri
budo a outeome.
A quinta instruo concatena trs strings que indicam o clculo sendo realizado (utilizando
o operador de adio, +) e atribui o resultado propriedade expression.Text. Isso faz a string
aparecer na caixa de texto expression no formulrio.

Captulo 2

Trabalhando com variveis, operadores e expresses

73

A sexta instruo exibe o resultado do clculo atribuindo-o propriedade Text da caixa de texto
result. Lembre-se de que a propriedade Text uma string e de que o resultado do clculo um
int, portanto, voc precisa converter o int em uma string antes de atribu-la propriedade Text.
isso que o mtodo ToString do tipo int faz.

O m todo ToString
Cada classe no .NET Framework tem um mtodo ToString. A finalidade de ToString conver
ter um objeto na sua representao de string. No exemplo anterior, o mtodo ToString de
um objeto inteiro, outcome, utilizado para converter o valor inteiro de outcom e no valor
string equivalente. Essa converso necessria porque o valor exibido na propriedade Text
da caixa de texto result - a propriedade Text s pode conter strings. Ao criar suas prprias
classes, voc pode definir uma implementao prpria do mtodo ToString para especificar
a maneira como sua classe deve ser representada como uma string. (Veja como criar suas
prprias classes no Captulo 7, "Criando e gerenciando classes e objetos".)

C o n tro la n d o a p re c e d n c ia
A precedncia (ou prioridade) controla a ordem em que os operadores da expresso so avaliados.
Considere a expresso a seguir, que utiliza os operadores + e *:
2 + 3 *4
Essa expresso potencialmente ambgua; qual deve ser realizada primeiro, a adio ou a multipli
cao? A ordem das operaes importa porque muda o resultado:
Se realizar primeiro a adio e depois a multiplicao, o resultado da adio (2 + 3) forma o
operando esquerdo do operador *, e o resultado de toda a expresso ser 5 * 4 = 20.
Se realizar primeiro a multiplicao e depois a adio, o resultado da multiplicao (3 * 4) forma
o operando direito do operador + , e o resultado da expresso inteira 2 + 12 = 14.
No C#, os operadores multiplicativos (*, / e %) tm precedncia sobre os operadores aditivos (+ e -),
portanto, em expresses como 2 + 3 * 4, a multiplicao realizada primeiro, seguida pela adio.
A resposta para 2 + 3 * 4 , portanto, 14.
Os parnteses podem ser utilizados para sobrescrever a precedncia e forar os operandos a vincu
lar-se aos operadores de maneira diferente. Por exemplo, na expresso a seguir, os parnteses foram
o 2 e o 3 a se vincular ao operador + (produzindo o valor 5), e o resultado dessa soma o operando
esquerdo do operador * para produzir o valor 20:
(2 + 3) * 4

74

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

U tiliz a n d o a a s s o c ia tiv id a d e p a ra a v a lia r e x p re s s e s


A precedncia de operadores apenas uma questo a ser considerada. O que acontece quando uma
expresso contm operadores diferentes que tm a mesma precedncia? Aqui, a associatividade se
torna importante j que associatividade a direo (esquerda ou direita) em que os operandos de um
operador so avaliados. Considere a expresso a seguir que utiliza os operadores / e *:
4/2*6
Essa expresso potencialmente ambgua. Qual deve ser realizada primeiro, a diviso ou a multi
plicao? A precedncia dos dois operadores a mesma (so ambos multiplicativos), mas a ordem
na qual a expresso calculada importante porque dois resultados diferentes podem ser obtidos:
Se realizar primeiro a diviso, o resultado da diviso (4/2) formar o operando esquerdo do *
operador, e o resultado da expresso inteira ser (4/2) * 6 ou 12.
Se realizar primeiro a multiplicao, o resultado da multiplicao (2 * 6) formar o operando
direito do operador/, e o resultado da expresso inteira ser 4 /(2 * 6) ou 4/12.
Nesse caso, a associatividade dos operadores determina como a expresso avaliada. Ambos os
operadores, * e /, associam-se esquerda, assim, os operandos so calculados da esquerda para a
direita. Nesse caso, 4/2 ser avaliado antes da multiplicao por 6, que resulta em 12.

A a s s o c ia tiv id a d e e o o p e r a d o r d e a t rib u i o
No C#, o sinal de igual = um operador. Todos os operadores retornam um valor com base nos
seus operandos. O operador de atribuio = no diferente, aceita dois operandos; o operando
sua direita avaliado e ento armazenado no operando sua esquerda. O valor do operador de
atribuio o valor que foi atribudo para o operando esquerdo. Por exemplo, na seguinte instruo
de atribuio, o valor retornado pelo operador de atribuio 10, que tambm o valor atribudo
varivel mylnt:
int mylnt;
mylnt = 10; // o valor da expresso de atribuio 10

Captulo 2

Trabalhando com variveis, operadores e expresses

75

Voc provavelmente est pensando que tudo isso interessante e esotrico, mas e da? Bem, como
o operador de atribuio retorna um valor, voc pode utilizar esse mesmo valor com uma outra
ocorrncia da instruo de atribuio, desta maneira:
int mylnt;
int mylnt2;
mylnt2 = mylnt = 10;

O valor atribudo varivel mylnt2 o valor que foi atribudo a mylnt. A instruo de atribuio atri
bui o mesmo valor a ambas as variveis. Essa tcnica muito til se voc quiser inicializar diferentes
variveis com o mesmo valor. Torna-se claro a qualquer leitor do seu cdigo que todas as variveis
devem ter o mesmo valor:
mylnt5 = mylnt4 = mylnt3 = mylnt2 = mylnt = 10;

A partir dessa discusso, voc provavelmente pode deduzir que o operador de atribuio associado
da direita para a esquerda. A atribuio mais direita ocorre primeiro, e o valor atribudo se propaga
pelas variveis da direita para a esquerda. Se uma das variveis j tivesse um valor, esse seria so
brescrito pelo valor sendo atribudo.
Entretanto, trate essa construo com um pouco de cautela. Um erro frequentemente cometidos por
novos programadores C# tentar combinar esse uso do operador de atribuio com declaraes de
variveis, como esta:
int mylnt, mylnt2, mylnt3 = 10

Este um cdigo vlido do C# (porque ele compilado). Na realidade, ele declara as variveis mylnt,
mylnt2 e mylntJ, e inicializa mylnt3 com o valor 10. Contudo, ele no inicializa mylnt ou mylnt2. Se
voc tentar utilizar mylnt ou mylnt2 em expresses como:
mylnt3 = mylnt / mylnt2;

o compilador gerar os seguintes erros:


Use of unassigned local variable 'mylnt'
Use of unassigned local variable 'mylnt2'

Incrementando e decrementando variveis


Se quiser adicionar 1 a uma varivel, o operador + pode ser utilizado:
count = count + 1;

Mas adicionar 1 a uma varivel to comum que o C# fornece um operador somente para essa
finalidade: o operador + + . Para incrementar a varivel count por 1, voc pode escrever a instruo
a seguir:
count++;

76

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Da mesma forma, o C# fornece o operador - - que pode ser utilizado para subtrair 1 de uma varivel,
desta maneira:
count ;

Os operadores + + e - - so operadores unrios, ou seja, eles tm um nico operando. Eles compar


tilham a mesma precedncia e associatividade esquerda que o operador unrio!, que ser discutido
no Captulo 4, Utilizando instrues de deciso".

P refix o e su fix o
Os operadores de incremento, + + , e decremento, - -, fogem do comum porque voc pode coloc-los
antes ou depois da varivel. Quando o smbolo do operador colocado antes da varivel, chamamos
de forma prefixada do operador, e quando colocado depois, chamamos de forma ps-fixada ou sufi
xada do operador. Eis alguns exemplos:
count++;
++count;
count--;
--count;

//
//
//
//

incremento
incremento
decremento
decremento

sufixado
prefixado
sufixado
prefixado

Utilizar a forma prefixa ou sufixa do operador + + ou - - no faz a menor diferena para a varivel
que est sendo incrementada ou decrementada. Por exemplo, se voc escrever count + +, o valor de
count aumenta por 1, e se escrever + +count, o valor de count tambm aumenta por 1. Sabendo isso,
voc provavelmente poderia perguntar por que h duas maneiras de escrever a mesma coisa. Para
entender a resposta, voc precisa lembrar que + + e - - so operadores e que todos os operadores
so utilizados para avaliar uma expresso que tem um valor. O valor retornado por count++ o
valor de count antes da incrementao, enquanto o valor retornado por + -l-count o valor de count
depois que a incrementao ocorre. Veja um exemplo:
int x ;
x = 42;
Console.WriteLine(x++); // x agora 43, 42 escrito no console
x = 42;
Console.WriteLine(++x); // x agora 43, 43 escrito no console

A maneira de lembrar o que cada operando faz examinar a ordem dos elementos (o operando e o
operador) em uma expresso prefixada ou sufixada. Na expresso x+ +, a varivel x ocorre primeiro,
portanto, seu valor utilizado como o valor da expresso antes dex ser incrementado. Na expresso
+ +x, o operador ocorre primeiro, portanto, sua operao executada antes de o valor de x ser cal
culado como o resultado.
Esses operadores so mais utilizados nas instrues while e do, que so apresentadas no Captulo 5,
Utilizando atribuio composta e instrues de iterao . Se voc estiver utilizando os operadores
de incremento e decremento isoladamente, mantenha a forma sufixada e seja consistente.

Declarando variveis locais implicitamente tipadas


Vimos anteriormente neste captulo que uma varivel declarada especificando um tipo de dado e
um identificador, assim:
int mylnt;

Tambm foi mencionado que um valor deve ser atribudo a uma varivel antes de tentar utiliz-la.
Voc pode declarar e inicializar uma varivel na mesma instruo, desta maneira:

Captulo 2

Trabalhando com variveis, operadores e expresses

77

int mylnt = 99;

Ou assim, supondo que myOtherlnt uma varivel do tipo inteiro j inicializada:


int mylnt = myOtherlnt * 99;

Agora, lembre-se de que o valor que voc atribui a uma varivel deve ser do mesmo tipo que a vari
vel. Por exemplo, voc pode atribuir um valor int apenas a uma varivel int. O compilador C# pode
calcular rapidamente o tipo de uma expresso utilizada para inicializar uma varivel e informar se
este no corresponde ao tipo da varivel. Tambm pode instruir o compilador C# a deduzir o tipo de
uma varivel a partir de uma expresso e utiliz-lo ao declarar a varivel usando a palavra-chave
var no lugar do tipo, da seguinte maneira:
var myVariable = 99;
var myOtherVariable = "Hello";

As variveis myVariable e myOtherVariable so conhecidas como variveis implicitamente tipadas. A


palavra-chave var faz o compilador deduzir o tipo das variveis a partir dos tipos das expresses uti
lizados para inicializ-las. Nesses exemplos, myVariable um int, e myOtherVariable uma string.
importante entender que essa uma convenincia apenas para declarar variveis e que, depois
que uma varivel foi declarada, voc s pode atribuir valores do tipo inferido a ela - valores,float,
double ou string no podem ser atribudos a myVariable em um ponto posterior no seu programa,
por exemplo. Voc tambm deve entender que s possvel utilizar a palavra-chave var quando for
necer uma expresso para inicializar uma varivel. A declarao a seguir ilegal e causar um erro
de compilao:
var yetAnotherVariable; // Erro - compilador no pode inferir o tipo

Importante Se voc j programou em Visual Basic, talvez conhea o tipo Variant que pode ser
utilizado para armazenar qualquer tipo de valor em uma varivel. importante enfatizar que voc
deve esquecer tudo que j aprendeu ao programar no Visual Basic variveis Variant. Embora as palavras-chave paream semelhantes, var e Variant so totalmente diferentes. Ao declarar uma varivel
em C# utilizando a palavra-chave var, o tipo de valor que voc atribui varivel no pode mudar em
relao quele utilizado para inicializar a varivel.

Se voc for um purista, provavelmente esteja rangendo os dentes e perguntando-se por que os pro
jetistas de uma linguagem perfeita como o C# permitiram que um recurso como var fosse utilizado.
Afinal, parece uma desculpa para a extrema preguia dos programadores e pode tornar mais difcil
entender o que um programa est fazendo ou rastrear bugs (e pode at mesmo introduzir facilmente
novos bugs no seu cdigo). Mas confie no fato de que var tem um lugar vlido no C#, como veremos
ao trabalhar nos captulos a seguir. Por enquanto, iremos nos ater ao uso de variveis explicitamente
tipadas, exceto quando a tipagem implcita tornar-se uma necessidade.
Neste captulo, voc aprendeu a criar e utilizar variveis, e alguns tipos de dados comuns, dispon
veis para as variveis no C#. Voc conheceu os identificadores. Voc usou alguns operadores para
construir expresses e aprendeu que a precedncia e associatividade dos operadores determinam o
modo como as expresses so avaliadas.
Se voc quiser seguir para o prximo captulo agora:
Mantenha o Visual Studio 2010 em execuo e v para o Captulo 3.
Se quiser sair do Visual Studio 2010:
No menu File, clique em Exit. Se vir um caixa de dilogo Save, clique em Ves e salve o projeto.

78

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Referncia rpida do Captulo 2


Para

Faa isto

Declarar uma varivel

Escreva o nome do tipo de dado, seguido pelo nome da varivel, se


guido por um ponto e vrgula. Por exemplo:
in t outcome;

Alterar o valor de uma varivel

Escreva o nome da varivel esquerda, seguido pelo operador de


atribuio, seguido pela expresso que calcula o novo valor, seguido
por um ponto e vrgula. Por exemplo:
outcome = 42;

Converter uma string em um int

Chame o mtodo System.lnt32.Parse. Por exemplo:


System .Int32.P a rs e ("4 2 ");

Sobrescrever a precedncia de um operador

Utilize parnteses na expresso para explicitar a ordem de avaliao.


Por exemplo:
(3 + 4) * 5

Atribuir o mesmo valor a diferentes variveis

Use uma instruo de atribuio que lista todas as variveis. Por


exemplo:
mylnt4 = mylnt3 = mylnt2 = mylnt = 10;

Incrementar ou decrementar uma varivel

Utilize o operador ++ ou --. Por exemplo:


count++;

Captulo 3

Escrevendo mtodos e
aplicando escopo
Neste captulo, voc vai aprender a:
Declarar e cham ar mtodos.
Passar informaes para um mtodo.
Retornar as informaes de um mtodo.
Definir o escopo de classe e local.
Utilizar o depurador integrado para entrar e sair dos m todos medida que eles so
executados.

No Captulo 2, Trabalhando com variveis, operadores e expresses", voc aprendeu a declarar va


riveis, a criar expresses utilizando operadores, e entendeu de que modo a precedncia e a associatividade controlam a maneira como as expresses que contm mltiplos operadores so avaliadas.
Neste captulo, voc aprender sobre os mtodos. Aprender tambm a utilizar os argumentos e
parmetros para passar informaes para um mtodo e a retorn-las empregando as instrues de
retorno. Por fim, ver como entrar e sair dos mtodos usando o depurador integrado do Microsoft
Visual Studio 2010. Essas informaes so teis quando voc precisa rastrear a execuo dos seus
mtodos se eles no funcionam conforme o esperado.

Criando mtodos
Um mtodo uma sequncia nomeada de instrues. Se voc j utilizou linguagens como C ou
Microsoft Visual Basic, perceber que um mtodo muito semelhante a uma funo ou a uma
sub-rotina. Um mtodo tem um nome e um corpo. O nome do mtodo deve ser um identificador sig
nificativo que indique sua finalidade geral (calcularlmpostoDeRenda, por exemplo). O corpo do mto
do contm as instrues reais a serem executadas quando o mtodo chamado. Alm disso, os mto
dos podem receber alguns dados para serem processados e retornar informaes, que normalmente
so o resultado do processamento, caracterizando-se como um mecanismo poderoso e fundamental.

80

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

D e c la ra n d o u m m to d o
A sintaxe para declarar um mtodo C# :
tipoDeRetorno nomeDoMtodo

( 7

istaDeParmetros)

{
// instrues do corpo do mtodo entram aqui

}
O tipoDeRetorno o nome de um tipo e especifica a informao que o mtodo retorna como
resultado do seu processamento. Ele pode ser qualquer tipo, como int ou string. Se voc est
escrevendo um mtodo que no retorna um valor, deve utilizar a palavra-chave void no lugar
do tipo de retorno.
O nomeDoMtodo o nome utilizado para chamar o mtodo. Os nomes de mtodo seguem as
mesmas regras identificadoras dos nomes de variveis. Por exemplo, addValues um nome de
mtodo vlido, mas add$Values no . Por enquanto, voc dever seguir a conveno camelo
para nomes de mtodos - por exemplo, exibirClientes.
A listaDeParmetros opcional e descreve os tipos e nomes das informaes que voc pode pas
sar para o mtodo processar. Escreva os parmetros entre os parnteses de abertura e fechamen
to como se estivesse declarando variveis, com o nome do tipo seguido pelo nome do parme
tro. Se o mtodo que estiver escrevendo tiver dois ou mais parmetros, separe-os com vrgulas.
As instrues do corpo do mtodo so as linhas de cdigo executadas quando o mtodo cha
mado. Elas ficam entre as chaves de abertura e de fechamento { } .

Importante Os programadores C, C+ + e Microsoft Visual Basic devem notar que o C# no su


porta mtodos globais. Voc deve escrever todos seus mtodos dentro de uma classe ou seu cdigo
no compilar.

Eis a definio de um mtodo chamado addValues que retorna um resultado int e tem dois parme
tros int chamados leftHandSide e rightHandSide-,
int addValues(int leftHandSide, int rightHandSide)

{
//

. . .

// as instrues do corpo do mtodo entram aqui


// ...

Captulo 3

Escrevendo mtodos e aplicando escopo

81

A seguir, a definio de um mtodo chamado showResult que no retorna um valor e tem um nico
parmetro int chamado answer-,
void showResu1t(int answer)

n ...

}
Observe o uso da palavra-chave void para indicar que o mtodo nada retorna.

Importante

Os programadores em Visual Basic devem notar que o C# no utiliza palavras-chave

diferentes para distinguir entre um mtodo que retorna um valor (uma funo) e um mtodo que
no retorna um valor (um procedimento ou sub-rotina). Voc sempre deve especificar um tipo de
retorno ou a palavra-chave void.

R e to rn a n d o d a d o s d e u m m to d o
Para que um mtodo retorne uma informao (ou seja, seu tipo de retorno no void), voc deve
incluir uma instruo de retorno no final do processamento do mtodo. Uma instruo de retorno
consiste em uma palavra-chave retum seguida por uma expresso que especifica o valor retornado
e um ponto e vrgula. 0 tipo da expresso deve ser o mesmo tipo especificado pela declarao do
mtodo. Por exemplo, se um mtodo retorna um int, a instruo de retorno deve retornar um int;
caso contrrio, o programa no compilar. Eis um exemplo de um mtodo com uma instruo retum-.
int addVa1ues(int 1eftHandSide, int rightHandSide)

{
II ...
return TeftHandSide + rightHandSide;

}
A instruo retum geralmente est no final do mtodo porque o faz terminar e controla os retornos
para a instruo que chamou o mtodo, como descrito posteriormente neste captulo. Todas as ins
trues que ocorrerem aps a instruo return no sero executadas (embora o compilador avise
sobre esse problema caso voc coloque instrues depois da instruo retum).
Se no quiser que o mtodo retorne informaes (ou seja, seu tipo de retorno void), voc pode
utilizar uma variao da instruo retum para causar uma sada imediata do mtodo. Escreva a
palavrachave retum, seguida por um ponto e vrgula. Por exemplo:
void showResult(int answer)

{
// exibe a resposta
return;

82

Parte I Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Se o mtodo no retornar coisa alguma, voc tambm pode omitir a instruo retum, porque o m
todo finaliza automaticamente quando a execuo chega chave de fechamento no fim do mtodo.
Embora essa prtica seja comum, ela nem sempre considerada um bom estilo de programao.
No exerccio a seguir, examinaremos uma outra verso do projeto MathsOperators do Captulo 2.
Essa verso foi aprimorada pela utilizao cuidadosa de alguns pequenos mtodos.

Examine as definies de mtodo


1. Inicialize o Visual Studio 2010 se ainda no estiver em execuo.
2. Abra o projetoMethods na pasta \Microsoft PressWisual CSharp Step By Step\Chapter 3\Methods
na sua pasta Documentos.
3. No menu Debug, clique em Start Without Debugging.
O Visual Studio 2010 compila e executa o aplicativo.
4. Explore o aplicativo e o modo como ele funciona, e clique em Quit.
5. Exiba o cdigo de MainWindow.xaml.es na janela Code and Text Editor.
6. Na janela Code and Text Editor, localize o mtodo addValues.
O mtodo semelhante a este:
private int addValues(int leftHandSide, int rightHandSide)

{
expression.Text = leftHandSide.ToStringO + " + " + rightHandSide.ToStringO;
return leftHandSide + rightHandSide;

}
O mtodo addValues contm duas instrues. A primeira exibe o clculo executado na caixa de
texto expression do formulrio. Os valores dos parmetros leftHandSide e rightHandSide so
convertidos em strings (utilizando o mtodo ToString que vimos no Captulo 2) e concatenados
com uma representao em string do operador de adio (+) no meio.
A segunda utiliza o operador + para somar os valores das variveis int leftHandSide e
rightHandSide e retorna o resultado dessa operao. Lembre-se de que somar dois valores
int cria outro valor int, portanto, o tipo de retorno do mtodo addValues int. Se examinar
os mtodos subtractValues, multiplyValues, divideValues e remainderValues, voc ver que
eles seguem um padro semelhante.
7. Na janela Code and Text Editor, localize o mtodo showResult.
O mtodo showResult semelhante a este:
private void showResult(int answer)

{
result.Text = answer.ToStringO ;

Captulo 3

Escrevendo mtodos e aplicando escopo

83

Esse mtodo contm uma instruo que exibe uma representao em string do parmetro answer na
caixa de texto result. Ele no retorna um valor, de modo que o tipo desse mtodo void.

Dica

No h um comprimento mnimo para um mtodo. Se um mtodo ajudar a evitar a re

petio e a tornar seu programa mais fcil de entender, ele ser til, independentemente do seu
tamanho.
No h tambm um tamanho mximo para um mtodo, mas uma boa prtica de programao
mant-lo com o menor tamanho possvel. Se o mtodo ocupar mais que uma tela, considere a pos
sibilidade de dividi-lo em mtodos menores para torn-lo mais legvel.

C h a m a n d o m to d o s
Os mtodos existem para ser chamados! Voc chama um mtodo pelo nome para pedir a ele que
execute sua tarefa. Se o mtodo precisar de informaes (conforme especificado pelos seus parme
tros), voc deve fornec-las. Se o mtodo retorna informaes (conforme especificado pelo seu tipo de
retorno), voc deve providenciar sua captura de alguma maneira.

E sp e c ific a n d o a sin ta x e d e c h a m a d a d e m to d o
A sintaxe de uma chamada de mtodo em C# :
resultado = nomeDoMtodo (listaDeArgumentos)

O nomeDoMtodo deve corresponder exatamente ao nome do mtodo que voc est chamando.
Lembre-se, o C# uma linguagem que faz distino entre maisculas e minsculas.
A clusula resultado = opcional. Se especificada, a varivel identificada como resultado conte
r o valor retornado pelo mtodo. Se o mtodo for void (no retorna um valor), voc deve omitir
a clusula resultado = da instruo. Se voc no especificar a clusula resultado = e o mtodo
retornar um valor, o mtodo ser executado, mas o valor de retorno ser descartado.
A listaDeArgumentos oferece informaes opcionais que o mtodo aceita. Voc deve fornecer
um argumento para cada parmetro, e o valor de cada argumento deve ser compatvel com o
tipo do seu parmetro correspondente. Se o mtodo que voc est chamando tiver dois ou mais
parmetros, separe os argumentos com vrgulas.

Importante

Voc deve incluir os parnteses em cada chamada de mtodo, mesmo quando

estiver chamando um mtodo sem argumentos.

84

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Para esclarecer esses pontos, examine o mtodo add.Va.lues novamente:


int addVa1ues(int TeftHandSide, int rightHandSide)

II ...
}
O mtodo addValues tem dois parmetros int , portanto, voc deve cham-lo com dois argumentos
int separados por vrgulas:
addValues(39, 3); // ok

Voc tambm pode substituir os valores literais 39 e 3 pelos nomes de variveis int. Os valores des
sas variveis so ento passados para o mtodo como seus argumentos, como a seguir:
int argl = 99;
int arg2 = 1;
addValues(argl, arg2);

Se voc tentar chamar addValues de alguma outra maneira, provavelmente no ser bem-sucedido,
pelas razes descritas nos exemplos abaixo:
addValues;
addValuesO;
addVa1ues(39);
addValues("39", "3");

//
//
//
//

erro
erro
erro
erro

de
de
de
de

tempo
tempo
tempo
tempo

de
de
de
de

compilao,
compilao,
compilao,
compilao,

sem parnteses
sem argumentos suficientes
sem argumentos suficientes
tipos errados

O mtodo addValues retorna um valor int. Esse valor int poder ser utilizado sempre que um valor
int puder ser utilizado. Considere estes exemplos:
int result = addValues(39, 3);
showResult(addValues(39, 3));

// no lado direito de uma atribuio


// como argumento para outra chamada de mtodo

O exerccio a seguir continua a analisar o aplicativo Methods. Desta vez, voc vai examinar algumas
chamadas de mtodo.

Examine as chamadas de mtodo


1. Retorne ao projeto Methods. (Esse projeto j estar aberto no Visual Studio 2010, se voc estiver
continuando do exerccio anterior. Se no, abra-o na pasta \Microsoft PressWisual CSharp Step
By Step\Chapter 3\Methods na sua pasta Documentos.)
2. Exiba o cdigo de MainWindow.xaml.es na janela Code and Text Editor.
3. Localize o mtodo calculateClick e examine as duas primeiras instrues desse mtodo aps a
instruo try e uma chave de abertura. (Abordaremos a finalidade das instrues try no Captu
lo 6, Gerenciando erros e excees .)
As instrues so:
int leftHandSide = System.Int32.Parse(lhsOperand.Text);
int rightHandSide = System.Int32.Parse(rhsOperand.Text);

Captulo 3

Escrevendo mtodos e aplicando escopo

85

Essas duas instrues declaram duas variveis int denominadas leftHandSide e rghtHandSide.
Entretanto, as partes interessantes so como as variveis so inicializadas. Em ambos os casos,
o mtodo Parse da classe System.Int32 chamado. (System um namespace, e Int32 o nome
da classe nesse namespace.) Vimos esse mtodo anteriormente; ele recebe um nico parme
tro strng e o converte em um valor int. Essas duas linhas de cdigo recebem as entradas do
usurio nos controles de caixa de texto IhsOperand e rhsOperand no formulrio e as converte
em valores int.

4. Examine a quarta instruo no mtodo calculateClick (aps a instruo i f t outra chave de aber
tura):
calculatedValue = addValues(leftHandSide, rightHandSide);

Essa instruo chama o mtodo addValues, passando os valores das variveis leftHandSide e
rghtHandSide como seus argumentos. O valor retornado pelo mtodo addValues armazenado
na varivel ealculatedValue.
5. Examine a prxima instruo:
showResult(calculatedValue);

Essa instruo chama o mtodo showResult, passando o valor da varivel ealculatedValue como
seu argumento. O mtodo showResult no retorna um valor.
6. Na janela Code and Text Editor, localize o mtodo showResult examinado anteriormente. A ni
ca instruo desse mtodo esta:
result.Text = answer.ToString();

Observe que a chamada do mtodo ToString utiliza parnteses embora no haja argumentos.

Dica

Voc pode chamar os mtodos que pertencem a outros objetos prefixando o mtodo

com o nome do objeto. No exemplo anterior, a expresso answer.ToStringO chama o mtodo


chamado ToString pertencente ao objeto chamado answer.

Aplicando escopo
Em alguns exemplos, voc pode ver que possvel criar variveis dentro de um mtodo. Essas va
riveis passam a existir a partir do ponto em que elas so definidas, e as instrues subsequentes
no mesmo mtodo podem ento utiliz-las; uma varivel s pode ser explorada depois de ser criada.
Quando o mtodo termina, essas variveis desaparecem.
Se uma varivel pode ser empregada em um local especfico em um programa, dizemos que ela est
no escopo desse local. Ou seja, o escopo de uma varivel simplesmente a regio do programa na

86

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

qual essa varivel utilizada. O escopo se aplica aos mtodos e s variveis. O escopo de um identifi
cador (de uma varivel ou mtodo) est vinculado ao local da declarao que introduz o identificador
no programa, como voc ver agora.

D e fin in d o o e s c o p o lo cal
As chaves de abertura e fechamento que formam o corpo de um mtodo definem um escopo. Todas
as variveis que voc declara dentro do corpo de um mtodo esto no seu escopo; elas desaparecem
quando o mtodo termina e s podem ser acessadas pelo cdigo executado dentro desse mtodo.
Essas variveis so denominadas variveis locais porque so locais para o mtodo em que so decla
radas; elas no esto no escopo de outro mtodo. Essa estrutura significa que voc no pode utilizar
as variveis locais para compartilhar informaes entre os mtodos. Considere este exemplo:
class Example

{
void fi rstMethodO

{
int myVar;

}
void anotherMethodO

{
myVar = 42; // erro - varivel fora de escopo

}
}
Ocorrer uma falha na compilao desse cdigo porque anotherMethod est tentando utilizar a va
rivel myVar que no est no escopo. A varivel myVar s est disponvel para as instrues em
JirstMethod, ocorrendo depois que a linha do cdigo declarada como myVar.

D e fin in d o o e s c o p o d e c la s s e
As chaves de abertura e fechamento que formam o corpo de uma classe tambm criam um escopo.
Todas as variveis que voc declara dentro do corpo de uma classe (mas no dentro de um mtodo)
esto no escopo dela. O nome apropriado do C# para as variveis definidas por uma classe field
(campo). Ao contrrio das variveis locais, os campos podem ser utilizados para compartilhar infor
maes entre mtodos. A seguir, um exemplo:
class Example

{
void fi rstMethodO

{
myField = 42; // ok

Captulo 3

Escrevendo mtodos e aplicando escopo

87

void anotherMethodO

{
myField++; // ok

}
int myField = 0;

}
A varivel myField definida dentro da classe, mas fora dos mtodosfirstMethod e anotherMethod.
Portanto, myField tem escopo de classe e est disponvel para uso por todos os mtodos na classe.
H outro ponto a ser observado nesse exemplo. Em um mtodo, voc deve declarar uma varivel an
tes de poder utiliz-la. Os campos so um pouco diferente. Um mtodo pode utilizar um campo antes
da instruo que define o campo - o compilador resolve os detalhes para voc!

S o b re c a rre g a n d o m to d o s
Se dois identificadores tm o mesmo nome e so declarados no mesmo escopo, dizemos que eles
esto sobrecarregados (overloaded). Um identificador sobrecarregado costuma ser um erro capturado
como um erro de tempo de compilao. Por exemplo, se declarar duas variveis locais com o mes
mo nome no mesmo mtodo, o compilador informar um erro. Da mesma forma, se declarar dois
campos com o mesmo nome na mesma classe ou dois mtodos idnticos na mesma classe, voc
tambm receber um erro de tempo de compilao. No vale a pena mencion-lo, uma vez que tudo
que vimos at aqui tem resultado em um erro de tempo de compilao. Mas h uma maneira til e
importante pela qual voc pode sobrecarregar um identificador.
Considere o mtodo WriteLine da classe Console. Voc j utilizou esse mtodo para escrever uma
string na tela. Mas ao digitar WriteLine na janela Code and Text Editor escrevendo em C#, voc no
tar que o Microsoft IntelliSense oferece 19 opes diferentes! Cada verso do mtodo WriteLine tem
um conjunto de parmetros diferente; uma verso no tem parmetros e simplesmente gera uma
linha em branco; outra aceita um parmetro bool e gera uma representao em string desse valor
(:True ou False)-, e ainda outra aceita um parmetro decimal e gera uma string, e assim por diante. Em
tempo de compilao, o compilador examina os tipos de argumentos que voc est passando e ento
chama a verso do mtodo que tem o conjunto de parmetros correspondente. Segue um exemplo:
static void Main()

{
Console.WriteLine("The answer is
Console.WriteLi ne(42);

>
A sobrecarga til principalmente quando voc precisa executar a mesma operao em diferentes
tipos de dados. Voc pode sobrecarregar um mtodo quando as diferentes implementaes tm dife
rentes conjuntos de parmetros; isto , quando elas tm o mesmo nome, mas um nmero diferente

88

Parte I

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

de parmetros, ou quando os tipos de parmetro forem diferentes. Esse recurso permitido para que,
quando voc chamar um mtodo, possa fornecer uma lista de argumentos separados por vrgula; e o
nmero e o tipo dos argumentos sejam utilizados pelo compilador para selecionar um dos mtodos
sobrecarregados. Mas observe que, embora possa sobrecarregar os parmetros de um mtodo, voc
no pode sobrecarregar o tipo de retorno de um mtodo. Ou seja, voc no pode declarar dois mto
dos com o mesmo nome cuja diferena seja apenas o seu tipo de retorno (o compilador inteligente,
mas no to inteligente).

Escrevendo mtodos
Nos exerccios a seguir, voc criar um mtodo que calcula quanto um consultor ganhar por um
determinado nmero de dias de consultoria a uma dada remunerao por dia. Voc comear desen
volvendo a lgica do aplicativo e ento utilizar o assistente Generate Method Stub para ajudar a
escrever os mtodos que sero utilizados por essa lgica. Em seguida, voc executar esses mtodos
em um aplicativo Console para ter uma ideia do programa. Por fim, voc ir explorar o depurador do
Visual Studio 2010 para entrar e sair das chamadas de mtodo medida que elas so executadas.

Desenvolva a lgica do aplicativo


1. Utilizando o Visual Studio 2010, abra o projeto DailyRate na pasta \Microsoft PressWisualCSharp Step By Step\Chapter 3\DailyRate na sua pasta Documentos.
2. No Solution Explorer, clique duas vezes no arquivo Program.cs para exibir o programa na janela

Code and Text Editor.


3. Adicione as seguintes instrues ao corpo do mtodo run, entre as chaves de abertura e de fe
chamento:
double dailyRate = readDoub1e("Enter your daily rate: ");
int noOfDays = readInt("Enter the number of days: ");
wnteFee(ca1cu1ateFee(dailyRate, noOfDays));

O mtodo run chamado pelo mtodo Main quando o aplicativo inicia. (A maneira como ele cha
mado requer um entendimento das classes, o que examinaremos no Captulo 7, Criando e gerenciando classes e objetos .)
O bloco de cdigo que voc adicionou ao mtodo run chama o mtodo readDouble (que voc vai
escrever em breve) para pedir ao usurio que informe a taxa diria para o consultor. A prxima
instruo chama o mtodo readlnt (que voc tambm vai escrever) para obter o nmero de dias. Por
fim, o mtodo writeFee (a ser escrito) chamado para exibir os resultados na tela. Observe que o
valor passado para writeFee o valor retornado pelo mtodo calculateFee (o ltimo que precisar ser
escrito), ao qual informado o preo por dia e o nmero de dias, e calcula a taxa total a ser paga.

Captulo 3

Escrevendo mtodos e aplicando escopo

89

Escreva os mtodos utilizando o assistente Generate Method Stub


1. Na janela Code and Text Editor, clique com o boto direito do mouse na chamada do mtodo
readDouble no mtodo run.
Um menu de atalho aparece, contendo comandos teis para gerar e editar cdigo, como mos
trado aqui:

2. Nesse menu de atalho, aponte para Generate e clique em Method Stub.


O assistente Generate Method Stub examina a chamada ao mtodo readDouble, verifica o tipo
dos seus parmetros e do valor de retorno e gera um mtodo com uma implementao padro,
como mostrado a seguir:
private double readDouble(string p)

{
throw new NotlmplementedExceptionO;

90

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

0 novo mtodo criado com o qualificador private, descrito no Captulo 7. Atualmente o corpo
do mtodo simplesmente lana uma NotlmplementedException (excees so descritas no Cap
tulo 6). Voc substituir o corpo pelo seu prprio cdigo na prxima etapa.
3. Exclua a instruo throw new NotImplementedException(); do mtodo readDouble e a substitua
pelas linhas de cdigo a seguir:
Console.Write(p);
string line = Console.ReadLineQ;
return double.Parse(line);

Esse bloco de cdigo exibe a string da varivel p na tela. Essa varivel o parmetro de string
que passado quando o mtodo chamado e contm uma mensagem solicitando que o usurio
digite a taxa diria.

Nota

O mtodo Console. Write semelhante instruo Console. WriteLine j utilizada nos

exerccios anteriores, exceto pelo fato de ele no gerar um caractere de nova linha depois da
mensagem.

O usurio digita um valor, que lido em um tipo string utilizando o mtodo ReadLine e conver
tido em um tipo double utilizando o mtodo double.Parse. O resultado passado de volta como
o valor de retorno da chamada de mtodo.

Nota

O mtodo ReadLine companheiro do mtodo WriteLine', ele l a entrada do usurio

no teclado, terminando quando o usurio pressiona a tecla Enter. O texto digitado pelo usurio
passado de volta como o valor de retorno. O texto retornado como um valor de string.

4. No mtodo run, clique com o boto direito do mouse na chamada ao mtodo readlnt, aponte
para Generate e clique em Method Stub para gerar o mtodo readlnt.

O mtodo readlnt gerado, desta maneira:


private int readlnt(string p)

{
throw new NotImplementedException();

}
5. Substitua a instruo throw new NotlmplementedExceptionQ; no corpo do mtodo readlnt pelo
cdigo a seguir:
Console.Write(p);
string line = Console.ReadLineO;
return int.Parse(line);

Captulo 3

Escrevendo mtodos e aplicando escopo

91

Esse bloco de cdigo semelhante ao cdigo do mtodo readDouble. A nica diferena que o
mtodo retorna um valor int, portanto, a string digitada pelo usurio convertida em um n
mero, utilizando o mtodo int.Parse.
6. Clique com o boto direito do mouse na chamada ao mtodo calculateFee dentro do mtodo run,
Aponte para Generate e clique em Method Stub.
O mtodo calculateFee gerado, desta maneira:
private object calculateFee (double dailyRate, int noOfDays)

{
throw new NotlmplementedExceptionO;

}
Nesse caso, observe que o Visual Studio utiliza o nome dos argumentos passados para gerar os
nomes dos parmetros (voc pode alterar os nomes dos parmetros se eles no forem adequa
dos). O mais intrigante o tipo retornado pelo mtodo, que object. O Visual Studio incapaz
de determinar exatamente que tipo de valor deve ser retornado pelo mtodo a partir do contexto
em que ele chamado. O tipo object significa apenas uma coisa , e voc deve alter-lo para
o tipo necessrio quando adicionar o cdigo ao mtodo. Discutiremos o tipo object em mais
detalhes no Captulo 7.
7. Mude a definio do mtodo calculateFee para que ele retorne um double, como mostrado em
negrito aqui:
private double calculateFee(double dailyRate, int noOfDays)

{
throw new NotlmplementedExceptionO;

}
8. Substitua o corpo do mtodo calculateFee pela instruo a seguir, que calcula e retorna a remu
nerao a ser paga multiplicando os dois parmetros:
return dailyRate * noOfDays;

9. Clique com o boto direito do mouse na chamada do mtodo writeFee no mtodo run e clique em

Generate Method Stub.


Observe que o Visual Studio utiliza a definio do mtodo calculateFee para concluir que seu pa
rmetro deve ser um double. Alm disso, a chamada do mtodo no utiliza um valor de retorno,
portanto o tipo do mtodo void:
private void writeFee(double p)

<*>

Dica

Se voc se sentir vontade com a sintaxe, tambm pode escrever os mtodos digitan

do-os diretamente na janela Code and Text Editor. No necessrio utilizar sempre a opo de
menu Generate.

92

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

10. Digite as instrues a seguir dentro do mtodo writeFee


Console.WriteLine("The consultant's fee is: {0}", p * 1.1);

11. No menu Build, clique em Build Solution.

1. No menu Debug, clique em Start Without Debugging.


0 Visual Studio 2010 compila o programa e o executa. Uma janela de console exibida.
2. No prompt Enteryour daily rate, digite 525 e pressione Enter.
3. No prompt Enter the number o f days, digite 17 e pressione Enter.
0 programa escreve a seguinte mensagem na janela de console:
The consultant's fee is: 9817.5

4. Pressione a tecla Enter para fechar o aplicativo e retornar ao ambiente de programao do Vi


sual Studio 2010.

Captulo 3

Escrevendo mtodos e aplicando escopo

93

No prximo exerccio, voc vai utilizar o depurador do Visual Studio 2010 para executar seu pro
grama lentamente. Voc ver quando cada mtodo chamado (o que citado como stepping into of
the method ou entrar no mtodo) e como cada instruo de retorno transfere o controle de volta
ao chamador (tambm conhecido como stepping out o f the method ou sair do mtodo). Ao entrar
e sair dos mtodos, voc vai utilizar as ferramentas da barra de ferramentas Debug. Mas os mesmos
comandos tambm esto disponveis no menu Debug quando um aplicativo est sendo executado no
modo Debug.

inspecione os mtodos passo a passo utilizando o depurador


do Visual Studio 2010
1. Na janela Code and Text Editor, localize o mtodo run.
2. Mova o mouse para a primeira instruo do mtodo run.
double dailyRate = readDoub1e("Enter your daily rate: ");

3. Clique com o boto direito do mouse em qualquer lugar dessa linha e, no menu de atalho, clique
em Run To Cursor.
O programa inicia e executado at chegar primeira instruo do mtodo run e, ento, faz
uma pausa. Uma seta amarela na margem esquerda da janela Code and Text Editor indica a
instruo atual, que tambm realada por um segundo plano amarelo.
4. No menu

View, aponte para Toolbars e verifique se a barra de ferramentas Debug est selecionada.

Se ela ainda no estiver visvel, a barra de ferramentas Debug aberta. Ela pode aparecer en
caixada com as outras barras de ferramentas. Se no puder ver a barra de ferramentas, tente
utilizar o comando Toolbars no menu View para ocult-la e observe quais botes desaparecem.
Ento exiba a barra de ferramentas novamente. A barra de ferramentas Debug se parece a esta
(embora a barra de ferramentas seja um pouco diferente entre o Visual Studio 2010 e o Micro
soft Visual C# 2010 Express, ela no contm o boto Breakpoint no lado direito):
Step into
1
;

iii

1
Continue

Dica

<> ,?-=

Step over

...1
(*-2

-J

_________

Hex

% : -3

1
Step out

Para fazer a barra de ferramentas Debug ser exibida na sua prpria janela, utilize a ala

na extremidade esquerda da barra de ferramentas e arraste-a sobre a janela Code and Text

Editor.

5. Na barra de ferramentas Debug, clique no boto Step Into ( o sexto boto esquerda).

Essa ao faz o depurador entrar no mtodo chamado. O cursor amarelo pula para a chave de
abertura no incio do mtodo readDouble.

94

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

6. Clique novamente em Step Into. O cursor avana para a primeira instruo:


Console.Write(p);

Dica

Voc tambm pode pressionar F11 em vez de clicar vrias vezes em Step

de ferramentas

Into na

barra

Debug.

7. Na barra de ferramentas Debug, clique em Step Over ( o stimo boto esquerda).


Essa ao faz o mtodo executar a prxima instruo sem depur-la (sem entrar nele). O cursor
amarelo se move para a segunda instruo do mtodo, e o programa exibe o prompt Enteryour
daily rate em uma janela Console antes de retornar ao Visual Studio 2010 (a janela Console
talvez esteja oculta atrs do Visual Studio).

Dica

Voc tambm pode pressionar F10 em vez de clicar em

Step Over na

barra de ferra-

mentas Debug.

8. Na barra de ferramentas Debug, clique em Step Over.


Desta vez, o cursor amarelo desaparece, e a janela de Console recebe o foco porque o programa
est executando o mtodo Console.ReadLine e esperando que voc digite algo.
9. Digite 525 na janela Console e pressione Enter.
O controle retorna ao Visual Studio 2010. O cursor amarelo aparece na terceira linha do mtodo.
10. Posicione o mouse sobre a referncia varivel line na segunda ou na terceira linha do mtodo
(no importa qual delas).
Uma dica de tela aparece, exibindo o valor atual da varivel line (525). Voc pode utilizar esse
recurso para verificar se uma varivel foi definida com um valor esperado durante a execuo
passo a passo dos mtodos.
11. Na barra de ferramentas Debug, clique em Step Out ( o oitavo boto esquerda).
Essa ao faz o mtodo atual continuar a executar ininterruptamente at o fim. O mtodo readDouble termina, e o cursor amarelo colocado de volta na primeira instruo do mtodo run.

Dica

Voc tambm pode pressionar Shift+F11 em vez de clicar em

ferramentas

Debug.

Step Out

na barra de

Captulo 3

Escrevendo mtodos e aplicando escopo

95

12. Na barra de ferramentas Debug, clique em Step Into.


0 cursor amarelo se move para a segunda instruo no mtodo run-.
int noOfDays = readlntCEnter the number of days: ");

13. Na barra de ferramentas Debug, clique em Step Over.


Desta vez, voc escolheu executar o mtodo sem fazer a inspeo passo a passo. A janela Con
sole aparece novamente solicitando o nmero de dias.
14. Na janela Console, digite 17 e pressione Enter.
O controle retorna ao Visual Studio 2010. O cursor amarelo se move para a terceira instruo
do mtodo run-.
writeFee(calculateFee(dailyRate, noOfDays));

15. Na barra de ferramentas Debug, clique em Step Into.


O cursor amarelo pula para a chave de abertura no incio do mtodo calculateFee. Esse mtodo
o primeiro a ser chamado, antes de writeFee, porque o valor retornado por esse mtodo utili
zado como o parmetro para writeFee.
16. Na barra de ferramentas Debug, clique em Step Out.
O cursor amarelo pula de volta para a terceira instruo do mtodo run.
17. Na barra de ferramentas Debug, clique em Step Into.
Desta vez, o cursor amarelo pula para a chave de abertura no incio do mtodo writeFee.
18. Coloque o mouse sobre a varivel p na definio do mtodo.
O valor de p, 8925.0, exibido em uma dica de tela.
19. Na barra de ferramentas Debug, clique em Step Out.
A mensagem The consultants fee is.- 9817.5 exibida na janela Console (voc pode precisar
abrir a janela Console no primeiro plano para exibi-la se ela estiver atrs do Visual Studio
2010). O cursor amarelo retorna terceira instruo do mtodo run.
20. Na barra de ferramentas Debug, clique em Continue (o primeiro boto na barra de ferramentas)
para fazer o programa continuar a executar sem parar em cada instruo.

Dica

Voc tambm pode pressionar F5 para continuar a execuo no depurador.

O aplicativo termina e para de executar.

96

Parte I

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Utilizando parmetros opcionais e argumentos


nomeados
Voc j sabe que, ao definir mtodos sobrecarregados, possvel implementar diversas verses de um
mtodo, que aceitam diferentes parmetros. Quando voc constri um aplicativo que utiliza mtodos
sobrecarregados, o compilador determina quais instncias especficas de cada mtodo ele deve usar
para atender chamada de cada mtodo. Esse um recurso comum de vrias linguagens orientadas
a objetos, no apenas do C#.
Entretanto, existem outras linguagens e tecnologias que os desenvolvedores podem utilizar para
construir aplicativos Windows e componentes que no seguem essas regras. Um recurso importante
do C# e de outras linguagens elaboradas para o .NET Framework a possibilidade de interoperar
com os aplicativos e componentes escritos em outras tecnologias. Uma das principais tecnologias
utilizadas pelo Microsoft Windows o Component Object Model ou COM, que no oferece suporte
para mtodos sobrecarregados, mas utiliza mtodos que aceitam parmetros opcionais. Para facilitar
ainda mais a incorporao de bibliotecas COM e componentes em uma soluo do C#, esta lingua
gem tambm dispe de suporte para os parmetros opcionais.
Os parmetros opcionais tambm so teis em outras situaes. Eles representam uma soluo
compacta e simples, quando no possvel utilizar a sobrecarga porque os tipos dos parmetros
no variam o bastante para permitir que o compilador possa distinguir entre as implementaes. Por
exemplo, considere o seguinte mtodo:
public void DoWorkWithDataCint intData, float floatData, int morelntData)

{
}
O mtodo DoWorkWithData aceita trs parmetros: dois ints e umfloat. Vamos supor que voc quei
ra fornecer uma implementao do mtodo DoWorkWithData que aceite apenas dois parmetros:
intData efloatData. Voc pode sobrecarregar o mtodo, como demonstrado a seguir:
public void DoWorkWithDataCint intData, float floatData)

{
}
Se voc escrever uma instruo que chama o mtodo DoWorkWithData, poder fornecer dois ou
trs parmetros dos tipos adequados, e o compilador usar a informao do tipo para determinar a
sobrecarga a ser chamada:
int argl = 99;
float arg2 = 100.0F;
int arg3 = 101;
DoWorkWithData(argl, arg2, arg3); // Chamar a sobrecarga com trs parmetros
DoWorkWithData(argl, arg2);
// Chamar a sobrecarga com dois parmetros

Captulo 3

Escrevendo mtodos e aplicando escopo

97

Entretanto, vamos supor que voc queira implementar duas outras verses do mtodo DoWorkWithData,
que aceitem apenas o primeiro e o terceiro parmetros. Voc poderia experimentar o seguinte:
public void DoWorkWithDataCint intData)

{
}
public void DoWorkWithDataCint morelntData)

{
}
A questo que, para o compilador, essas duas sobrecargas parecem idnticas e a compilao de seu c
digo falhar e gerar o erro Type typename' already defines a member called DoWorkWithData with
the same parameter types (O tipo nome_do_tipo j define um membro chamado DoWorkWithData
com os mesmos tipos de parmetro). Para entender por que isso acontece, se esse cdigo era vlido,
considere as seguintes instrues:
int argl = 99;
int arg3 = 101;
DoWorkWi thData(argl);
DoWorkWi thData(arg3);

Que sobrecarga ou sobrecargas as chamadas ao mtodo DoWorkWithData acionariam? O uso de


parmetros opcionais e argumentos nomeados pode ajudar a solucionar esse problema.

D e fin in d o p a r m e tro s o p c io n a is
Ao definir um mtodo, voc especifica que um parmetro opcional, fornecendo um valor padro
para o parmetro. Para indicar um valor padro, utilize um operador de atribuio. No mtodo optMethod mostrado a seguir, o parmetrofirst obrigatrio porque ele no especifica um valor padro,
mas os parmetros second e third so opcionais:
void optMethodCint first, double second = 0 . 0 , string third = "Hello")

{
}
Voc deve especificar todos os parmetros obrigatrios antes de qualquer parmetro opcional.
Chame um mtodo que aceita parmetros opcionais da mesma maneira como voc chama qualquer
outro mtodo; especifique o nome do mtodo e inclua os argumentos necessrios. A diferena em
relao aos mtodos que aceitam parmetros opcionais a possibilidade de omitir os argumentos
correspondentes, e o mtodo usar o valor padro quando for executado. No exemplo de cdigo a
seguir, a primeira chamada ao mtodo optMethod fornece os valores dos trs parmetros. A segunda

98

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

chamada especifica apenas dois argumentos, e esses valores so aplicados aos parmetrosfirst e

second. O parmetro third recebe o valor padro de Hello quando o mtodo executado.
optMethod(99, 123.45, "World"); // Argumentos fornecidos para os trs parmetros
optMethod(100, 54.321);
// Argumentos fornecidos para os dois primeiros parmetros
// apenas

P a ss a n d o a r g u m e n to s n o m e a d o s
Por padro, o C# utiliza a posio de cada argumento na chamada a um mtodo para determinar os
parmetros aos quais eles so aplicveis. Portanto, o segundo exemplo de mtodo mostrado na seo
anterior passa os dois argumentos para os parmetrosfirst e second no mtodo optMethod, porque
essa a sequncia na qual eles ocorrem na declarao do mtodo. O C# tambm permite especifi
car os parmetros pelo nome, e esse recurso deixa voc passar os argumentos em uma sequncia
diferente. Para passar um argumento como um parmetro nomeado, inclua o nome do parmetro,
um caractere de dois-pontos, e o valor a ser utilizado. Os exemplos a seguir desempenham a mesma
funo daqueles apresentados na seo anterior, exceto pelo fato de que os parmetros so especifi
cados por nome:
optMethod(first : 99, second ; 123.45, third : "World");
optMethod(first : 100, second : 54.321);

Os argumentos nomeados permitem que voc passe os argumentos em qualquer ordem. Voc pode
reescrever o cdigo que chama o mtodo optMethod, como a seguir:
optMethod(third : "World", second : 123.45, first ; 99);
optMethod(second : 54.321, first : 100);

Esse recurso tambm permite omitir os argumentos. Por exemplo, voc pode chamar o mtodo opt
Method e especificar apenas os valores dos parmetrosfirst e third e utilizar o valor padro para o
parmetro second, como a seguir:
optMethod(first : 99, third : "World");

Alm disso, possvel mesclar argumentos posicionais e nomeados. Entretanto, ao utilizar essa tc
nica, voc deve especificar todos os argumentos posicionais antes do primeiro argumento nomeado:
optMethod(99, third : "World");

// 0 primeiro argumento posicionai

R e so lv e n d o a m b ig u id a d e s c o m p a r m e tr o s o p c io n a is e
a r g u m e n to s n o m e a d o s
O uso de parmetros opcionais e argumentos nomeados pode gerar algumas ambiguidades em seu
cdigo. Voc deve saber como o compilador resolve essas ambiguidades; caso contrrio, seus apli
cativos podero se comportar de modo imprevisto. Vamos supor que voc tenha definido o mtodo
optMethod como um mtodo sobrecarregado, como mostra o exemplo a seguir:

Captulo 3

Escrevendo mtodos e aplicando escopo

99

void optMethodCint first, double second = 0.0, string third = "Hello")

{
}
void optMethod(int first, double second = 1.0, string third = "Goodbye", int fourth = 100 )

{
}
Este um cdigo do C# perfeitamente vlido, que segue as regras dos mtodos sobrecarregados. O
compilador pode diferenciar entre os mtodos porque eles tm listas de parmetros diferentes. En
tretanto, pode ocorrer um problema se voc tentar chamar o mtodo optMethod e omitir algum dos
argumentos correspondentes a um ou mais parmetros opcionais:
optMethodCl, 2.5, "World");

Mais uma vez, um cdigo vlido, mas ele executa qual verso do mtodo optMethodl A resposta
que ele executa a verso que mais se aproxima da chamada ao mtodo, de modo que ele chama o
mtodo que aceita trs parmetros, e no a verso que aceita quatro. Isso justificvel; portanto,
considere o seguinte:
optMethodCl, fourth : 101);

Nesse cdigo, a chamada ao mtodo optMethod omite os argumentos dos parmetros second e third,
mas especifica o parmetrofourth pelo nome. Apenas uma verso do mtodo optMethod corresponde
a essa chamada, de modo que no ocorre qualquer problema. Entretanto, este cdigo vai deix-lo
intrigado!
optMethodCl, 2.5);

Dessa vez, nenhuma das verses do mtodo optMethod combina exatamente com a lista de argumen
tos fornecida. Ambas as verses desse mtodo tm parmetros opcionais para o segundo, o terceiro
e o quarto argumentos. Ento, essa instruo chama a verso do mtodo optMethod que aceita trs
parmetros e utiliza o valor padro para o parmetro third ou chama a verso do optMethod que
aceita quatro parmetros e utiliza o valor padro para os parmetros third eJourthl A resposta
nem uma coisa, nem outra. O compilador determina que essa uma chamada de mtodo ambgua
e no permite a compilao do aplicativo. A mesma situao ocorrer, com o mesmo resultado, se
voc tentar chamar o mtodo optMethod, como mostrado em qualquer uma das seguintes instrues:
optMethodCl, third : "World");
optMethodCl);
optMethodCsecond : 2.5, first : 1);

No ltimo exerccio deste captulo, voc vai praticar a implementao de mtodos que aceitam par
metros opcionais, e cham-los por meio de argumentos nomeados. Voc tambm testar exemplos
comuns de como o compilador do C# resolve as chamadas a mtodos que englobam parmetros
opcionais e argumentos nomeados.

100

Parte I

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Defina e chame um mtodo que aceita parmetros opcionais


1. No Visual Studio 2010, abra o projeto DailyRate, armazenado na pasta \Microsoft PressWisual
CSharp Step By Step\Chapter 3\DailyRate Using Optional Parameters, dentro da pasta Documentos.
2. No Solution Explorer, clique duas vezes no arquivo Program.cs para exibir o cdigo do programa
na janela Code and Text Editor,
3. Na classe Program, adicione o mtodo calculateFee abaixo do mtodo run. Essa a mesma ver
so do mtodo implementado no conjunto anterior de exerccios, exceto pelo fato de ele aceitar
dois parmetros opcionais com valores padro. O mtodo tambm imprime uma mensagem
que indica a verso chamada do mtodo calculateFee. (Nas etapas a seguir, voc adicionar as
verses sobrecarregadas desse mtodo.)
private double calculateFee(double dailyRate = 500.0, int noOfDays = 1)

{
Console.WriteLineCcalculateFee using two optional parameters");
return dailyRate * noOfDays;

}
4. Adicione outra implementao do mtodo calculateFee classe Program, como demonstrado a
seguir. Essa verso aceita um nico parmetro, chamado dailyRate, do tipo double. O corpo do
mtodo calcula e retorna a taxa de um nico dia.
private double calculateFee(double dailyRate = 500.0)

{
Console.WriteLineCcalculateFee using one optional parameter");
int defaultNoOfDays = 1;
return dailyRate * defaultNoOfDays;

}
5. Adicione uma terceira implementao do mtodo calculateFee classe Program. Essa verso no
aceita parmetros e utiliza os valores codificados para a taxa diria e o nmero de dias.
private double calculateFeeO

{
Console.WriteLineCcalculateFee using hardcoded values");
double defaultDailyRate = 400.0;
int defaultNoOfDays = 1;
return defaultDailyRate * defaultNoOfDays;

}
6. No mtodo run, adicione as seguintes instrues, que chamam o calculateFee e exibem os resul
tados:
public void run O

{
double fee = calculateFeeO;
Console.WriteLine("Fee is {0}", fee);

Captulo 3

Escrevendo mtodos e aplicando escopo

101

7. No menu Debug, clique em Start Without Debugging, para construir e executar o programa.
0 programa executado em uma janela do console e exibe as seguintes mensagens:
calculateFee using hardcoded values
Fee is 400

O mtodo run chamou a verso de calculateFee que no aceita parmetros e no as implemen


taes que aceitam parmetros opcionais. Isso acontece porque essa a verso que mais se
aproxima chamada do mtodo.
Pressione qualquer tecla para fechar a janela do console e retornar ao Visual Studio.
8. No mtodo run, modifique a instruo que chama o calculateFee, conforme indicado em negrito
no seguinte exemplo de cdigo:
public void run()

{
double fee = calculateFee(650.0);
Console.WriteLine("Fee is {0}", fee);

}
9. No menu Debug, clique em Start Without Debugging, para construir e executar o programa. O
programa exibe as seguintes mensagens:
calculateFee using one optional parameter
Fee is 650

Dessa vez, o mtodo run chamou a verso de calculateFee que aceita um nico parmetro op
cional. Como anteriormente, isso acontece porque essa a verso que mais se aproxima da
chamada do mtodo.
Pressione qualquer tecla para fechar a janela do console e retornar ao Visual Studio.
10. No mtodo run, modifique novamente a instruo que chama calculateFee
.
public void run()

{
double fee = ca1culateFee(500.0, 3);
Console.WriteLine("Fee is {0}", fee);

}
11. No menu Debug, clique em Start Without Debugging, para construir e executar o programa. O
programa exibe as seguintes mensagens:
calculateFee using two optional parameters
Fee is 1500

Como voc j previa, com base nos dois casos anteriores, o mtodo run chamou a verso de

calculateFee que aceita dois parmetros opcionais.


Pressione qualquer tecla para fechar a janela do console e retornar ao Visual Studio.

102

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

12. No mtodo run, modifique a instruo que chama o calculateFee, e especifique o parmetro
dailyRate pelo nome:
public void run()

{
d o u b l e

f e e

c a l c u l a t e F e e ( d a i l y R a t e

3 7 5 . 0 ) ;

Console.WriteLine("Fee is {0}", fee);

}
13. No menu Debug, clique em Start Without Debugging, para construir e executar o programa.
O programa exibe as seguintes mensagens:
calculateFee using one optional parameter
Fee is 375

Como anteriormente, o mtodo run chamou a verso de calculateFee que aceita um nico par
metro opcional. Mudar o cdigo para utilizar um argumento nomeado no altera o modo como
o compilador resolve a chamada ao mtodo neste exemplo.
Pressione qualquer tecla para fechar a janela do console e retornar ao Visual Studio.
14. No mtodo run, modifique a instruo que chama o calculateFee, e especifique o parmetro
noOfDays pelo nome:
public void run()

{
double fee = calculateFee(noOfDays : 4);
Console.WriteLine("Fee is {0}", fee);

}
15. No menu Debug, clique em Start Without Debugging, para construir e executar o programa. O
programa exibe as seguintes mensagens:
calculateFee using two optional parameters
Fee is 2000

Dessa vez, o mtodo run chamou a verso de calculateFee que aceita dois parmetros opcionais.
A chamada do mtodo omitiu o primeiro parmetro (<
dailyRate) e especificou o segundo par
metro pelo nome. Esta a nica verso do mtodo calculateFee que corresponde chamada.
Pressione qualquer tecla para fechar a janela do console e retornar ao Visual Studio.
16. Modifique a implementao do mtodo calculateFee que aceita dois parmetros opcionais. Mude
o nome do primeiro parmetro para theDailyRate e atualize a instruo retum, como mostrado
em negrito no cdigo a seguir:
private double calculateFee(double theDailyRate = 500.0, int noOfDays = 5)

{
Console.WriteLine("calculateFee using two optional parameters");
return theDailyRate * noOfDays;

Captulo 3

Escrevendo mtodos e aplicando escopo

103

17. No mtodo run, modifique a instruo que chama o calculateFee, e especifique o parmetro
cheDailyRate pelo nome:
p u b lic void runO

{
double fee = calculateFee(theDai1yRate : 375.0);
C o n so le .W rite Lin e ("Fe e i s { 0 } " , f e e );

}
18. No menu Debug, clique em Start W ithout Debugging, para construir e executar o programa. O
programa exibe as seguintes mensagens:
c a lc u la te F e e using two option al parameters
Fee is 1875

Quando voc especificou a taxa, mas no a taxa diria (etapa 13), o mtodo run chamou a
verso de calculateFee que aceita um nico parmetro opcional. Dessa vez, o mtodo run cha
mou a verso de calculateFee que aceita dois parmetros opcionais. Nesse caso, o uso de um
argumento nomeado mudou o modo como o compilador resolve a chamada do mtodo. Se voc
especificar um argumento nomeado, o compilador vai comparar o nome do argumento com os
nomes dos parmetros especificados nas declaraes de mtodos e selecionar o mtodo que
possui um parmetro com um nome correspondente. Pressione qualquer tecla para fechar a
janela do console e retornar ao Visual Studio.
Neste captulo, voc aprendeu a definir mtodos para implementar um bloco de cdigo nomeado.
Voc examinou como passar parmetros para os mtodos e como retornar dados dos mtodos. Voc
tambm viu como chamar um mtodo, passar argumentos e obter um valor de retorno. Voc apren
deu a definir mtodos sobrecarregados com diferentes listas de parmetros e constatou que o escopo
de uma varivel determina onde ela pode ser acessada. Depois, voc utilizou o depurador do Visual

Studio 2010 para passar pelo cdigo ao longo de sua execuo. Finalmente, voc aprendeu a escrever
mtodos que aceitam parmetros opcionais e a chamar mtodos por meio de parmetros nomeados.
Se voc quiser seguir para o prximo captulo agora:
Mantenha o Visual Studio 2010 em execuo e v para o Captulo 4.
Se quiser sair do Visual Studio 2010:
No menu File, clique em Exit. Se vir uma caixa de dilogo Save, clique em Yes e salve o projeto.

104

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Referncia rpida do Captulo 3


Para
Declarar um mtodo

Faa isto
Escreva o mtodo dentro de uma classe. Por exemplo:
in t addValues(int leftHandSide, int rightHandSide)
{
}

Retornar um valor de dentro de um mtodo

Escreva uma instruo return dentro do mtodo. Por exemplo:


return leftHandSide + rightHandSide;

Retornar de um mtodo antes do seu final

Escreva uma instruo return dentro do mtodo. Por exemplo:


return;

Chamar um mtodo

Escreva o nome do mtodo junto com os argumentos entre parnte


ses. Por exemplo:
addValues(39, 3);

Utilizar o assistente Generate Method Stub

D um clique com o boto direito em uma chamada para o mtodo


e ento clique em Generate Method Stub no menu de atalho.

Exibir a barra de ferramentas Debug

No menu View, aponte para Toolbars e clique em Debug.

Entrar em um mtodo

Na barra de ferramentas Debug, clique em Step Into. Ou no menu


Debug, clique em Step Into.

Sair de um mtodo

Na barra de ferramentas Debug, clique em Step Out. Ou no menu


Debug, clique em Step Out.

Especificar um parmetro opcional para um


mtodo

Fornea um valor padro para o parmetro na declarao do mto


do. Por exemplo:
void optMethod(int f ir s t , double second = 0.0),
string third = "Hello")
{
}

Passar um argumento do mtodo como um


parmetro nomeado

Especifique o nome do parmetro na chamada do mtodo. Por


exemplo:
optMethod(first : 100, third : "World");

Captulo 4

Utilizando instrues de deciso


Neste captulo, voc vai aprender a:
Declarar variveis booleanas.
Utilizar os operadores booleanos para criar expresses cujo resultado verdadeiro
ou falso.
Escrever instrues if para tomar decises baseadas no resultado de uma expresso
booleana.
Escrever instrues switch para tomar decises mais complexas.

No Captulo 3, Escrevendo mtodos e aplicando escopo , voc aprendeu a agrupar as instrues


relacionadas em mtodos. Tambm aprendeu a utilizar parmetros para passar informaes para um
mtodo e a utilizar as instrues retum para passar informaes a partir de um mtodo. Dividir um
programa em um conjunto de mtodos distintos, cada um deles projetado para executar uma tarefa
ou clculo especfico, uma estratgia necessria. Muitos programas precisam resolver problemas
grandes e complexos. A diviso de um programa em mtodos ajuda a entender esses problemas e a
focar a soluo de uma parte a cada vez. Voc pode precisar escrever mtodos que executem diferen
tes aes, dependendo das circunstncias. Neste captulo, voc ver como realizar essa tarefa.

Declarando variveis booleanas


No mundo da programao C#, tudo preto ou branco, certo ou errado, verdadeiro ou falso. Por
exemplo, se voc criar uma varivel inteira chamada x, atribuir o valor 99 a x e ento perguntar,
A varivel x contm o valor 99? , a resposta ser true. Se voc perguntar x menor que 10? , a
resposta serfalse. Esses so exemplos de expresses booleanas. Uma expresso booleana sempre
avaliada como verdadeira ou falsa.

106

Parte I

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

0 Microsoft Visual C# fornece um tipo de dados chamado bool. Uma varivel bool pode armazenar
um dos dois valores: true oufa lse . Por exemplo, as trs instrues a seguir declaram uma varivel
bool chamada areYouReady, atribuem o valor true a essa varivel e ento escrevem seu valor no
console:
bool areYouReady;
areYouReady = tru e ;
Consol e . W riteLine(areYouReady) ; // escreve True no console

Utilizando operadores booleanos


Um operador booleano um operador que faz um clculo cujo resultado verdadeiro ou falso. O
C# tem vrios operadores booleanos muito teis, sendo o mais simples deles o operador NOT, que
representado pelo ponto de exclamao (!). O operador! nega um valor booleano, resultando em
valor oposto a esse. No exemplo anterior, se o valor da varivel areYouReady fosse true, o valor da
expresso '.areYouReady seriafalse.

Entendendo operadores de igualdade e relacionais


Dois operadores booleanos utilizados com frequncia so os operadores de igualdade (==) e desi
gualdade (!=). Esses operadores binrios so utilizados para descobrir se um valor igual a outro
valor de mesmo tipo. A tabela a seguir resume como esses operadores funcionam, utilizando uma
varivel in t chamada age como exemplo.

Operador

Significado

Exemplo

0 resultado se age for 42

==

Igual a

age = =100

falso

!=

Diferente de

age I = 0

verdadeiro

Intimamente ligados a esses dois operadores esto os operadores relacionais. Voc utiliza esses ope
radores para descobrir se um valor menor ou maior que outro do mesmo tipo. A tabela a seguir
mostra como utilizar esses operadores.

Operador

Significado

Exemplo

O resultado se age for 42

<

Menor que

age < 21

falso

<=

Menor ou igual a

age <=18

falso

>

Maior que

age > 16

verdadeiro

>=

Maior ou igual a

age >=30

verdadeiro

No confunda o operador de igualdade = = com o operador de atribuio =. A expresso x==y com


para x comy e tem o valor true se os valores forem idnticos. A expresso x=y atribui o valor de y a
x e retorna o valor de y como resultado.

Captulo 4

Utilizando instrues de deciso

107

Entendendo operadores lgicos condicionais


0 C# tambm fornece dois outros operadores booleanos: o operador lgico AND, que representado
pelo smbolo &&, e o operador lgico OR, que representado pelo smbolo 11. Coletivamente, eles
so conhecidos como os operadores lgicos condicionais. O propsito dessa expresso combinar
duas expresses ou valores booleanos em um nico resultado booleano. Esses operadores binrios
so semelhantes aos operadores relacionais e de igualdade pelo fato de que o valor das expresses
em que eles aparecem verdadeiro ou falso, mas diferem pelo fato de que os valores em que eles
operam devem ser verdadeiros ou falsos.
O resultado do operador && ser true se e somente se as duas expresses booleanas em que ele ope
ra forem true. Por exemplo, a instruo a seguir atribuir o valor true a validPercentage se e somente
se o valor de percent for maior ou igual a 0 e o valor de percent for menor ou igual a 100:
bool va lid Pe rce n ta g e ;
va lid Pe rce n ta g e = (p ercent >= 0) && (percent <= 100);

Dica Um erro comum dos iniciantes tentar combinar os dois testes nomeando a varivel percent
somente uma vez, como abaixo:
percent >= 0 && <= 100 // essa in s tru o no com pilar

O uso de parnteses ajuda a evitar esse tipo de erro e tambm esclarece o objetivo da expresso. Por
exemplo, compare estas duas expresses:
valid Pe rce n ta g e = percent >= 0 && percent <= 100

e
va lid Pe rce n ta g e = (p ercen t >= 0) && (p ercen t <= 100)

Ambas as expresses retornam o mesmo valor, porque a precedncia do operador && menor que
a precedncia dos operadores >= e <=. Mas a segunda expresso passa seu sentido de maneira
mais legvel.

O resultado do operador | | ser true se pelo menos uma das expresses booleanas em que ele opera
for true. O operador 11 utilizado para determinar se uma expresso de uma combinao de expres
ses booleanas true. Por exemplo, a instruo a seguir atribuir o valor true a invalidPercentage se
o valor de percent for menor que 0 ou se o valor de percent for maior que 100:
bool in va lid P e rc e n ta g e ;
in v alid P e rce n tag e = (percen t < 0) || (percen t > 100);

108

Parte I

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Curto-circuito
Os operadores & & e 11 exibem um recurso chamado curto-circuito. s vezes, no necessrio avaliar
os dois operandos ao determinar o resultado de uma expresso lgica condicional. Por exemplo, se o
operando esquerdo do operador && for avaliado comofa ls e , ento o resultado da expresso inteira
deve serfalse, independentemente do valor do operando direito. De maneira semelhante, se o valor
do operando esquerdo do operador | | for avaliado como true, o resultado da expresso inteira de
ver ser true, independentemente do valor do operando direito. Nesses casos, os operadores && e
11 pulam a avaliao do operando direito. Eis alguns exemplos:
(percent >= 0) && (percent <= 100)

Nessa expresso, se o valor de percent for menor que 0, a expresso booleana do lado esquerdo de
&& ser avaliada comofalse. Esse valor significa que o resultado de toda a expresso deve serfalse,
e a expresso booleana direita do operador && no avaliada.
(percen t < 0) || (percen t > 100)

Nessa expresso, se o valor de percent for menor que 0, a expresso booleana no lado esquerdo de
| | ser avaliada como true. Esse valor significa que o resultado da expresso inteira deve ser true e
a expresso booleana direita do operador | | no avaliada.
Se projetar cuidadosamente as expresses que usam os operadores lgicos condicionais, voc po
der aumentar o desempenho do seu cdigo evitando trabalho desnecessrio. Coloque expresses
booleanas simples que possam ser avaliadas facilmente no lado esquerdo de um operador lgico
condicional e as expresses mais complexas no lado direito. Em muitos casos, voc perceber que o
programa no precisar avaliar as expresses mais complexas.

Resumindo a precedncia e a associatividade dos operadores


A tabela a seguir resume a precedncia e a associatividade de todos os operadores sobre os quais
voc aprendeu at aqui. Os operadores da mesma categoria tm a mesma precedncia. Os operadores
nas primeiras categorias da tabela tm precedncia sobre os operadores nas ltimas categorias.

Categoria

Operadores

Descrio

Associatividade

Primrio

Substitui precedncia

Esquerda

++

Sufixo de incremento

--

Sufixo de decremento

Unrio

NOT lgico

Adio

Subtrao

++

Prefixo de incremento

Prefixo de decremento

Esquerda

Captulo 4

Utilizando instrues de deciso

109

Categoria

Operadores

Descrio

Associatividade

Multiplicativo

Multiplicao

Esquerda

Diviso

Resto da diviso

Adio

Subtrao

<

Menor que

<=

Menor ou igual a

>

Maior que

>=

Maior ou igual a

==

Igual a

j=

Diferente de

AND condicional

&&

AND lgico

Esquerda

OR condicional

II

OR lgico

Esquerda

Atribuio

Aditivo

Relacional

Igualdade

Esquerda

Esquerda

Esquerda

Direita

Utilizando instrues if para tomar decises


Se voc quiser escolher entre executar dois blocos diferentes de cdigo com base no resultado de uma
expresso booleana, utilize uma instruo if.

Entendendo a sintaxe da instruo if


A sintaxe de uma instruo if a seguinte (ifs else so palavras-chave do C#):
i f ( expressoBoolena )
instruo-1;
el se
i nstruo-2;

Se a expressoBoolena for avaliada como true, a instruo-1 executada; caso contrrio, a instruo-2 executada. A palavra-chave else e a instruo-2 subsequente so opcionais. Se no houver
uma clusula else e a expressoBoolena forfalse, a execuo continua com o cdigo que vem depois
da instruo if.
Por exemplo, eis uma instruo if que incrementa uma varivel representando o ponteiro de segun
dos de um cronmetro. (Os minutos so ignorados por enquanto.) Se o valor da varivel seconds for
59, ela ser redefinida para 0, caso contrrio, ser incrementada utilizando o operador + + :
in t seconds;
i f (seconds == 59)
seconds = 0;
el se
seconds++;

110

Parte I Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Somente expresses booleanas, por favor!


A expresso em uma instruo if deve estar entre parnteses. Alm disso, ela deve ser uma
expresso booleana. Em algumas outras linguagens (principalmente C e C++), voc pode
escrever uma expresso do tipo inteiro, e o compilador discretamente converter o valor
inteiro em true (no zero) ou false (0). O C# no suporta esse tipo de comportamento, e o
compilador reporta um erro se uma expresso desse tipo for escrita.
Se voc especificar acidentalmente o operador de atribuio, =, em vez do operador de teste
de igualdade, ==, em uma instruo if, o compilador C# perceber seu erro e no ir com
pilar seu cdigo. Por exemplo:
in t seconds;
i f (seconds = 59) // erro de tempo de compilao
i f (seconds == 59) // ok

As atribuies acidentais so outra fonte de erros comum em programas C e C+ +, que con


vertem discretamente o valor atribudo (59) a uma expresso booleana (tudo diferente de
zero considerado verdadeiro). O resultado que o cdigo aps a instruo if executado
todas as vezes.
Ocasionalmente, uma varivel booleana pode ser utilizada como a expresso para uma ins
truo if, embora ela ainda deva ser includa entre parnteses, como mostrado neste exem
plo:
bool inWord;
i f (inWord == tr u e ) // ok, mas no muito usado
i f (inW ord) // mais comum e considerado um e s t il o melhor

Utilizando blocos para agrupar instrues


Observe que a sintaxe da instruo ^mostrada anteriormente especifica uma nica instruo depois
do if (expressoBoolena) e uma nica instruo depois da palavra-chave else. s vezes voc vai que
rer realizar mais de uma instruo quando uma expresso booleana for verdadeira. As instrues
podero ser agrupadas dentro de um novo mtodo e ento chamar o novo mtodo, mas uma soluo
mais simples agrupar as instrues dentro de um bloco. Um bloco simplesmente uma sequncia
de instrues agrupadas entre uma chave de abertura e uma de fechamento. Um bloco tambm inicia
um novo escopo. As variveis podem ser definidas dentro de um bloco, mas elas desaparecero no
final do bloco.

Captulo 4

Utilizando instrues de deciso

111

No exemplo a seguir, duas instrues que redefinem a varivel seconds como 0 e incrementam a
varivel minutes esto agrupadas em um bloco, e o bloco inteiro executado se o valor de seconds
for igual a 59:
in t seconds = 0;
in t minutes = 0;
i f (seconds == 59)

{
seconds = 0;
minutes++;

}
e lse
seconds++;

Importante Se as chaves forem omitidas, o compilador do C# associar apenas a primeira ins


truo (seconds = 0) instruo if. A instruo subsequente (minutes++;) no ser reconhecida
pelo compilador como parte da instruo if quando o programa for compilado. Alm disso, quando
o compilador alcanar a palavra-chave e/se, ele no a associar instruo //anterior e informar
um erro de sintaxe.

Instrues if em cascata
Voc pode aninhar instrues if dentro de outras instrues if. Assim, pode encadear uma sequncia
de expresses booleanas, que so testadas uma aps a outra at que uma delas seja avaliada como
true. No exemplo a seguir, se o valor de day for 0, o primeiro teste ser avaliado como true; e dayName receber a string Sunday. Se o valor de day no for 0, o primeiro teste falhar, e o controle
passar para a clusula else, que executa a segunda instruo ife compara o valor de day com 1. A
segunda instruo if alcanada somente se o primeiro teste for false. Da mesma forma, a terceira
instruo if s ser avaliada se o primeiro e o segundo teste foremfalse.
i f (day == 0)
dayName = "Su nd ay";
e ls e i f (day == 1)
dayName = "Monday";
e ls e i f (day == 2)
dayName = "Tuesday ;
e ls e i f (day 3)
dayName = "Wednesday";
e ls e i f (day == 4)
dayName = "Thursday";
e ls e i f (day == 5)
dayName = "F r id a y ";
e ls e i f (day == 6)
dayName = "S a tu rd a y ";
e lse
dayName = "unknown";

112

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

No exerccio a seguir, voc escrever um mtodo que utiliza uma instruo ifzm cascata para com
parar duas datas.

Escreva instrues if
1. Inicie o Microsoft Visual Studio 2010 se ele ainda no estiver em execuo.
2. Abra o projeto Selection, localizado na pasta \Microsoft PressWisual CSharp Step by Step\Chapter4\Selection na sua pasta Documentos.
3. No menu Debug, clique em Start W ithout Debugging.
O Visual Studio 2010 compila e executa o aplicativo. O formulrio contm dois controles Date
TimePicker chamadosfirs t e second (esses controles exibem um calendrio permitindo que voc
selecione uma data ao clicar no cone). Esses dois controles so inicialmente configurados com
a data atual.
4. Clique em Compare.
O texto a seguir exibido na caixa de texto:
fir s t
fir s t
fir s t
fir s t
fir s t
fir s t

== second : False
1= second : True
< second : False
<= second : False
> second : True
>= second : True

A expresso booleana f i r s t == second deve ser true porque tanto/?r5 quanto second esto
configurados como a data atual. De fato, somente o operador menor que e o operador maior
que ou igual a parecem funcionar corretamente.

19/08/2009

El

19/08/2009

Com pare

first == second : False


first != second :T ru e
first < second : False
first <= second : False
first > second : True
first >= second : True

5. Clique em Quit para retornar ao ambiente de programao do Visual Studio 2010.


6. Exiba o cdigo de MainWindow.xaml.es na janela Code and Text Editor.

Captulo 4

Utilizando instrues de deciso

113

7. Localize o mtodo compareClick, que semelhante a este:


p riv a te void com pareC lick(object sender, RoutedEventArgs e)

{
in t d i f f = d ate C o m p a re (first. Selected D ate.V a iu e, sec o n d .S e le c te d D a te .V a lu e );
in fo .T e x t =
s h o w ("fir s t == second", d i f f == 0 );
s h o w ("fir s t != second , d i f f != 0 );
s h o w ("fir s t < second", d i f f < 0 );
s h o w ("fir s t <= second", d i f f <= 0 );
s h o w ("fir s t > second", d i f f > 0 );
s h o w ("fir s t >= second", d i f f >= 0 );

}
Esse mtodo executado sempre que o usurio clica no boto Compare do formulrio. Ele recu
pera os valores de datas exibidos nos controles D ateTim ePickerJirst e second no formulrio. A
data selecionada pelo usurio em cada controle DateTimePicker fica disponvel na propriedade
SelectedDate. Para recuperar a data, use a propriedade Value dessa propriedade. (Voc conhecer
mais detalhes sobre propriedades no Captulo 15, Implementando propriedades para acessar
campos.) O tipo dessa propriedade DateTime. O tipo de dadoDateTime apenas mais um tipo
de dado, como in t ouJlo a t, exceto pelo fato de que contm subelementos que permitem acessar
as partes individuais de uma data, como ano, ms ou dia.
O mtodo compareClick passa os dois valores de DateTime para o mtodo dateCompare, que os
compara. Examinaremos o mtodo dateCompare no prximo passo.
O mtodo show resume os resultados da comparao no controle de caixa de texto info do for
mulrio.
8. Localize o mtodo dateCompare, que se parece com este:
p riv a te in t dateCompare(DateTime leftH a nd Sid e , DateTime rightHandSide)

{
// T0 DO
return 42;

}
Esse mtodo atualmente retorna o mesmo valor sempre que chamado, em vez de 0, -1 ou
+ 1, dependendo dos valores de seus parmetros. Isso explica por que o aplicativo no funciona
conforme o esperado!
O propsito desse mtodo examinar os argumentos e retornar um valor inteiro com base nos
valores relativos; ele deve retornar 0 se os argumentos tiverem o mesmo valor, -1 se o valor do
primeiro argumento for menor que o valor do segundo argumento e +1 se o valor do primeiro
argumento for maior que o valor do segundo argumento (uma data considerada maior que
outra se vier antes dela cronologicamente). Voc precisa implementar a lgica nesse mtodo
para comparar duas datas corretamente.
9. Remova o comentrio // TO DO e a instruo return do mtodo dateCompare.*

* N. d e R .T : O c o m e n t rio TO DO", q u e s ig n if ic a A FAZER , re c o n h e c id o a u to m a ti c a m e n te p e lo V is u a l S tu d io .

114

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

10. Adicione as seguintes instrues mostradas em negrito ao corpo do mtodo dateCompare-,


p riv a te in t dateCompare(DateTime leftH a n d Sid e, DateTime rightHandSide)

{
in t r e s u lt;
i f (le ftH a n d S id e .Y e a r < rightH andSide.Year)
r e s u lt = - lj
e ls e i f (le ftH a n d S id e .Y e a r > righ tH andSide.Year)
r e s u lt = 1;

}
Se a expresso leftH and Sid e .Year < rightHandSide. Year ortrue, a data em leftHandSide
deve ser anterior data em rightHandSide, portanto, o programa configura a varivel result
como-1. Se a expresso le ftH a n d S id e .Y e a r > rig h tH and Sid e.Y ear for true, a data em
leftHandSide deve ser posterior data em rightHandSide, e o programa configura a varivel
result como 1.
Se a expresso le ftH a n d S id e .Y e a r < rig h tH a n d S id e .Y e a r forJa ls e , e a expresso
leftH an d Sid e .Y ear > r i ghtHandSi de. Year tambm o ijalse, a propriedade Year das duas
datas deve ser a mesma, portanto, o programa precisa comparar os meses em cada data.
11. Adicione as instrues a seguir mostradas em negrito ao corpo do mtodo dateCompare, depois
do cdigo que voc inseriu no passo anterior:
p riv a te in t dateCompare(DateTime leftH a nd Sid e, DateTime rightHandSide)

e ls e i f (leftH andSide.M onth < rightHandSide.Month)


r e s u lt = -1;
e ls e i f (leftH andSide.M onth > rightHandSide.Month)
r e s u lt = 1;

}
Essas instrues seguem uma lgica semelhante para comparar meses quela utilizada para
comparar anos no passo anterior.
Se a expresso le ftH a n d S id e . Month < r i ghtHandSi de. Month forJa ls e , e a expresso
leftH an d Sid e. Month > ri ghtHandSi de. Month tambm o jlse, a propriedade Month das
duas datas deve ser a mesma, assim o programa acaba precisando comparar o dia em cada
data.
12. Adicione as seguintes instrues ao corpo do mtodo dateCompare depois do cdigo que voc
inseriu nos dois passos anteriores:
p riv a te in t dateCompare(DateTime leftH a n d Sid e, DateTime rightHandSide)

{
e ls e i f (leftH an d Sid e.D ay < rightH andSide.D ay)
r e s u lt = -1;

Captulo 4

Utilizando instrues de deciso

115

else if OeftHandSide.Day > rightHandSide.Day)


result = 1;
el se
result = 0;
return result;

}
Voc j deve reconhecer o padro nessa lgica.
SeleftH andSide.D ay < rightH andSid e .Day eleftH and Side.D ay > rightH andSid e .Day
foremfalse, o valor nas propriedades D ay nas duas variveis deve ser o mesmo. Os valores
Month e os valores Year tambm devem ser idnticos para que a lgica do programa chegue
at esse ponto; portanto, as duas datas devem ser iguais, e o programa configura o valor de
result como 0.
A ltima instruo retorna o valor armazenado na varivel result.
13. No menu Debug, clique em Start W ithout Debugging.
O aplicativo recompilado e reiniciado. Mais uma vez, os dois controles D ateTim ePickerfirst e
second, so definidos com a data de hoje.
14. Clique em Compare.
O texto a seguir exibido na caixa de texto:
fir s t
fir s t
fir s t
fir s t
fir s t
fir s t

== second : True
!= second : False
< second : False
<= second : True
> second : False
>= second : True

Esses so os resultados corretos para datas idnticas.


15. Clique no cone para o segundo controle DateTimePicker e clique na data de amanh no calen
drio exibido.
16. Clique em Compare.
O texto a seguir exibido na caixa de texto:
f i rs t
f i rs t
f i rs t
f i rs t
f i rs t
fir s t

== second : False
= second : True
< second : True
<= second : True
> second : False
>- second : False

Mais uma vez, esses so os resultados corretos quando a primeira data anterior segunda
data.
17. Teste algumas outras datas e verifique se os resultados so os esperados. Clique em Quit depois
de terminar.

116

Parte I

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Comparando datas em aplicativos do mundo real


Agora que vimos como utilizar uma srie um tanto longa e complicada de instrues if e
else, devo mencionar que essa no a tcnica que voc utilizaria para comparar datas em
um aplicativo real. Na biblioteca de classes do Microsoft NET Framework, as datas so ar
mazenadas utilizando um tipo especial chamado DateTime. Se examinar o mtodo dateCompare que voc escreveu no exerccio anterior, ver que os dois parmetros, leftHandSide
e rightHandSide, so valores DateTime. A lgica escrita s compara a parte da data dessas
variveis - tambm h um elemento hora. Para que dois valores DateTime sejam considera
dos iguais, eles no apenas devem ter a mesma data, mas tambm a mesma hora. Comparar
datas e horas uma operao to comum que o tipo DateTime tem um mtodo predefinido
chamado Compare para fazer exatamente isso. O mtodo Compare recebe dois argumentos
DateTime e os compara, retornando um valor que indica se o primeiro argumento menor
que o segundo, caso em que o resultado ser negativo; se o primeiro argumento maior que
o segundo, caso em que o resultado ser positivo; ou se os dois argumentos representam a
mesma data e hora, caso em que o resultado ser 0.

Utilizando instrues switch


Algumas vezes, ao escrever uma instruo j/em cascata, todas as instrues if aparecem iguais, por
que todas avaliam uma expresso idntica. A nica diferena que cada compara o resultado da
expresso com um valor diferente. Por exemplo, considere o seguinte bloco de cdigo que utiliza uma
instruo^para examinar o valor na varivel day e calcular qual o dia da semana:
if

(day == 0)
dayName = "Sunday";
e ls e i f (day == 1)
dayName = "Monday";
e ls e i f (day == 2)
dayName = "Tuesday";
e ls e i f (day == 3)
e ls e
dayName = "Unknown";

Nessas situaes, normalmente possvel reescrever a instruo ife m cascata como uma instruo
switch para tornar o programa mais eficiente e legvel.

Captulo 4

Utilizando instrues de deciso

117

Entendendo a sintaxe da instruo switch


A sintaxe de uma instruo switch a seguinte (sw itch, case e default so palavras-chave):
switch ( expressoDeControle )

{
case expressoConstante :
in stru es
break;
case expressoConstante :
i nstrues
break;
d e fa u lt :
i nstrues
break;

}
A expressoDeControle avaliada uma vez. 0 controle passa ento para o bloco do cdigo identifica
do pela expressoConstante, cujo valor igual ao resultado da expressoDeControle (o identificador
chamado de rtulo de caso). A execuo prossegue at a instruo break e, ento, a instruo switch
termina, e o programa continua a partir da primeira instruo depois da chave de fechamento da
instruo switch. Se nenhum dos valores da expressoConstante for igual ao valor da expressoDe
Controle, as instrues abaixo do rtulo default opcional so executadas.

Por exemplo, voc pode reescrever a instruo ife m cascata anterior como a instruo switch a se
guir:
switch (day)

{
case 0 :
dayName = "Sunday";
break;
case 1 :
dayName = "Monday";
break;
case 2 :
dayName = "Tuesday";
break;
d e fa u lt :
dayName = "Unknown";
break;

118

Parte I Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Seguindo as regras da instruo switch


A instruo switch muito til, mas, infelizmente, nem sempre voc poder utiliz-la da maneira
desejada. Todas as instrues switch que voc escrever devem obedecer s seguintes regras:
A instruo switch s pode ser utilizada em tipos de dados primitivos, como in t ou strng. Com
qualquer outro tipo (incluindoflo a t e double), voc ter de utilizar uma instruo if.
Os rtulos de caso devem ser expresses constantes, como 42 ou 42". Se for necessrio calcu
lar valores dos rtulos de caso em tempo de execuo, utilize uma instruo if.
Os rtulos de caso devem ser expresses nicas. Ou seja, dois rtulos de caso no podem ter o
mesmo valor.
Voc pode especificar que deseja executar as mesmas instrues para mais de um valor forne
cendo uma lista de rtulos de caso sem nenhuma instruo no meio, caso em que o cdigo para
o rtulo final na lista executado para todas as instrues case nessa lista. Mas se um rtulo
tiver uma ou mais instrues associadas, a execuo no poder prosseguir (fall-through) para
os rtulos subsequentes, e o compilador gerar um erro. Por exemplo:
sw itch (trumps)

{
case Hearts :
case Diamonds :
// Fall-through perm itido - nenhum cdigo en tre rtulos
c o lo r = "Red";
// Cdigo executado para Hearts e Diamonds
break;
case Clubs :
c o lo r = "B la c k ";
case Spades :
// Erro - cdigo entre rtulos
c o lo r = " B la c k " ;
break;

No prximo exerccio, voc completar um programa que l os caracteres de uma string e mapeia
cada caractere para sua representao XML. Por exemplo, o caractere de sinal de menor, < tem um
significado especial em XML. (Ele utilizado para formar elementos.) Se houver dados que conte
nham esse caractere, eles devero ser convertidos no texto "&11 ;" de modo que um processador
de XM L saiba que so dados e no parte de uma instruo XML. Regras semelhantes se aplicam ao
sinal de maior (>) e aos caracteres e comercial (&), aspa nica ( ') e aspa dupla (''). Voc escrever
uma instruo switch que testa o valor do caractere e captura os caracteres XM L especiais como
rtulos case.

Captulo 4

Utilizando instrues de deciso

119

Regras de fall-through da instruo switch


Como voc no pode passar acidentalmente de um rtulo de caso para outro, se houver
algum cdigo no meio, voc pode reorganizar livremente as sees de uma instruo switch
sem afetar o significado (incluindo o rtulo default que, por conveno, normalmente po
sicionado como o ltimo rtulo, mas no obrigatrio).
Os programadores C e C++ devem notar que a instruo break obrigatria para cada case
em uma instruo switch (mesmo o case padro). H uma razo para isso: comum em pro
gramas C ou C++ instruo break ser esquecida, permitindo que a execuo prossiga (fali
through) para o prximo rtulo, originando erros que so difceis de descobrir.
Se voc quiser, pode simular o fall-through do C/C++ no C# usando a instruo goto para ir
para a instruo case seguinte ou para o rtulo default. Mas, em geral, o uso de goto no
recomendvel, e este livro no demonstra como faz-lo!

Escreva instrues sw itch


1. Inicialize o Visual Studio 2010 se ainda no estiver em execuo.
2. Abra a pasta SwitchStatement project, localize a pasta \Microsoft PressWisual Step by CSharp
Step by Step\Chapter 4\SwitchStatement na sua pasta Documentos.
3. No menu Debug, clique em S tart W ithout Debugging.
0 Visual Studio 2010 compila e executa o aplicativo, que exibe um formulrio contendo duas
caixas de texto separadas por um boto Copy.

120

Parte I Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

4. Digite o seguinte texto de exemplo na caixa de texto superior.


inRange = (lo <= number) && (hi >= number);

5. Clique em Copy.
A instruo copiada ipsis litteris para a caixa de texto inferior, e no ocorre qualquer traduo
dos caracteres <, & ou >.
6. Feche o formulrio e retorne ao Visual Studio 2010.
7. Exiba o cdigo para MainWindow.xaml.es na janela Code and Text Editor e localize o mtodo
copyOne.
O mtodo copyOne copia o caractere especificado como o parmetro de entrada para o final do
texto exibido na caixa de texto inferior. No momento, copyOne contm uma instruo switch
com uma nica ao default. Nos prximos passos, voc modificar essa instruo switch para
converter os caracteres que so significativos em XML para seu mapeamento XML. Por exemplo,
o caractere < ser convertido na string "&11 ;
8. Adicione as seguintes instrues instruo switch depois da chave de abertura da instruo e
imediatamente antes do rtulo default-.
case '<' :
ta rg e t.T e x t += "& 1 t ;";
break;

Se o caractere que est sendo copiado for um <, esse cdigo acrescentar a string "&11 ;" ao
texto que est sendo gerado no lugar dele.
9. Adicione as seguintes instrues instruo switch depois da instruo break que voc recm
adicionou, acima do rtulo default-,
case >' :
ta rg e t.T e x t
break;
case
;
ta rg e t.T e x t
break;
case
:
ta rg e t.T e x t
break;
case \ " :
ta rg e t.T e x t
break;

+= "& g t ;";

+= "&amp;";

+=

"&#34;";

+= "&#39;";

Captulo 4

Utilizando instrues de deciso

121

10. No menu Debug, clique em Start W ithout Debugging.


11. Digite o texto a seguir na caixa de texto superior.
inRange = ( lo <= number) && (h i >= number);

12. Clique em Copy.


A instruo copiada na caixa de texto inferior. Desta vez, cada caractere submete-se ao ma
peamento XML implementado na instruo switch. A caixa de texto de destino exibe o seguinte
texto:
inRange = ( l o & lt ; = number) &amp; &amp; (h i &gt; = number)

13. Teste outras strings e verifique se todos os caracteres especiais (<, >, &, e ) so tratados
corretamente.
14. Feche o formulrio.
Neste captulo, voc conheceu expresses booleanas e variveis; aprendeu a usar expresses booleanas com instrues if t switch para tomar decises em seus programas, e combinou expresses
booleanas por meio de operadores booleanos.
Se voc quiser seguir para o prximo captulo agora:
Mantenha o Visual Studio 2010 em execuo e v para o Captulo 5.
Se quiser sair do Visual Studio 2010:
No menu File, clique em Exit. Se vir uma caixa de dilogo Sctve, clique em Yes e salve o projeto

122

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Referncia rpida do Captulo 4


Para

Faa isto

Exemplo

Determinar se dois valores so


equivalentes

Utilize o operador == ou!=.

answer == 42

Comparar o valor de duas expresses

Utilize o operador <, <=, > ou


>=.

age >=21

Declarar uma varivel booleana

Utilize a palavra-chave bool como o


tipo da varivel.

bool inRange;

Criar uma expresso booleana que seja


verdadeira apenas se as duas outras
condies forem verdadeiras

Utilize o operador &&.

inRange = (lo <= number)


&& (number <= h i);

Criar uma expresso booleana que seja


verdadeira se uma das duas outras
condies for verdadeira

Utilize o operador | |.

outOfRange = (number < lo)


11 (hi < number);

Executar uma instruo se uma condio


for verdadeira

Use uma instruo if.

i f (inRange)
processO ;

Executar mais de uma instruo se uma


condio for verdadeira

Utilize uma instruo if e um bloco.

i f (seconds == 59)
{
seconds = 0;
minutes++;
}

Associar diferentes instrues a diferentes


valores de uma expresso de controle

Use uma instruo switch.

switch (current)
{
case 0:
break;
case 1:
break;
default ;
break;
}

Captulo 5

Utilizando atribuio composta


e instrues de iterao
Neste captulo, voc vai aprender a:
Atualizar o valor de uma varivel utilizando os operadores de atribuio composta.
Escrever as instrues de iterao (ou repetio) w hile, fo r e d o.
Inspecionar uma instruo do passo a passo e ver como os valores de variveis mudam.

No Captulo 4, Utilizando instrues de deciso", voc aprendeu a usar as construes if t switch


para executar as instrues seletivamente. Neste captulo, voc ver como utilizar vrias instrues
de iterao (loop) para executar uma ou mais instrues repetidamente. Ao escrever as instrues
de iterao, normalmente voc precisa controlar o nmero de iteraes que sero executadas. Isso
feito por meio de uma varivel que atualiza seu valor a cada iterao e para o processo quando a
varivel atinge um valor especfico. Voc aprender tambm sobre operadores de atribuio especiais
que devem ser utilizados para atualizar o valor de uma varivel nessas circunstncias.

Utilizando operadores de atribuio composta


Voc j sabe como usar os operadores matemticos para criar novos valores. Por exemplo, a instru
o a seguir utiliza o operador (+) para exibir no console um valor que 42 unidades maior que a
varivel answer.
Console.W riteLine(answ er + 42);

Voc tambm aprendeu como usar as instrues de atribuio para alterar o valor de uma varivel.
A instruo a seguir usa o operador de atribuio para alterar o valor da varivel answer para 42:
answer = 42;

Se voc quiser adicionar 42 ao valor de uma varivel, pode combinar o operador de atribuio com
o operador de adio. Por exemplo, a instruo a seguir adiciona 42 varivel answer. Depois da
execuo dessa instruo, o valor de answer ser 42 unidades maior que o valor anterior:
answer = answer + 42;

Embora essa instruo funcione, voc provavelmente nunca ver um programador experiente escre
ver um cdigo assim. Adicionar um valor a uma varivel to comum que o C# permite executar

124

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

essa tarefa de maneira mais rpida utilizando o operador + = . Para adicionar 42 a answer, escreva
esta instruo:
answer += 42;

Utilize esse atalho para combinar qualquer operador aritmtico com o operador de atribuio,
como mostra a tabela a seguir. Esses operadores so conhecidos como operadores de atribuio
composta.

No escreva isto

Escreva isto

varivel = varivel * nmero;

varivel *= nmero;

varivel = varivel / nmero;

varivel/= nmero;

varivel = varivel % nmero;

varivel %= nmero;

varivel = varivel + nmero;

varivel += nmero;

varivel = varivel - nmero;

varivel -= nmero;

Dica Os operadores de atribuio composta compartilham a mesma precedncia e associatividade


direita que os operadores de atribuio simples.

O operador += tambm funciona em strings; ele anexa uma string ao final de outra. Por exemplo, o
cdigo a seguir exibe Hello John no console:
s trin g name = "Jo h n ";
s trin g greetin g = "H e llo " ;
g re etin g += name;
C o n s o le .W rite L in e (g re e tin g );

Voc no pode utilizar outro operador de atribuio composta em strings.

Escrevendo instrues while


Voc utiliza uma instruo while para executar uma instruo repetidamente enquanto alguma con
dio se mantiver verdadeira. A sintaxe de uma instruo while esta:
w h ile ( expressoBooleana )
i nstruo

Captulo 5 Utilizando atribuio composta e instrues de iterao

125

A expresso booleana avaliada e, se for verdadeira, a instruo executada e a expresso booleana


ento avaliada novamente. Se a expresso se mantiver verdadeira, a instruo repetida e ento
a expresso booleana avaliada de novo. Esse processo continua at que a expresso booleana seja
avaliada comofalse, momento em que a instruo while termina. A execuo ento continua com a
primeira instruo depois da instruo while. Uma instruo while compartilha muitas semelhanas
semnticas com uma instruo i f (na verdade, a sintaxe idntica, s muda a palavra-chave):
A expresso deve ser uma expresso booleana.
A expresso booleana deve ser escrita entre parnteses.
Se a expresso booleana for avaliada como falsa na primeira avaliao, a instruo no ser
executada.
Se voc quiser executar duas ou mais instrues sob o controle de uma instruo while, deve
utilizar chaves para agrupar essas instrues em um bloco.
Observe uma instruo while que escreve os valores de 0 a 9 no console:
in t i = 0;
w h ile ( i < 10)

{
C o n s o le .W r it e L in e (i);

i++;
}
Todas as instrues while devem terminar em algum ponto. Um erro comum de iniciantes esquecer
de incluir uma instruo para fazer a expresso booleana ser, por fim, avaliada como false e terminar
o loop, o que resulta em um programa que executado de modo contnuo. No exemplo, a instruo
i++ desempenha esse papel.

No exerccio a seguir, voc escrever um loop while para iterar pelo contedo de um arquivo de texto,
uma linha de cada vez, e escrever cada linha para uma caixa de texto em um formulrio.

Escreva uma instruo while


1. Utilizando o Microsoft Visual Studio 2010, abra o projeto WhileStatement, localizado na pasta
\Microsoft PressWisual CSharp Step by Step\Chapter 5\WhileStatement na sua pasta Documentos.
2. No menu Debug, clique em Start W ithout Debugging.
O Visual Studio 2010 compila e executa o aplicativo. O aplicativo um visualizador simples de
arquivo de texto que voc pode utilizar para selecionar um arquivo de texto e exibir o contedo.

126

Parte I Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

3. Clique em Open File.


A caixa de dilogo Open se abre.
4. Abra a pasta \Microsoft PressWisual CSharp Step By Step\Chapter 5\WhileStatement\ WhileStatement
na sua pasta Documentos.
5. Selecione o arquivo MainWindow.xaml.es e clique em Open.
O nome do arquivo, MainWindow.xaml.es, aparece na caixa de texto pequena, no formulrio,
mas o contedo do arquivo MainWindow.xaml.es no aparece na caixa de texto grande. Isso
ocorre porque voc ainda no implementou o cdigo que l e exibe o contedo do arquivo. Voc
adicionar essa funcionalidade nos prximos passos.
6. Feche o formulrio e retorne ao Visual Studio 2010.
7. Exiba o cdigo para o arquivo MainWindow.xaml.es na janela Code and Text Editor e localize o
mtodo openFileDialogFileOk.
Esse mtodo chamado quando o usurio clica no boto Open aps selecionar um arquivo na
caixa de dilogo Open. O corpo do mtodo est atualmente implementado da seguinte maneira:
p riv a te void o p e n F ile D ia lo g F ile O k (o b je c t sender, System.ComponentModel.
CancelEventArgs e)

{
s trin g fullPathnam e = o p e n F ile D ia lo g . FileName;
F ile ln f o src = new F ile ln f o (f u llP a t h n a m e );
filenam e.Tex t = src.Name;
// ad icio n e o loop w h ile aqui

}
A primeira instruo declara uma varivel string chamadafullPathnam e e a inicializa para a
propriedade FileNam e do objeto openFileDialog. Essa propriedade contm o nome completo (in
cluindo a pasta) do arquivo-fonte selecionado na caixa de dilogo Open.

A segunda instruo declara uma varivel Filelnfo chamada src e a inicializa para um objeto
que representa o arquivo selecionado na caixa de dilogo Open. (Filelnfo uma classe fornecida
pelo Microsoft .NET Framework que pode ser utilizada para manipular arquivos.)
A terceira instruo atribui a propriedade Text do controlefilenam e propriedade Name da va
rivel src. A propriedade Name da varivel src contm o nome do arquivo selecionado na caixa

Captulo 5

Utilizando atribuio composta e instrues de iterao

127

de dilogo Open, sem o nome da pasta. Essa instruo exibe o nome do arquivo na caixa dt
texto do formulrio.
8. Substitua o comentrio // adicione um loop while aqui (//add while loop here) pela seguinte ins
truo:
source.Text = " " ;

A varivel source refere-se caixa de texto grande no formulrio. A configurao de sua pro
priedade Text como uma string vazia ( ) limpa todo o texto que exibido atualmente nessa
caixa de texto.
9. Digite a seguinte instruo depois da linha que voc acabou de adicionar para o mtodo openFi
leDialogFileOh.
TextReader reader = src.O p en T ex tO ;

Essa instruo declara uma varivel TextReader chamada reader. TextReader outra classe,
disponibilizada pelo .NET Framework, que pode ser utilizada para ler fluxos de caracteres a
partir de fontes como arquivos. Ela est localizada no namespace System .IO. A classe Filelnfo
fornece o mtodo OpenText para abrir um arquivo para leitura. Essa instruo abre o arquivo
selecionado pelo usurio na caixa de dilogo Open de modo que a varivel reader possa ler o
contedo desse arquivo.
10. Adicione a seguinte instruo depois da linha anterior que voc adicionou ao mtodo openFileDialogFileOk:
s trin g lin e = reader.R eadLin eO ;

Essa instruo declara uma varivel string chamada line e chama o mtodo reader.ReadLine
para ler a primeira linha do arquivo nessa varivel. Esse mtodo retorna a prxima linha de
texto ou um valor especial chamado nu ll caso no haja mais linhas para ler. (Se inicialmente
no houver linha alguma, o arquivo deve estar vazio.)
11. Adicione as seguintes instrues ao mtodo openFileDialogFileOk depois do cdigo que voc
acabou de inserir:
w h ile ( lin e

!= n u ll)

{
source.Text += lin e + ' \ n ';
lin e = reader.R eadLineO ;

}
Esse um loop while que itera pelo arquivo uma linha por vez at que no haja linha alguma
disponvel.
A expresso booleana no incio do loop while examina o valor da varivel line. Se ele no for
nulo, o corpo do loop exibir a linha de texto anexando-a propriedade Text da caixa de texto
source, junto com um caractere de nova linha (\n - o mtodo ReadLine do objeto TextRea
der exclui os caracteres de nova linha medida que l cada linha, portanto, o cdigo precisa
adicion-lo novamente). 0 loop while ento l a prxima linha de texto antes de realizar a pr
xima iterao. 0 loop while termina quando no h mais texto no arquivo e o mtodo ReadLine
retorna um valor null.

128

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

12. Adicione a seguinte instruo depois da chave de fechamento no fim do loop while:
r e a d e r.C lo s e O ;

Essa instruo fecha o arquivo. uma prtica recomendada fechar os arquivos aps utiliz-los;
esse procedimento permite que outros aplicativos utilizem o arquivo, alm de liberar memria e
outros recursos necessrios para a leitura do arquivo.
13. No menu Debug, clique em Start WithoutDebugging.
14. Quando o formulrio aparecer, clique em Open File.
15. Na caixa de dilogo Open File, abra a pasta \Microsoft Press\Visual CSharp Step By Step\ Chapter
5\WhileStatement na sua pasta Documentos. Selecione o arquivo MainWindow.xaml.es e clique
em Open.
Desta vez, o contedo do arquivo selecionado aparece na caixa de texto - voc deve reconhecer
o cdigo que acabou de editar:

! w hile S ta tem en t

j p o - |T - S

Current File
^ O p e n F ile ]

MainWindow.xaml.cs

using System;

using System.Collections.Generic;
using System.Linq;
using System .Text;
using System.Windows;
using System .Windows,Controls;
using System.Windows.Data;
using System.Windows,Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using M icrosoftWin32;
using System JO ;

namespace W hileStatem ent

16. Role pelo texto na caixa de texto e localize o mtodo openFi/eDialogFileOk. Verifique se esse
mtodo contm o cdigo que voc acabou de adicionar.
17. Feche o formulrio e retome ao ambiente de programao do Visual Studio 2010.

Captulo 5 Utilizando atribuio composta e instrues de iterao

129

Escrevendo instrues for


A maioria das instrues while tem a seguinte estrutura geral:
in ic ia liz a o
w h ile (expresso booleana)

{
i nstruo
atu a liz a o da v a ri v e l de co n tro le

}
Com uma instruofor, voc pode escrever uma verso mais formal desse tipo de construo com
binando a inicializao, a expresso booleana e o cdigo que atualiza a varivel de controle. Voc
achar a instruoT&r til porque muito mais difcil de esquecer qualquer uma de suas trs partes.
Observe a sintaxe da instruofor-,
fo r ( in ic ia liz a o ; expresso booleana; a tu a liz a o da v a r i v e l de c o n tro le )
i nstruo

Voc pode reformular o loop while mostrado anteriormente que exibe os inteiros de 0 a 9 como o loop
fo r a seguir:
fo r ( i n t i = 0; i < 10; i++)

{
C o n s o le .W r it e L in e (i);

}
A inicializao ocorre uma vez no incio do loop. Portanto, se a expresso booleana for avaliada
como true, a instruo ser executada. A atualizao da varivel de controle ocorre e ento a expres
so booleana reavaliada. Se a condio ainda for verdadeira, a instruo executada novamente, a
varivel de controle atualizada, a expresso booleana avaliada mais uma vez e assim por diante.
Observe que a inicializao ocorre apenas uma vez e que a instruo no corpo do loop sempre exe
cutada antes que a atualizao se realize e que a atualizao acontece antes de a expresso booleana
ser reavaliada.
Voc pode omitir qualquer uma das trs partes de uma instruofo r. Se voc omitir a expresso boo
leana, ela assumir true por padro. A instruofo r a seguir executada continuamente:
fo r ( i n t i - 0; ;i++)

{
Console.W riteLine("som ebody stop m e !");

130

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Se voc omitir as partes da inicializao e atualizao, ter um loop while escrito de forma estranha:
in t i = 0 ;
fo r ( ; i < 1 0 ; )

{
C o n s o le .W rite L in e (i) ;

i++;
}

Se necessrio, possvel fornecer vrias inicializaes e vrias atualizaes em um loop,for. (Voc


pode ter somente uma expresso booleana.) Para conseguir isso, separe as vrias inicializaes e
atualizaes por vrgula, como mostrado no exemplo a seguir:
fo r ( i n t i = 0, j = 10; i <= j ;

i++, j )

{
}
Como um exemplo final, observe o loop while do exerccio anterior reescrito como um loopfor.
fo r (s t r in g Tine = rea d e r.R e a d l_in e (); Tine != nul 1; Tine = rea d er.R e ad Lin e O )

{
source.Text += Tine + ' \ n ';

Dica Considera-se um bom estilo utilizar chaves para delinear explicitamente o bloco de instruo
para o corpo das instrues if, while e for, mesmo quando o bloco contm apenas uma instruo.
Escrevendo o bloco, voc facilita a adio de instrues ao bloco posteriormente. Sem o bloco, para
adicionar outra instruo, voc teria que lembrar de adicionar a instruo extra e as chaves, e
muito fcil esquecer as chaves.

Entendendo o escopo da instruo for


Talvez voc tenha notado que possvel declarar uma varivel na parte da inicializao de uma ins
truofo r. Essa varivel tem o escopo definido para o corpo da instruofo r e desaparece quando a
instruofo r termina. Essa regra tem duas consequncias importantes. Em primeiro lugar, voc no
pode utilizar essa varivel aps a instruofo r ter terminado, porque ela no estar mais em escopo.
Veja o exemplo:
fo r ( i n t i = 0; i < 10; i++)

{
}
C o n s o le .W r it e L in e (i); // erro de tempo de compilao

Captulo 5

Utilizando atribuio composta e instrues de iterao

131

Segundo, voc pode escrever duas ou mais instruesfo r prximas entre si que reutilizam o mesmo
nome de varivel, porque cada varivel est em um escopo diferente, como no cdigo a seguir:
fo r int i = 0 ; i < 1 0 ; i++)

{
}
fo r (int i = 0; i < 20; i += 2) // ok

{
}

Escrevendo instrues do
As instrues while efo r testam suas expresses booleanas no incio do loop. Isso significa que, se a
expresso avaliada como false no primeiro teste, o corpo do loop no executado nem mesmo uma
vez. A instruo do diferente; sua expresso booleana avaliada aps cada iterao e, portanto, o
corpo sempre executado ao menos uma vez.
A sintaxe da instruo do a seguinte (no esquea o ponto e vrgula final):
do
i nstruo
whi 1e (exp ressoBooleana);

Voc deve utilizar um bloco de instrues se o corpo do loop incluir mais de uma instruo. Eis uma
verso do exemplo que escreve os valores de 0 a 9 no console, dessa vez construda utilizando uma
instruo do-,
in t i = 0;
do

{
C o n s o le .W r it e L in e (i);
i++;

}
w h ile ( i < 10);

As instrues break e continue


No Captulo 4, voc viu como a instruo break utilizada para sair de uma instruo switch.
Voc tambm pode utilizar uma instruo break para sair do corpo de uma instruo de
iterao. Quando voc sai de um loop, ele encerra imediatamente e a execuo continua na
primeira instruo aps o loop. Nem a atualizao nem a condio de continuao do loop
so executadas novamente.

132

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Por outro lado, a instruo continue faz o programa executar imediatamente a prxima ite
rao do loop (depois de avaliar de novo a expresso booleana). Eis uma verso do exemplo
anterior que escreve os valores de 0 a 9 no console, desta vez utilizando as instrues break
e continue:
in t i = 0 ;
w h ile (tr u e )

{
C o n so le .W rite Lin e ("co n tin u e " + i ) ;

i++;
if

( i < 10)
continue;
el se
break;

}
Esse cdigo est medonho. Muitas diretrizes de programao recomendam a utilizao
cautelosa da instruo continue ou simplesmente no utiliz-la, porque ela est muitas
vezes associada a um cdigo difcil de entender. O comportamento de continue tambm
muito sutil. Por exemplo, se voc executar uma instruo continue de dentro de uma
instruo for, a parte da atualizao ser executada antes da execuo da prxima iterao
do loop.

No exerccio a seguir, voc vai escrever uma instruo do para converter um nmero inteiro decimal
positivo na sua representao de string em notao octal. O programa se baseia no seguinte algorit
mo, fundamentado em um procedimento matemtico conhecido:
armazene o nmero decimal na v a ri v e l dec
fa a o seguinte
d iv id a dec por 8 e armazene o resto
d efin a dec com o quociente da etapa a n te rio r
enquanto dec no fo r ig ual a zero
combine os v a lo re s armazenados para o resto em cada c lc u lo , em ordem in ve rsa

Por exemplo, vamos supor que voc queira converter o nmero decimal 999 em octal. Execute as
seguintes etapas:
1. Divida 999 por 8. O quociente 124 e o resto 7.
2. Divida 124 por 8. O quociente 15 e o resto 4.
3. Divida 15 por 8. O quociente 1 e o resto 7.
4. Divida 1 por 8. O quociente 0 e o resto 1.
5. Combine os valores calculados para o resto em cada etapa, em ordem inversa. O resultado
1747. Essa a representao octal do valor decimal 999.

Captulo 5 Utilizando atribuio composta e instrues de iterao

133

Escreva uma instruo do


1. Utilizando Visual Studio 2010, abra o projeto DoStatement, localizado na pasta \Microsoft
PressWisual CSharp Step By Step\Chapter 5\DoStatement na sua pasta Documentos.
2. Exiba o formulrio WPF, MainWindow.xam l na janela Design View.

O formulrio contm uma caixa de texto chamada number, na qual o usurio pode digitar um
nmero decimal. Quando o usurio clicar no boto Show Steps, a representao octal do n
mero inserido gerada. A caixa de texto inferior, chamada steps, mostra os resultados de cada
estgio do clculo.
3. Exiba o cdigo de MainWindow.xaml.es, na janela Code and Text Editor. Localize o mtodo
showStepsClick, que executado quando o usurio clica no boto Show Steps, no formulrio.
Atualmente, ele est vazio.
4. Adicione as seguintes instrues, que aparecem em negrito, ao mtodo showStepsClick-,
p riv a te void show StepsC1ick(object sender, RoutedEventArgs e)

{
int amount = int.Parse(number.Text);
steps.Text =
string current =

}
A primeira instruo converte o valor da string na propriedade Text da caixa de texto number em
um tipo in t usando o mtodo Parse do tipo in t e armazene-o em uma varivel local, chamada
amount.
A segunda instruo limpa o texto exibido na caixa de texto inferior, definindo sua propriedade
Text como uma string vazia.
A terceira instruo declara uma varivel string chamada current e a inicializa como a string
vazia. Use essa string para armazenar os dgitos gerados em cada iterao do loop utilizado
para converter o nmero decimal em sua representao octal.
5. Adicione a seguinte instruo do, que aparece em negrito, ao mtodo showStepsClick
.
p riv a te void show StepsC1ick(object sender, RoutedEventArgs e)

{
in t amount = i n t . Parse(num ber.T ext);
steps.Tex t =
s trin g cu rren t =
do

{
int nextDigit = amount % 8;
amount /= 8;
int digitCode = '0' + nextDigit;
char digit = Convert.ToChar(digitCode);

134

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

current = digit + current;


steps.Text += current + "\n";

}
while (amount != 0);

}
0 algoritmo realiza repetidamente aritmtica de inteiros para dividir a varivel amount por 8 e
determinar o resto; o resto depois de cada diviso sucessiva constitui o prximo dgito na string
sendo compilada. Por fim, quando amount reduzida a 0, o loop termina. Observe que o corpo
deve ser executado ao menos uma vez. Esse comportamento exatamente o que necessrio,
porque mesmo o nmero 0 tem um dgito octal.
Examine o cdigo, de forma mais minuciosa, e voc ver que a primeira instruo dentro do
loop do esta:
in t n ex tD ig it = amount % 8;

Essa instruo declara uma varivel in t chamada nextDigit e a inicializa como o resto da divi
so do valor em amount por 8. Isso ser um nmero em algum lugar entre 0 e 7.
A prxima instruo dentro do loop do
amount/=8;

Essa uma instruo de atribuio composta e equivale a escrever amount = amount / 8;.
Se o valor de amount for 999, o valor de amount depois da execuo dessa instruo ser 124.
A prxima instruo esta:
in t digitCode = '0 ' + n e x tD ig it;

Essa expresso exige uma pequena explicao! Os caracteres tm um cdigo nico de acordo
com o conjunto de caracteres utilizado pelo sistema operacional. Nos conjuntos de caracte
res frequentemente utilizados pelo sistema operacional Microsoft Windows, o cdigo para o
caractere 0 tem um valor de inteiro 48. O cdigo para o caractere 1 49, o cdigo para o
caractere 2 50 e assim por diante at o cdigo para o caractere 9 , que tem valor de in
teiro 57. O C# permite tratar um caractere como um inteiro e realizar aritmtica nele, mas,
quando voc faz isso, o C# utiliza o cdigo do caractere como o valor. Portanto, a expresso
' 0' + nextDi gi t na verdade resultar em um valor em algum lugar entre 48 e 55 (lembre-se
de que nextDigit estar entre 0 e 7), correspondente ao cdigo para o dgito octal equivalente.
A quarta instruo dentro do loop do :
char d i g it = C o n vert.T o C ha r(d ig itC o d e);

Essa instruo declara uma varivel char chamada digit e a inicializa para o resultado da cha
mada do mtodo Convert.ToChar(digitCode). O mtodo Convert.ToChar recebe um inteiro que

Captulo 5

Utilizando atribuio composta e instrues de iterao

135

contm um cdigo de caractere e retorna o caractere correspondente. Assim, por exemplo, se


digitCode tiver o valor 54, Convert.ToChar(digitCode) retornar o caractere 6 .
Resumindo, as trs primeiras instrues no loop do determinaram o caractere que representa o
dgito octal menos significativo (mais direita) que corresponde ao nmero que o usurio digi
tou. A prxima tarefa prefixar esse dgito string sendo gerada, desta maneira:
cu rren t = d i g it + cu rre n t;

A prxima instruo dentro do loop do esta:


steps.Tex t += cu rren t + "\ n ";

Essa instruo adiciona caixa de texto Steps a string que contm os dgitos produzidos at
agora para a representao octal do nmero. A instruo tambm inclui um caractere de nova
linha, para que cada estgio da converso aparea em uma linha separada, na caixa de texto.
Por fim, a condio na clusula while no fim do loop avaliada:
w h ile (amount!= 0)

Como o valor de amount ainda no 0, o loop realiza mais uma iterao.


No exerccio final, voc utilizar o depurador do Visual Studio 2010 para inspecionar passo a passo
a instruo do anterior para ajud-lo a entender como ela funciona.

Inspecione passo a passo a instruo c/o


1. Na janela Code and Text Editor que exibe o arquivo MainWindow.xaml.es, mova o cursor para a
primeira instruo do mtodo showStepsClick
.
in t amount = i n t . Parse(num ber.T ext);

2. Clique com o boto direito do mouse em qualquer lugar da primeira instruo e clique em Run
To Cursor.
3. Quando o formulrio aparecer, digite 999 na caixa de texto superior e, em seguida, clique em
Show Steps.
O programa para e voc colocado no Visual Studio 2010 no modo de depurao. Uma seta
amarela na margem esquerda da janela Code and Text Editor indica a instruo atual.
4. Exiba a barra de ferramentas Debug se ainda no estiver visvel. No menu View, aponte para
Toolbars e clique em Debug.
5. Se estiver utilizando o Visual Studio 2010 Professional ou Standard, na barra de ferramentas
Debug, clique na seta suspensa Breakpoints. Se estiver usando o Visual C# 2010 Express, na
barra de ferramentas Debug, clique na seta suspensa Output.

136

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

O menu a seguir exibido:


*^5 DoStatement (Debugging) - Microsoft Visual Studio
File

Edit

View

Project

J _J J J i

Build

Debug

Data

Tools

Test

4 o

Window

Help

p*,

;^CPU

,i

, - i

. it ,

MainWindow.xaml.ci
''IjDoStatement. MainWindow

d^showStepsClickiobject sender, RoutedEventArgs e)

InitializeComponent();

U
. 3 - .W m
Breakpoints
Ctrl+D, B
Pr 3
a Output
Parallel Tasks
Ctrl+D, K
a
Ctrl+D, S
a Parallel Stacks
Watch

private void showStepsClick(object sender, RoutedEventArgs e)

int amount int.Parse(number.Text);


steps.Text "j
string current = }
do

{
int nextigit = amount X 8;
amount / 8;

Autos

Ctrl+D, A

Locals

Ctrl+D, L

Immediate

Ctrl+D, 1

Call Stack

Ctrl+D, C

si

Threads

Ctrl+D, T

Modules

Ctrl+D, M

Processes

Ctrl+D, P

Memory
Autos

- ? X

Name

GB f

Value
amount 0
{System.Windows.RoutedEve
number {System.Wndows.Contr

ES # sender {System. Windows.Contr


IB this
{DoStatement.MariWind

m Locate t ? B B a

;
J

Typ
int
System.V
System.V
object {S
DoStaterr

CallStack

- 9

1Name

1Lane

3
s
4

DoStatement.exe'DoStatement.MamWindow.shol C=
1"^External Code]

Watch 1

B i Immediate Window

Ready

Ln31

Col 13

Disassembly

Ctrl+Alt+D

Registers

Ctrl+D, R

Toggle Disassembly

Ctrl+D, D

s
!NS

,;j

6. No menu drop-down, clique em Locais.


A janela Locais aparece (se ainda no estiver aberta). Essa janela exibe o nome, o valor e o tipo
das variveis locais no mtodo atual, incluindo a varivel local amount. Observe que o valor de
amount no momento 0:
Locals

I Nam e

Value

Type

^M SUBk

IB

0 sender

V e
0

amount

current

L o c a ls J^ JJ

{System. Windows.Controls.ButtoC^ object {System.Windows.Cc


{System .Windows.RoutedEventArgs)

_________

ho

null

'

System.Windows.RoutedEvi
int
string

1 '

x|

Captulo 5 Utilizando atribuio composta e instrues de iterao

137

7. Na barra de ferramentas Debug, clique no boto Step Into. O depurador executa a instruo:
in t amount = i n t . Parse(num ber.T ext);

O valor de amount na janela Locais muda para 999 e a seta amarela se move para a prxima
instruo.
8. Clique novamente em Step Into. 0 depurador executa a instruo:
s te p s .Text = " " ;

Essa instruo no afeta a janela Locais porque steps um campo do formulrio e no uma
varivel local. A seta amarela se move para a prxima instruo.
9. Clique em Step Into.
O depurador executa a instruo:
s trin g cu rren t =

A seta amarela se move para a chave de abertura no incio do loop do. O loop do contm trs va
riveis locais prprias: nextDigit, digitCode e digit. Observe que essas variveis locais aparecem
na janela Locais, e que o valor das trs variveis 0.
10. Clique em Step Into.
A seta amarela se move para a primeira instruo dentro do loop do.
11. Clique em Step Into.
O depurador executa a instruo:
in t n e x tD ig it = amount % 8;

O valor de nextDigit na janela Locais muda para 7. Esse o resto depois da diviso de 999
por 8.
12. Clique em Step Into. O depurador executa a instruo:
Amount /= 8;

O valor de amount muda para 124, na janela Locais.


13. Clique em Step Into.
O depurador executa a instruo:
in t digitCode = '0 ' + n e x tD ig it;

138

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

O valor de digitCode na janela Locais muda para 55. Esse o cdigo de caracteres de
7 (48 + 7).
14. Clique em Step Into.
0 depurador executa a instruo:
char d ig it = C o n vert.T o C h a r(d ig itC o d e );

O valor de digit muda para ' 71 na janela Locais. A janela Locais mostra valores char utilizando
tanto o valor numrico subjacente (nesse caso, 55) como tambm a representao em caractere
('7 ') .
Observe que, na janela Locais, o valor da varivel current ainda
15. Clique em Step Into. O depurador executa a instruo:
cu rren t = cu rren t + d i g it ;

O valor de current muda para "7" na janela Locais.


16. Clique em Step Into.
O depurador executa a instruo:
step s.T ex t += cu rren t + "\ n ";

Essa instruo exibe o texto 7 na caixa de texto steps, seguido por um caractere de nova
linha para fazer a sada subsequente ser exibida na prxima linha na caixa de texto. (O formu
lrio atualmente est oculto atrs do Visual Studio, portanto, voc no ser capaz de v-lo.) O
cursor se desloca para a chave de fechamento no final do loop do.
17. Clique em Step Into. A seta amarela se move para a instruo while para avaliar se o loop do foi
concludo ou se deve continuar para outra iterao.
18. Clique em Step Into.
O depurador executa a instruo:
w h ile (amount!= 0 );

O valor de amount 124 e a expresso 124! = 0 avaliada como true, portanto, o loop do rea
liza uma outra iterao. A seta amarela retorna chave de abertura no incio do loop do.
19. Clique em Step Into.
A seta amarela se move novamente para a primeira instruo do loop do.
20. Clique repetidamente em Step Into para investigar as trs iteraes seguintes do loop do e obser
ve como os valores das variveis mudam na janela Locais.

Captulo 5 Utilizando atribuio composta e instrues de iterao

139

21. No fim da quarta iterao do loop, o valor de amount agora 0 e o valor de current 1747".
A seta amarela est na condio while no final do loop do
.
w h ile (amount!= 0 );

O valor de amount agora 0, portanto, a expresso amount! = 0 ser avaliada comofa lse e o
loop do terminar.
22. Clique em Step Into. O depurador executa a instruo:
w h ile (amount!= 0 );

Conforme previsto, o loop do termina e a seta amarela se move para a chave de fechamento no
final do mtodo showStepsClick.
23. Clique no boto Continue na barra de ferramentas Debug.
O formulrio aparece exibindo as quatro etapas utilizadas para criar uma representao octal
de 999: 7,47, 747 e 1747.

24. Feche o formulrio para retornar ao ambiente de programao do Visual Studio 2010.
Neste captulo, voc aprendeu a utilizar os operadores de atribuio composta para atualizar va
riveis numricas. Voc viu como possvel utilizar a as instrues w h ile,Jor e do para executar o
cdigo vrias vezes, enquanto uma condio booleana for true.
Se voc quiser seguir para o prximo captulo agora:
Mantenha o Visual Studio 2010 em execuo e v para o Captulo 6.
Se quiser sair do Visual Studio 2010:
No menu File, clique em Exit. Se vir uma caixa de dilogo Save, clique em Yes e salve o projeto.

140

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Referncia rpida do Captulo 5


Para

Adicionar uma quantidade a uma varivel

Faa isto

Utilize o operador de adio composto. Por exemplo:


v a r ia b le += amount;

Subtrair uma quantidade de uma varivel

Utilize o operador de subtrao composto. Por exemplo:


v a r ia b le -= amount;

Executar uma ou mais instrues zero ou mais vezes,


enquanto uma condio for verdadeira

Utilize uma instruo while. Por exemplo:


in t i = 0 ;
w h ile ( i < 10)
{
C o n s o le .W r it e L in e (i);
i++;

}
Como alternativa, utilize uma instruo for. Por exemplo:
fo r ( i n t i = 0; i < 10; i++)
{
C onsole.W ri te L i n e ( i ) ;
}

Executar repetidamente as instrues uma ou mais vezes

Utilize a instruo do. Por exemplo:


in t i = 0 ;
do
{
C o n s o le .W r it e L in e (i);
i++;
}
w h ile ( i < 10);

Captulo 6

Gerenciando erros e excees


Neste captulo, voc vai aprender a:
Tratar excees utilizando as instrues try, catch e finally.
Controlar o overflow de nmeros inteiros utilizando as palavras-chave ch ecked e unchecked.
Levantar excees a partir de seus mtodos utilizando a palavra-chave th row .
Garantir que o cdigo sempre execute, mesmo aps a ocorrncia de uma exceo, utili
zando um bloco finally.

Voc viu at agora as principais instrues do Microsoft Visual C# necessrias para escrever mto
dos, declarar variveis, utilizar operadores para criar valores, escrever instrues if t switch para
executar um cdigo seletivamente e escrever as instrues while Jo r e do para executar o cdigo repe
tidamente. Mas os captulos anteriores no consideraram a possibilidade (ou probabilidade) de algo
dar errado. muito difcil garantir que o cdigo sempre funcione conforme o esperado. As falhas
podem ocorrer por vrios motivos, muitos dos quais esto alm do seu controle como programador.
Todos os aplicativos que voc escrever devem ser capazes de detectar falhas e de lidar com elas de
maneira elegante. Neste captulo final da Parte I, Apresentando o Microsoft Visual C# e o Microsoft
Visual Studio 2010", voc aprender como o C# utiliza excees para sinalizar a ocorrncia de um
erro e como utilizar as instrues try, catch eJin a lly para capturar e lidar com os erros que essas
excees representam. No final deste captulo, voc ter uma base slida em C#, sobre a qual cons
truir seu conhecimento na Parte II, Entendendo a linguagem C# .

Lidando com erros


Faz parte da vida que coisas ruins aconteam s vezes. Pneus furam, baterias descarregam, ferra
mentas nunca ficam onde voc as deixou e os usurios de seus aplicativos se comportam de maneira
imprevisvel. No mundo dos computadores, os discos falham, outros aplicativos em execuo no
mesmo computador em que seu programa executado consomem, de modo descontrolado, toda a
memria disponvel e as redes se desconectam no momento mais atribulado. Erros podem ocorrer
em praticamente qualquer estgio da execuo de um programa; portanto, como detect-los e tentar
se recuperar deles?
Ao longo dos anos, vrios mecanismos foram criados. Uma abordagem tpica adotada por sistemas
mais antigos, como o UNIX, envolvia determinar que o sistema operacional definisse uma varivel
global especial sempre que um mtodo falhasse. Ento, depois de cada chamada a um mtodo, voc
verificava a varivel global para ver se o mtodo havia sido bem-sucedido. O C# e a maioria das
outras linguagens modernas orientadas a objetos no tratam erros dessa maneira. simplesmente
muito trabalhoso. Por isso, elas utilizam excees. Se quiser escrever programas robustos em C#,
voc precisa conhecer as excees.

142

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Testando o cdigo e capturando as excees


Os erros podem acontecer a qualquer momento, e o uso de tcnicas tradicionais para adicionar ma
nualmente um cdigo de deteco de erro em torno de cada instruo complicado, lento e simples
mente propenso a erros. Voc tambm pode se desviar do fluxo principal de um aplicativo, se cada
instruo exigir uma lgica enrolada de tratamento de erros, para gerenciar cada possvel erro que
ocorra em cada estgio. Felizmente, o C# facilita separar o cdigo de tratamento do cdigo que im
plementa o fluxo principal do programa utilizando as excees e as rotinas de tratamento de exceo.
Para escrever programas compatveis com excees, voc precisa fazer duas coisas:
1. Escrever o cdigo dentro de um bloco try {try uma palavra-chave do C#). Ouando o cdigo
executa, ele tenta executar todas as instrues dentro do bloco try e, se nenhuma das instrues
gerar uma exceo, todas sero executadas, uma aps a outra, at a concluso. Mas se uma
condio de erro ocorrer, a execuo sai do bloco try e vai at um outro fragmento de cdigo
projetado para capturar e tratar a exceo - uma rotina de tratamento catch.
2. Escrever uma ou mais rotinas de tratamento catch (catch outra palavra-chave do C#) ime
diatamente aps o bloco try para tratar todas as condies de erro possveis. Uma rotina de
tratamento catch concebida para capturar e tratar um tipo de exceo especfica e voc pode
ter mltiplas rotinas de tratamento catch depois de um bloco try, cada uma projetada para in
terceptar e processar uma exceo especfica para que voc possa fornecer diferentes rotinas de
tratamento para os diferentes erros que poderiam aparecer no bloco try. Se qualquer uma das
instrues dentro do bloco try causar um erro, o runtime gera e lana uma exceo. O runtime
ento examina as rotinas de tratamento catch aps o bloco try e transfere o controle diretamen
te para a primeira rotina de tratamento correspondente.
Observe um exemplo do cdigo em um bloco try que tenta converter as strings que um usurio
digitou em algumas caixas de texto em um formulrio para valores inteiros, chamar um mtodo
para calcular um valor e gravar o resultado em outra caixa de texto. Converter uma string em um
nmero inteiro requer que a sequncia contenha um conjunto de dgitos vlido e no alguma string
arbitrria. Se a sequncia contm caracteres invlidos, o mtodo Int.Parse lana automaticamente
uma FormatException e a execuo transferida para a rotina de tratamento catch correspondente.
Ouando a rotina de tratamento catch termina, o programa continua na primeira instruo aps a
rotina de tratamento:
try

{
in t TeftHandSide = i n t . P a rse (lh sO p eran d .T ex t);
in t rightHandSide = in t.P a rs e (rh s O p e ra n d .T e x t);
in t answer = d o C a lcu la tio n (le ftH a n d S id e , rightH an dSide);
r e s u lt.T e x t = an sw er.T o Strin g O ;

}
catch (Form atException fE x )

{
// T ra te a exceo

Captulo 6

Gerenciando erros e excees

143

Uma rotina de tratamento catch utiliza uma sintaxe similar utilizada por um parmetro de mtodo
para especificar a exceo a ser capturada. No exemplo anterior, quando FormatException lanada,
a varivelJE x preenchida com um objeto que contm os detalhes da exceo. O tipo FormatExcep
tion possui vrias propriedades que voc pode examinar para determinar a causa exata da exceo.
Muitas dessas propriedades so comuns a todas as excees. Por exemplo, a propriedade Message
contm uma descrio textual do erro que provocou a exceo. Voc pode utilizar essas informaes
ao tratar a exceo, talvez gravando os detalhes em um arquivo de log ou exibindo uma mensagem
significativa para o usurio e, depois, solicitando que ele tente novamente, por exemplo.

Excees no tratadas
O que acontece se um bloco try lana uma exceo e no h uma rotina de tratamento catch corres
pondente? No exemplo anterior, possvel que a caixa de texto IhsOperand contenha a representao
em string de um nmero inteiro vlido, mas o inteiro representado est fora do intervalo de nmeros
inteiros vlidos suportado pelo C# (por exemplo, 2147483648). Nesse caso, a instruo int.Parse
lana uma OverJlowException que no ser capturada pela rotina de tratamento FormatException
catch. Se isso ocorrer e se o bloco try for parte de um mtodo, este se encerrar imediatamente e
a execuo retornar ao mtodo chamador. Se o mtodo chamador utiliza um bloco try , o runtime
tenta localizar a rotina de tratamento catch correspondente, aps o bloco try no mtodo chamador,
e execut-la. Se o mtodo chamador no utiliza um bloco try ou no h uma rotina de tratamen
to catch correspondente, o mtodo chamador encerra imediatamente e a execuo retorna ao seu
chamador, onde o processo repetido. Se uma rotina de tratamento catch correspondente por fim
encontrada, a rotina executada e a execuo continua na primeira instruo aps a rotina de trata
mento catch no mtodo de captura.

Importante Observe que, aps capturar uma exceo, a execuo continua no mtodo que con
tm o bloco catch que capturou a exceo. Se a exceo ocorreu em um mtodo alm daquele que
contm a rotina de tratamento catch, o controle no retorna ao mtodo que causou a exceo.

Se, aps retornar pela cascata de mtodos chamadores, o runtime for incapaz de encontrar uma
rotina de tratamento catch correspondente, o programa terminar com uma exceo no tratada.
Voc pode examinar facilmente as excees geradas por seu aplicativo. Se voc estiver executando o
aplicativo no Microsoft Visual Studio 2010 no modo de depurao (isto , voc selecionou StartDebugging no menu Debug para executar o aplicativo), e uma exceo ocorrer, ser exibida uma caixa
de dilogo semelhante da imagem includa a seguir e o aplicativo far uma pausa, para que voc
determine a causa da exceo:

144

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

&

MathsOperators (Debugging) - Microsoft Visual Studio


File

dit

View

Project

guild

i j ' j ' j j j ' *

Debug

Data

Tools

* ,J - - 4

iJMathsOperators.MainWindow

Test

Window

t**>

,Any C PU

i a

JJ

^calculateClick(objectsender, RoutedEventArgse)

1M I

InitializeComponent();

private void calculateClick{object sender, RoutedEventArgs e}

int leftHandSide - int.ParseflhsOperand.Text);


injt rightHandSide = i n t .Parse(rhsOperand.Text^;
I

Form at Except ion w as unhandled

Input string was not in a correct format.

Make sure your method arguments are in the right format.


When converting a string to DateTime, parse the string to take the date before putting each variable into the DateTime object,

Autos
Name

Value

Get general help for this exception.

{System,wj Search for more Help Online...


ffl # e
0 leftHaq
B ^ h s O p e {System. Vi Actions;
IS & sender {System.Wl View Detail...
B) this.......
Copy exception detail to the clipboard

O aplicativo paralisado na instruo que causou a exceo e voc entra no depurador. Voc pode
examinar e alterar os valores de variveis, e pode analisar seu cdigo a partir do ponto em que a
exceo ocorreu, por meio da barra de ferramentas Debug e das vrias janelas de depurao.

Utilizando mltiplas rotinas de tratamento catch


A discusso anterior destacou como diferentes erros lanam diferentes tipos de exceo para repre
sentar diferentes tipos de falhas. Para lidar com essas situaes, voc pode fornecer mltiplas rotinas
de tratamento catch, uma aps a outra, assim:
try

{
in t leftH and Side = in t.P a rs e (lh s O p e ra n d .T e x t);
in t rightHandSide = i n t . Parse(rh sO p eran d .T ex t);
in t answer = doCa1cu1ation(1eftHandSide, rightH an dSide);
re s u lt.T e x t = an sw er.T o Strin g O ;

}
catch (Form atException fEx )

{
/ / ...
}
catch (O verflowException oEx)

{
/ / ...
}

Captulo 6 Gerenciando erros e excees

145

Se o cdigo no bloco try lanar uma exceo FormatException, as instrues no bloco catch para a
exceo FormatException sero executadas. Se o cdigo lanar uma exceo OveiflowException, o
bloco catch para a exceo OveiflowException ser executado.

Capturando mltiplas excees


O mecanismo de captura de excees fornecido pelo C# e pelo Microsoft .NET Framework bem
abrangente. O .NET Framework define vrios tipos de excees, e qualquer programa que voc escre
va poder lanar a maioria delas! pouco provvel que voc queira escrever rotinas de tratamento
catch para todas as excees possveis que seu cdigo pode lanar. Portanto, como voc assegura que
seus programas capturam e tratam todas as possveis excees?
A resposta a essa pergunta est na maneira como as diferentes excees esto relacionadas entre
si. As excees so organizadas em famlias chamadas hierarquias de herana. (Voc aprender he
rana no Captulo 12, Trabalhando com herana .) FormatException e OveiflowException pertencem
a uma famlia chamada System Exception, assim como vrias outras excees. System Exception ,
em si mesmo, um membro de uma famlia maior, chamada Exception, que a bisav de todas as
excees. Se voc capturar Exception, a rotina de tratamento ir capturar todas as excees possveis
que possam ocorrer.

O prximo exemplo mostra como capturar todas as possveis excees:


try

{
in t leftH and Side = i n t . Parse(lhsO perand.T e x t );
in t rightHandSide = i n t . Parse(rh sO p eran d .T ex t);
in t answer = doCalcul a t i on (le ftH a n d S id e , righ tH andSide);
r e s u lt .Text = answer.T o S t r in g ( ) ;

}
catch (Exception ex) // e sta uma ro tin a de tratam ento catch geral

II...
}

146

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Dica Se quiser capturar a exceo Exception, voc pode omitir seu nome na rotina de tratamento
catch, porque ela a exceo padro:
catch

{
I I . ..
}
Mas isso nem sempre recomendado. O objeto exceo passado para a rotina de tratamento catch
contm informaes teis referentes exceo, que no so acessveis ao utilizar essa verso da
construo catch.

Nesse ponto, h uma pergunta final que voc deve estar se fazendo: o que acontece se a mesma exce
o corresponder a mltiplas rotinas de tratamento catch no final de um bloco try l Se voc capturar
FormatException e Exception em duas rotinas de tratamento diferentes, qual delas ser executada
(ou ambas sero escutadas)?
Quando ocorre uma exceo, a primeira rotina de tratamento encontrada pelo runtime que corres
ponde exceo utilizada e as outras so ignoradas. O que isso significa que, se voc colocar uma
rotina de tratamento para Exception antes de uma rotina de tratamento para FormatException, a ro
tina de tratamento de FormatException nunca ser executada. Portanto, voc deve colocar as rotinas
de tratamento catch mais especficas acima de uma rotina de tratamento catch geral, depois de um
bloco try. Se nenhuma das rotinas de tratamento catch especficas corresponder exceo, a rotina
de tratamento catch geral ir corresponder.
No prximo exerccio, voc ir escrever um bloco try e capturar uma exceo.

Escreva um bloco de instrues try/catch


1. Inicie o Visual Studio 2010 se ele ainda no estiver em execuo.
2. Abra a soluo MathsOperators localizada na pasta \Microsoft PressWisual CSharp Step By
Step\Chapter 6\MathsOperators na sua pasta Documentos.
Essa uma variao do programa que voc viu no Captulo 2, Trabalhando com variveis,
operadores e expresses . Ele foi utilizado para demonstrar os diversos operadores aritmticos.
3. No menu Debug, clique em Start W ithout Debugging.
O formulrio aparece. Agora voc digitar um texto que no ser vlido na caixa de texto do
operando esquerdo. Essa operao demonstrar a falta de robustez da verso atual do pro
grama.
4. Digite John na caixa de texto do operando esquerdo e clique em Calculate.

Captulo 6

Gerenciando erros e excees

147

Essa ao aciona o tratamento de erros do Windows e a seguinte caixa de dilogo exibida:

MathsOperators

IwSiwI

MathsOperators has stopped working


W indows is checking for a solution to the problem...

Cancel

Essa caixa seguida por outra caixa de dilogo que reporta uma exceo no tratada;

Voc poderia ver uma verso diferente dessa caixa de dilogo, dependendo de como voc configurou
o informe de problemas no painel de controle.

MathsOperators

Ifc3wl

MathsOperators has stopped working


W indow s can check online for a solution to the problem.

- Check online for a solution and close the program


-> Close the program

View problem details

Se vir essa caixa de dilogo, simplesmente clique em Close the program e continue na segunda sen
tena da etapa 6 a seguir.
Alm disso, deve ser exibida uma caixa de dilogo com a mensagem Do you want to send infor
mation about the problem? . 0 Windows pode reunir informaes sobre os aplicativos com falha,
e envi-las para a Microsoft. Se essa caixa de dilogo for exibida, clique em Cancel e continue na
segunda sentena da etapa 6.

148

Parte I Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

5. Se voc estiver usando o Visual Studio 2010 Professional ou Standard, clique em Debug. Na
caixa de dilogo Visual Studio Just-In-Time Debugger, na caixa de listagem Possible Debuggers,
selecione MathsOperators - Microsoft Visual Studio.- Visual Studio 2010 e ento clique em Yes-,

6. Se voc estiver utilizando o Visual C# 2010 Express, clique em CloseProgram. No menu Debug,
clique em Sta rt Debugging. Digite John na caixa de texto do operando esquerdo e clique em
Calculate.
7. O Visual Studio 2010 exibe o seu cdigo e destaca a instruo que causou a exceo, juntamen
te com uma caixa de dilogo que descreve essa exceo. Nesse caso, a informao a seguinte:
Input string was not in a correct format." (Formato incorreto na string de entrada).
Voc pode ver que a exceo foi lanada pela chamada a int.Parse dentro do mtodo calculateClick.
O problema que esse mtodo incapaz de processar o texto John em um nmero vlido.

8. Na barra de ferramentas Debug, clique no boto Stop Debugging. O programa encerrado.


9. Exiba o cdigo para o arquivo MainWindow.xaml.es na janela Code and Text Editor e localize o
mtodo calculateClick.
10. Adicione um bloco try (incluindo chaves) em torno das quatro instrues dentro desse mtodo,
como mostrado no texto em negrito aqui:
try

{
in t leftH and Side = i n t . P a rse(lh sO p eran d .T ex t);
in t rightHandSide = in t.P a rs e (rh s O p e ra n d .T e x t);
in t answer = doC alcula t i o n (leftH an d Sid e, rightH an dSide);

Captulo 6

Gerenciando erros e excees

149

re s u lt.T e x t = answer.T o S trin g Q ;

}
11. Adicione um bloco catch logo aps a chave de fechamento para esse novo bloco try, como a
seguir:
catch (Form atException fE x )

{
re s u lt.T e x t = fEx.Message;

}
Essa rotina de tratamento catch captura a FormatException lanada por int.Parse e ento a
exibe na caixa result de texto, na parte inferior do formulrio, o texto na propriedade Message
da exceo.
12. No menu Debug, clique em Start W ithout Debugging.
13. Digite John na caixa de texto do operando esquerdo e clique em Calculate.
A rotina de tratamento catch captura com sucesso a FormatException, e a mensagem Input
string was not in a correct format escrita na caixa de texto Result. O aplicativo agora est um
pouco mais robusto.
K

j O Exceptions

tm

right operand

left operand

John

+ Addition
- Subtraction
* Multiolication
/ Division
% Remainder

Calculate

Expression
Input string was not in a correct form at

Result

14. Substitua John pelo nmero 10, digite Sharp na caixa de texto do operando direito e ento
clique em Calculate.
0 bloco try cerca as instrues que processam as duas caixas de texto, de modo que a mesma
rotina de tratamento de exceo trata os erros de entrada de usurio em ambas as caixas de
texto.
15. Substitua Sharp por 20 na caixa de texto do operando da direita, clique no boto Addition e
depois clique em Calculate. O aplicativo funciona como previsto e exibe o valor 30 na caixa de
texto Result.
16. Clique em Quit para retornar ao ambiente de programao do Visual Studio 2010.

150

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Utilizando aritmtica verificada e no verificada de


nmeros inteiros
No Captulo 2, voc aprendeu a utilizar os operadores aritmticos binrios, como + e *, em tipos
de dados primitivos, como in t e double. Voc tambm viu que os tipos de dados primitivos tm um
tamanho fixo. Por exemplo, um in t do C# tem 32 bits. Como in t tem um tamanho fixo, voc sabe
exatamente o intervalo de valores que ele pode armazenar: de -2147483648 a 2147483647.

Dica Se quiser referenciar o valor mnimo ou mximo de int no cdigo, utilize a propriedade int.
MinValue ou int.MaxValue.

O tamanho fixo de um tipo in t cria um problema. Por exemplo, o que acontece se voc adicionar 1 a
um in t cujo valor atualmente 2147483647? A resposta que depende de como o aplicativo com
pilado. Por padro, o compilador C# gera um cdigo que permite ao clculo ter um overflow (estou
ro) silencioso e voc obtm uma resposta errada. (Na verdade, o clculo excede para o valor inteiro
negativo e o resultado gerado -2147483648.) O motivo desse comportamento o desempenho: a
aritmtica de nmeros inteiros uma operao comum em quase todos os programas e adicionar
a sobrecarga de verificar o estouro (overflow) em cada expresso de nmeros inteiros pode levar a
um desempenho muito pobre. Em muitos casos, o risco aceitvel porque voc sabe (ou espera!)
que seus valores in t nunca atinjam seus limites. Se no gostar dessa abordagem, voc pode ativar a
verificao de overflow.

Dica Voc pode ativar o desativar a verificao de overflow no Visual Studio 2010 definindo as
propriedades do projeto. No Solution Explorer, clique em SeuProjeto (onde SeuProjeto o nome de
seu projeto). No menu Project, clique em SeuProjeto Properties. Na caixa de dilogo de propriedades
do projeto, clique na guia Build. Clique no boto Advanced no canto inferior direito da pgina. Na
caixa de dilogo Advanced Build Settings, marque ou desmarque a caixa de seleo Check for arith
metic overflow/underflow.

Independentemente de como voc compila um aplicativo, possvel utilizar as palavras-chave che


cked e unchecked para ativar e desativar seletivamente a verificao de overflow aritmtico de intei
ros nas partes de um aplicativo que voc julgar necessrio. Essas palavras-chave redefinem a opo
de compilador especificada para o projeto.

Escrevendo instrues verificadas


Uma instruo verificada um bloco precedido por uma palavra-chave checked. Toda a aritmtica de
nmeros inteiros em uma instruo verificada sempre lana uma OverflowException se um clculo
de inteiros no bloco sofrer overflow, como mostrado neste exemplo:
in t number = int.M axVaiue;
checked

{
in t w ilIT h ro w = number++;
C o n s o le .W rite L in e ("th is w on't be re a ch e d "); // Esta lin h a no ser executada

Captulo 6

Gerenciando erros e excees

151

Im p o rta n te Somente a aritmtica de inteiros diretamente dentro do bloco checked est sujeita
verificao de overflow. Por exemplo, se uma das instrues verificadas for uma chamada de mto
do, a verificao no se aplica ao cdigo que executa no mtodo que chamado.

Voc tambm pode utilizar a palavra-chave unchecked para criar uma instruo de bloco no veri
ficad a. Toda a aritmtica de inteiros em um bloco unchecked no verificada e nunca lana uma
OvejflowException. Por exemplo:
in t number = int.M axValue;
unchecked

{
in t wontThrow = number++;
C o n s o le .W rite L in e ("th is w i l l be rea ch ed "); // Est lin h a ser executada

>

Escrevendo expresses verificadas


Voc tambm pode utilizar as palavras-chave checked e unchecked para controlar a verificao de
overflow em expresses de nmeros inteiros precedendo apenas a expresso entre parnteses com a
palavra-chave checked ou unchecked, como mostrado neste exemplo:
in t wontThrow = unchecked(int.M axV alue + 1 );
in t w illT h row = checked(int.M axValue + 1 );

Os operadores compostos (como += e -=) e os operadores de incremento, ++, e de decremento,


so operadores aritmticos e podem ser controlados utilizando as palavras-chave checked e
unchecked. Lembre-se de que, x += y ; o mesmo que x = x + y ;.

Voc no pode usar as palavras-chave checked e unchecked para controlar a arit


mtica de ponto flutuante (no inteiro). As palavras-chave checked e unchecked s se aplicam
aritmtica de inteiros utilizando tipos de dados como int e long. A aritmtica de ponto flutuante
nunca lana uma OverflowException - nem mesmo quando voc divide por 0.0. (O ,NET Framework
tem uma representao para infinito.)
Im p o rta n te

No prximo exerccio, voc ver como executar a aritmtica verificada quando utilizar o Visual Studio 2010.

Utilize expresses verificadas


1. Retorne ao Visual Studio 2010.
2. No menu Debug, clique em Start W ithout Debugging.
Agora, voc tentar multiplicar dois valores grandes.

152

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

3. Digite 9876543 na caixa de texto do operando esquerdo, digite 9876543 na caixa de texto do
operando direito, clique no boto M ultiplication e ento clique em Calculate.
O valor -1195595903 aparece na caixa de texto Result do formulrio. Esse um valor negati
vo, que no pode estar correto. Esse valor o resultado de uma operao de multiplicao que
silenciosamente excedeu o limite de 32 bits do tipo int.
4. Clique em Quit e retorne ao ambiente de programao do Visual Studio 2010.
5. Na janela Code and Text Ed itor exibindo MainWindow.xaml.es, localize o mtodo multiplyValues. Ele semelhante a este:
p riv a te in t m u ltip ly V a lu e s (in t leftH a nd Sid e, in t rightHandSide)

{
expression.Text - le ftH a n d S id e .T o S trin g O + " * " + rig h tH a n d S id e .T o S trin g O ;
return leftH and Side * rightHandSide;

}
A instruo return contm a operao de multiplicao que causa silenciosamente um overflow.
6. Edite a instruo return para que o valor de retorno seja verificado, desta maneira:
return checked(leftH andSide * rightH andSide);

A multiplicao agora verificada e lanar uma OvetflowException em vez de retornar silencio


samente a resposta errada.
7. Localize o mtodo calculateClick.
8. Adicione a rotina de tratamento catch a seguir imediatamente aps a rotina de tratamento catch
FormatException existente no mtodo calculateClick:
catch (O verflow Exception oEx)

{
r e s u lt.T e x t = oEx.Message;

(>

Dica A lgica dessa rotina de tratamento catch a mesma da rotina de tratamento catch
FormatException. Mas ainda vale a pena manter essas duas rotinas de tratamentos separadas
em vez de simplesmente escrever uma rotina de tratamento catch Exception genrica, porque
voc poderia decidir tratar essas excees de maneira diferente no futuro.

9. No menu Debug, clique em S tart W ithout Debugging para compilar e executar o aplicativo.
10. Digite 9876543 na caixa de texto do operando esquerdo, digite 9876543 na caixa de texto do
operando direito, clique no boto M ultiplication e ento clique em Calculate.
A segunda rotina de tratamento catch captura com sucesso a OvetflowException e exibe a men
sagem Arithmetic operation resulted in an overflow na caixa de texto Result.
11. Clique em Quit para retornar ao ambiente de programao do Visual Studio 2010.

Captulo 6 Gerenciando erros e excees

153

Lanando excees
Suponha que voc esteja implementando um mtodo chamado monthName que aceita um nico ar
gumento in t e retorna o nome do ms correspondente. Por exemplo, m onthNam e(l) retorna January,
monthName(2) retorna February e assim por diante. A pergunta : o que o mtodo deve retornar se
o argumento inteiro for menor que 1 ou maior que 12? A melhor resposta que o mtodo no deve
retornar coisa alguma, ele deve lanar uma exceo. As bibliotecas de classes do .NET Framework
contm uma grande quantidade de classes de exceo projetadas especificamente para situaes des
se tipo. Na maioria das vezes, voc achar que uma dessas classes descreve sua condio excepcional.
(Se no, voc pode facilmente criar sua prpria classe de exceo, mas precisa conhecer um pouco
mais da linguagem C# antes de poder fazer isso.) Nesse caso, a classtArgumentOutOfRangeException
existente no .NET Framework serve perfeitamente. Voc pode lanar uma exceo utilizando a instru
o throw, como mostrado no exemplo a seguir:
p u b lic s t a t i c s trin g monthName(int month)

{
switch (month)

{
case 1 :
return "Ja n u a ry ";
case 2 :
return "F e b ru a ry";
case 12 :
return "December";
d e fa u lt :
throw new ArgumentOutOfRangeException("Bad month");

}
}
A instruo throw precisa de uma exceo para ser lanada. Esse objeto contm os detalhes da ex
ceo, incluindo qualquer mensagem de erro. Esse exemplo utiliza uma expresso que cria um novo
objeto ArgumentOutOfRangeException. O objeto inicializado com uma string que preenche sua
propriedade Message, utilizando um construtor. Os construtores sero abordados detalhadamente
no Captulo 7, Criando e gerenciando classes e objetos .
Nos exerccios a seguir, voc modificar o projeto MathsOperators para lanar uma exceo, se o
usurio tentar efetuar um clculo sem especificar uma operao a ser executada.

Lance uma exceo


1. Retorne ao Visual Studio 2010.
2. No menu Debug, clique em Start W ithout Debugging.
3. Digite 24 na caixa de texto do operando esquerdo, digite 36 na caixa de texto do operando di
reito e ento clique em Calculate.
O valor 0 aparece na caixa de texto Result. O fato de voc no ter selecionado uma opo de
operador no bvio. Seria til escrever uma mensagem de diagnstico na caixa de texto
Result.

154

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

4. Clique em Quit para retornar ao ambiente de programao do Visual Studio 2010.


5. Na janela Code and Text Ed itor exibindo MainWindow.xaml.es, localize e examine o mtodo
doCalculation. Ele semelhante a este:
private int doCalculation(int leftHandSide, int rightHandSide) {
int result = 0;
i f (additio n.IsC hecked.H asV alu e && a d d itio n .IsC h eck ed .V alu e )
r e s u lt = ad d V alu es(leftH an dSide, rightH andSide);
e ls e i f (sub traction .IsC heck ed .H asV alue && su b tractio n .IsC he ck e d .V alu e )
r e s u lt = su b tra c tV a lu e s(le ftH a n d Sid e , rightH andSide);
e ls e i f (m u ltip lic a tio n .IsC h e c k e d .H a sV a lu e && m u ltip lic a tio n .Is C h e c k e d .V a lu e )
r e s u lt = mui t i plyValues (le ftH a n d S id e , rightH andSide);
e ls e i f (d ivisio n .IsC h eck ed .H a sV alu e && d iv isio n .IsC h e c k e d .V a lu e )
re s u lt = d iv id e V a lu e s(le ftH a n d S id e , rightH an dSide);
e ls e i f (rem ainder.IsChecked.HasValue && rem ainder.IsChecked.Value)
re s u lt = rem ainderValuesO eftH andSide, rightH andSide);
return result;

}
Os campos addition, subtraction, m ultiplication, division e remainders so os botes de opo que
aparecem no formulrio. Cada boto tem uma propriedade chamada IsChecked que indica se o usu
rio a selecionou. A propriedade IsChecked um exemplo de um valor nullable, ou seja, ela pode
conter um valor especfico ou estar em um estado indefinido. (Discutiremos outros detalhes sobre
valores nullable no Captulo 8, Entendendo valores e referncias.) A propriedade IsChecked.HasValue indica se o boto est em um estado definido, e, se estiver, a propriedade IsChecked.Value
indica qual esse estado. A propriedade IsChecked. Va/ue uma booleana que tem o valor true se o
boto estiver selecionado oufa lse caso contrrio. A instruoiftm cascata examina cada boto para
descobrir qual deles est selecionado. (Os botes de opo so mutuamente exclusivos, portanto, o
usurio pode selecionar no mximo um boto de opo.) Se nenhum dos botes estiver selecionado,
nenhuma das instrues if ser verdadeira e a varivel result permanecer com seu valor inicial (0).
Essa varivel armazena o valor que retornado pelo mtodo.
Voc pode tentar resolver o problema adicionando mais uma instruo else cascata if-else, para es
crever uma mensagem na caixa de texto result do formulrio. Mas essa soluo no uma boa ideia
porque o verdadeiro objetivo desse mtodo no emitir mensagens. melhor separar a deteco e a
sinalizao de um erro da captura e tratamento desse erro.
6. Adicione outra instruo else lista de instrues if-else (imediatamente antes da instruo
return) e lance uma InvalidOperationException exatamente como mostrado a seguir:
else
throw new InvalidOperationException("No operator selected");

7. No menu Debug, clique em Start W ithout Debugging para compilar e executar o aplicativo.
8. Digite 24 na caixa de texto do operando esquerdo, digite 36 na caixa de texto do operando di
reito e ento clique em Calculate.

Captulo 6

Gerenciando erros e excees

155

O Windows detecta que seu aplicativo lanou uma exceo e (finalmente) exibida uma caixa
de dilogo de exceo. O aplicativo lanou uma exceo, mas seu cdigo no a captura ainda.
9. Clique em Closeprogram.
O aplicativo termina e voc retorna ao Visual Studio 2010.
Agora que escreveu uma instruo throw e verificou que ela lana uma exceo, voc escrever uma
rotina de tratamento catch para capturar essa exceo.

Capture a exceo
1. Na janela Code and Text Editor exibindo MainWindow.xaml.es, localize o mtodo calculateClick.
2. Adicione a rotina de tratamento catch a seguir imediatamente abaixo das duas rotinas de trata
mento catch existentes no mtodo calculateClick
.
catch ( In valid O p e ra tio n Ex cep tio n ioEx )

{
re s u lt.T e x t = ioEx.Message;

}
Esse cdigo captura a InvalidOperationException que lanada quando nenhum boto de ope
rador est selecionado.
3. No menu Debug, clique em Start W ithout Debugging.
4. Digite 24 na caixa de texto do operando esquerdo, digite 36 na caixa de texto do operando di
reito e ento clique em Calculate.
A mensagem no operator selected aparece na caixa de texto Result.
5. Clique em Quit.
O aplicativo agora est mais robusto do que antes. Mas ainda podem surgir vrias excees que no
sero capturadas e que podero provocar a falha do aplicativo. Por exemplo, se voc tentar dividir
por 0, uma DivideByZeroException no tratada ser lanada. (Diviso de inteiro por 0 lana uma ex
ceo, diferentemente da diviso de ponto flutuante por 0.) Uma maneira de resolver isso escrever
um nmero ainda maior de rotinas de tratamento catch dentro do mtodo calculateClick. Mas uma
soluo melhor adicionar uma rotina de tratamento catch geral que capture uma Exception, no
final da lista de rotinas de tratamento catch. Isso capturar todas as excees no tratadas.

Dica A deciso de capturar explicitamente todas as excees no tratadas em um mtodo depen


der da natureza do aplicativo que voc est compilando. Em alguns casos, faz sentido capturar
excees o mais prximo possvel do ponto em que elas ocorrem. Em outras situaes, mais til
deixar que uma exceo se propague de volta ao mtodo que invocou a rotina e lanou a exceo
e trate do erro ali.

156

Partei

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Capture excees no tratadas


1. Na janela Codeand TextEditor exibindo Windowl.xaml.es, localize o mtodo calculateClick.
2. Adicione a rotina de tratamento catch a seguir ao final da lista de rotinas de tratamento catch
existentes:
catch (Exception ex)

{
re s u lt.T e x t = ex.Message;

}
Essa rotina de tratamento catch ir capturar todas as excees no tratadas at aqui, qualquer
que seja seu tipo especfico.
3. No menu Debug, clique em Start W ithout Debugging.
Voc agora vai tentar realizar-alguns clculos conhecidos por provocar excees e confirmar que
elas sejam tratadas corretamente.
4. Digite 24 na caixa de texto do operando esquerdo, digite 36 na caixa de texto do operando di
reito e ento clique em Calculate.
Confirme que a mensagem de diagnstico no operator selected ainda exibida na caixa de
texto Result. Essa mensagem foi gerada pela rotina de tratamento InvalidOperationException.
5. Digite John na caixa de texto do operando esquerdo e clique em Calculate.
Confirme que a mensagem de diagnstico Input string was not in a correct format exibida
na caixa de texto Result. Essa mensagem foi gerada pela rotina de tratamento FormatException.
6. Digite 24 na caixa de texto do operando esquerdo, digite 0 na caixa de texto do operando direi
to, clique no boto D ivision e, em seguida, em Calculate.
Confirme que a mensagem de diagnstico Attempted to divide by zero exibida na caixa de
texto Result. Essa mensagem foi gerada pela rotina de tratamento Exception geral.
7. Clique em Quit.

Utilizando um bloco finally


importante lembrar que, quando uma exceo lanada, ela altera o fluxo da execuo no progra
ma. Isso significa que voc no pode garantir que uma instruo ser sempre executada quando a
instruo anterior terminar, porque a instruo anterior poder lanar uma exceo. Veja o exemplo
a seguir. muito fcil assumir que a chamada ao reader.Close sempre ocorrer quando o loop while
terminar. Afinal de contas, o que est no cdigo:
TextReader reader = src.OpenTextO ;
s trin g lin e ;
w h ile ( ( l i n e = re a d e r.R e ad Lin eO ) != n u ll)

{
so u rce.Text += lin e + "\ n ";

}
re a d e r.C lo seO ;

Algumas vezes, o fato de uma instruo especfica no ser executada no problema, mas em mui
tas ocasies isso pode ser um grande problema. Se a instruo libera um recurso que foi adquirido

Captulo 6

Gerenciando erros e excees

157

em uma instruo anterior, ento a falha na execuo dessa instruo resultar na reteno do
recurso. Este exemplo precisamente o caso: se a chamada ao src.OpenText for bem-sucedida, ento
ela adquire um recurso (um handle de arquivo) e voc deve garantir a chamada de reader.Close para
liberar o recurso. Se voc no fizer, cedo ou tarde voc no ter handles de arquivos suficientes e ser
incapaz de abrir mais arquivos. (Se achar handles de arquivos muito triviais, pense, em vez disso,
nas conexes de banco de dados.)
A maneira de garantir que uma instruo seja sempre executada, quer uma exceo seja ou no
lanada, escrever essa instruo em um blocofin a lly . Um blocofin a lly ocorre imediatamente aps
um bloco try ou imediatamente aps a ltima rotina de tratamento catch, depois de um bloco try.
Desde que o programa entre no bloco try associado a um blocofin a lly , o blocofin a lly sempre ser
executado, mesmo que uma exceo ocorra. Se uma exceo for lanada e capturada localmente, a
rotina de tratamento de exceo ser executada primeiro, seguida pelo blocofin a lly. Se a exceo no
for capturada localmente (ou seja, o runtime precisar pesquisar a lista de mtodos de chamada para
descobrir uma rotina de tratamento), o blocofin a lly ser executado primeiro. Em qualquer caso, o
blocofin a lly sempre executado.
A soluo para o problema da instruo reader.Close a seguinte:
TextReader reader = n u l l ;
try

{
reader = s r c . O p e n T e x t O ;
string Tine;
w h ile ( ( l i n e re a d e r.R e a d L in e O ) != n u ll)

{
source.Text += lin e + "\ n ";

}
>
f i n a l1y

{
i f (read er != n u ll)

{
re a d e r.C lo s e O ;

}
}
Mesmo que uma exceo seja lanada, o blocofin a lly garante que a instruo reader.Close sempre
seja executada. Voc ver outra maneira de resolver esse problema no Captulo 14, Utilizando a
coleta de lixo e o gerenciamento de recursos .
Neste captulo, voc aprendeu a capturar e tratar excees por meio das construes try e catch. Voc
viu como possvel habilitar e desabilitar a verificao de estouro de inteiros por meio das palavras-chave checked e unchecked. Voc aprendeu a lanar uma exceo se seu cdigo detectar uma situa
o excepcional, e examinou como utilizar um blocofin a lly para garantir que o cdigo crucial seja
executado, mesmo se ocorrer uma exceo.
Se voc quiser seguir para o prximo captulo agora:
Mantenha o Visual Studio 2010 em execuo e v para o Captulo 7.
Se quiser sair do Visual Studio 2010:
No menu File, clique em Exit. Se vir uma caixa de dilogo Save, clique em Yes e salve o projeto.

158

Parte I

Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010

Referncia rpida do Captulo 6


Para

Faa isto

Capturar uma exceo especfica

Escreva uma rotina e tratamento catch que capture a


classe de exceo especfica. Por exemplo:
Try
{
}

catchCFormatException fEx)
{
}

Garantir que a aritmtica de inteiros seja sempre verifica


da quanto a estouros

Use a palavra-chave checked. Por exemplo:


in t number = Int32.MaxVa1ue;
checked
{

number++;
}
Lanar uma exceo

Utilize uma instruo throw. Por exemplo:


throw new FormatException(source);

Capturar todas as excees em uma nica rotina de trata


mento catch

Escreva uma rotina de tratamento catch que captura Ex


ception. Por exemplo:
try
{
}
catch (Exception ex)
{
}

Garantir que algum cdigo sempre seja executado, mesmo se uma exceo for lanada

Escreva o cdigo dentro de um bloco finally. Por exemplo:


{
}
f i n a lly

{
// sempre executa

Microsoft Visual C # 2010 Passo a Passo

Parte II

Entendendo a linguagem C#
Captulo 7:

Criando e gerenciando classes e objetos.............................. 161

Captulo 8:

Entendendo valores e referncias........................................

Captulo 9:

Criando tipos-valor com enumeraes e estruturas............ 205

183

Captulo 10: Utilizando arrays e colees.................................................. 223


C ap tu lo H : Entendendo arrays de parm etros...................................... 251
Captulo 12: Trabalhando com herana.................................................... 263
Captulo 13: Criando interfaces e definindo classes abstratas.................. 285
Captulo 14: Utilizando a coleta de lixo e o gerenciamento de recursos. . 311

Captulo 7

Criando e gerenciando
classes e objetos
Neste captulo, voc vai aprender a:
Definir uma classe contendo um conjunto de mtodos e itens de dados relacionados.
Controlar a acessibilidade de membros utilizando as palavras-chave p u b lic e p rivate.
Criar objetos utilizando a palavra-chave n e w para invocar um construtor.
Escrever e chamar seus prprios construtores.
Criar mtodos e dados que podem ser compartilhados por todas as instncias da mesma
classe utilizando a palavra-chave static.
Explicar como criar classes annimas.

Na Parte I, Apresentando o Microsoft Visual C# e o Microsoft Visual Studio 2010, voc aprendeu a
declarar variveis, utilizar operadores para criar valores, chamar mtodos e escrever muitas das ins
trues necessrias para implementar um mtodo. Agora, voc j sabe o suficiente para prosseguir
para a prxima etapa - combinar mtodos e dados nas suas classes.
O Microsoft .NET Framework contm milhares de classes, e voc j usou vrias delas, inclusive
Console e Exception. As classes fornecem um mecanismo conveniente para modelar as entidades
manipuladas pelos aplicativos. Uma entidade pode representar um item especfico, como um clien
te, ou algo mais abstrato, como uma transao. Parte do processo do projeto de qualquer sistema
est relacionado determinao das entidades importantes para os processos implementados pelo
sistema e execuo de uma anlise para ver que informaes essas entidades precisam armazenar
e que operaes elas devem executar. Voc armazena as informaes contidas por uma classe como
campos e utiliza mtodos para implementar as operaes que uma classe pode realizar.
Os captulos da Parte II, Entendendo a linguagem C# , fornecem tudo o que voc precisa saber para
criar suas classes.

Entendendo a classificao
Classe a raiz da palavra classificao. Ao projetar uma classe, voc sistematicamente organiza as
informaes e o comportamento em uma entidade com significado. Essa organizao um ato de
classificao e algo que todos fazem - no apenas os programadores. Por exemplo, todos os carros
compartilham comportamentos comuns (eles podem ser dirigidos, parados, acelerados etc.) e atribu
tos comuns (eles tm um volante, um motor etc.). As pessoas utilizam a palavra carro para significar

162

Parte II

Entendendo a linguagem C#

objetos que compartilham esses comportamentos e atributos comuns. Desde que todos concordem
com o que a palavra significa, esse sistema funcionar bem e voc pode expressar ideias complexas,
mas precisas, de maneira concisa. Sem a classificao, difcil imaginar como as pessoas poderiam
pensar ou se comunicar.
Como a classificao est profundamente arraigada na maneira como pensamos e nos comunica
mos, faz sentido tentar escrever programas classificando os diferentes conceitos inerentes a um
problema e sua soluo e ento modelar essas classes em uma linguagem de programao. Isso
exatamente o que voc pode fazer com linguagens modernas de programao orientada a objetos,
como o Microsoft Visual C#.

O objetivo do encapsulamento
O encapsulamento um princpio importante durante a definio de classes. A ideia que um
programa que utiliza uma classe no precisa se preocupar com o modo como essa classe realmente
funciona internamente; o programa simplesmente cria uma instncia de uma classe e chama os
mtodos dessa classe. Desde que esses mtodos faam o que se propem a fazer, o programa no se
preocupa com a maneira como eles so implementados. Por exemplo, ao chamar o mtodo Console.
W riteLine, voc no quer se incomodar com todos os detalhes complicados de como a classe Console
organiza fisicamente os dados a serem escritos na tela. Uma classe talvez precise manter todos os
tipos de informaes internas para executar seus vrios mtodos. Essas atividades e informaes
de estado adicionais so ocultas do programa que est utilizando a classe. Portanto, o encapsula
mento s vezes chamado de ocultamento de informao. O encapsulamento na realidade tem dois
objetivos:
Combinar os mtodos e dados dentro de uma classe; ou seja, dar suporte classificao.
Controlar a acessibilidade de mtodos e dados; ou seja, controlar o uso da classe.

Definindo e utilizando uma classe


No C#, voc utiliza a palavra-chave class para definir uma nova classe. Os dados e os mtodos da
classe ocorrem no corpo da classe entre um par de chaves. Veja uma classe do C# chamada Circle que
contm um mtodo (para calcular a rea do crculo) e uma parte de dados (o raio do crculo):
c la s s C ir c le

{
in t rad iu s;
double A reaO

{
return M ath.PI * radius * rad iu s;

}
}

Captulo 7

Criando e gerenciando classes e objetos

163

Nota A classe Math contm mtodos para realizar clculos matemticos e campos contendo
constantes matemticas. O campo Math.PI engloba o valor 3.141 59265358979323846, que uma
aproximao do valor de pi.

0 corpo de uma classe contm mtodos comuns (como Area) e campos (como radius) - lembre-se
de que as variveis em uma classe so chamadas de campos. Voc j viu como declarar variveis
no Captulo 2, Trabalhando com variveis, operadores e expresses , e como escrever mtodos no
Captulo 3, Escrevendo mtodos e aplicando o escopo , de modo que no h quase sintaxe nova
aqui.
Voc pode utilizar a classe Circle de modo semelhante para usar os outros tipos j encontrados; voc
cria uma varivel especificando Circle como seu tipo e inicializa a varivel com algum dado vlido.
Observe um exemplo:
C ir c le c;
c = new C ir c l e O ;

// C ria uma v a r i v e l C ir c le
// I n i c i a l i z a a v a ri v e l

Um aspecto que merece destaque nesse cdigo o uso da palavra-chave new. Anteriormente, ao inicializar uma varivel como in t oujloat, voc simplesmente atribuiu um valor a ela:
1nt i ;

i =42;
Voc no pode fazer o mesmo com variveis do tipo classe. Uma razo que o C# no fornece uma
sintaxe para atribuir valores literais de classe s variveis. Voc no pode escrever uma instruo
como esta:
C ir c le c;
c = 42;

Acima de tudo, o que significaria um Circle igual a 42? Outra razo diz respeito maneira como
a memria para variveis do tipo classe alocada e gerenciada pelo runtime - isso discutido em
mais detalhes no Captulo 8, Entendendo valores e referncias. Por enquanto, basta aceitar que a
palavra-chave new cria uma nova instncia de uma classe, mais chamada de objeto.
Mas voc pode atribuir diretamente uma instncia de uma classe a uma outra varivel do mesmo
tipo, assim:
C ir c le c;
c = new Ci r c le O ;
Ci rc le d;
d = c;

Mas isso no to simples e direto quanto parece ser primeira vista, por razes que abordaremos
no Captulo 8.

164

Parte II

Entendendo a linguagem C#

Importante No confunda os termos classe e objeto. Uma classe a definio de um tipo. Um


objeto uma instncia desse tipo, criada quando o programa executado.

Controlando a acessibilidade
Surpreendentemente, a classe Circle no tem, atualmente, qualquer utilidade prtica. Ouando voc
encapsula seus mtodos e dados dentro de uma classe, a classe forma um limite para o mundo ex
terno. Campos (como radius) e mtodos (como Area) definidos na classe podem ser vistos por outros
mtodos dentro da classe, mas no pelo mundo externo - eles so privados para a classe. Em outras
palavras, embora se possa criar um objeto Circle em um programa, no se pode acessar seu campo
radius ou chamar seu mtodo Area, razo pela qual a classe no muito til - ainda! Mas voc pode
modificar a definio de um campo ou mtodo com as palavras-chave public e private para controlar
se ele pode ou no ser acessado de fora:
Dizemos que um mtodo ou campo privado se ele acessvel somente a partir de dentro da
classe. Para declarar que um mtodo ou campo privado, voc escreve a palavra-chave private
antes da sua declarao. Esse de fato o padro, mas uma boa prtica determinar explicita
mente que campos e mtodos so privados para evitar qualquer confuso.
Dizemos que um mtodo ou campo pblico se ele acessvel tanto de dentro quanto de fora
da classe. Para declarar que um mtodo ou campo pblico, voc escreve a palavra-chave public
antes da sua declarao.
Veja a classe Circle novamente. Desta vtz,A rea declarada como um mtodo pblico e radius de
clarado como um campo privado:
c la s s C ir c le

{
p riv a te in t rad iu s;
p u b lic double A re a ()

{
return M ath.PI * radius * rad iu s;

Embora radius seja declarado como um campo privado e no esteja acessvel fora da classe, radius
estar acessvel a partir de dentro da classe Circle. O mtodo Area est dentro da classe Circle, portan
to, o corpo At Area tem acesso a radius. Entretanto, a classe ainda de valor limitado, pois no h
como inicializar o campo radius. Para corrigir isso, voc utiliza um construtor.

Captulo 7

Criando e gerenciando classes e objetos

165

Trabalhando com construtores


Quando voc utiliza a palavra-chave new para criar um objeto, o runtime tem de construir esse
objeto utilizando a definio da classe. O runtime tem de se apropriar de uma parte da memria do
sistema operacional, preench-la com os campos definidos pela classe e ento invocar o construtor
para executar qualquer inicializao necessria.
Um construtor um mtodo especial que se executa automaticamente quando voc cria uma ins
tncia de uma classe. Ele tem o mesmo nome da classe e pode receber parmetros, mas no pode
retornar um valor (nem mesmo void). Toda classe deve ter um construtor. Se voc no escrever um,
o compilador ir gerar automaticamente um construtor padro para voc. (Mas o construtor padro
gerado pelo compilador na realidade no faz coisa alguma.) Voc pode escrever seu prprio constru
tor padro de forma muito fcil - basta adicionar um mtodo pblico, com o mesmo nome da classe,

166

Parte II

Entendendo a linguagem C#

que no retorna um valor. O exemplo a seguir mostra a classe Circle com um construtor padro que
inicializa o campo radius como 0:
class Circle
{
p riv a te in t radius;
p u b lic C ir c le O

// c o n struto r padro

{
radius = 0;

}
p u b lic double A re a ()

{
return M ath .PI * radius * ra d iu s;

Nesse exemplo, o construtor est marcado como public. Se essa palavra-chave for omitida, o constru
tor ser privado (exatamente como qualquer outro mtodo e campo). Se o construtor for privado, ele
no poder ser utilizado fora da classe, o que lhe impede de criar objetos Circle a partir dos mtodos
que no fazem parte da classe Circle. Mas voc poder achar que os construtores privados no so
to valiosos. Entretanto, eles realmente tm suas utilidades, mas estas esto alm do escopo da
discusso atual.
Voc agora pode utilizar a classe Circle e exercitar seu mtodo Area. Observe como voc utiliza a no
tao de ponto para chamar o mtodo Area em um objeto Circle-,
Ci rcle c;
c = new Ci rcle O ;
double areaOfCircle = c.AreaO;

Sobrecarregando construtores
Voc quase j terminou, s falta um detalhe. Agora voc pode declarar uma varivel Circle, apont-la
para um objeto Circle recm-criado e ento chamar seu mtodo Area. Mas ainda existe um ltimo
problema. A rea de todos os objetos Circle sempre ser 0 porque o construtor padro define o raio
como 0 e ele permanece em 0; o campo radius privado e no h como alterar seu valor depois que
ele inicializado. Entretanto, entenda que um construtor apenas um tipo especial de mtodo e

Captulo 7

Criando e gerenciando classes e objetos

167

que ele - como todos os mtodos - pode ser sobrecarregado. Assim como existem vrias verses do
mtodo Console. WriteLine, e cada uma das quais recebe parmetros diferentes, possvel tambm
escrever diferentes verses de um construtor. Voc pode adicionar um construtor classe Circle, com
o raio como seu parmetro, como este:
c la s s C ir c le

{
p riv a te in t rad iu s;
p u b lic C ir c le O

// co n stru to r padro

{
radius = 0;

}
p u b lic C ir c le C in t in it ia lR a d iu s ) // c o n stru to r sobrecarregado

{
radius = in it ia lR a d iu s ;

}
p u b lic double A reaO

{
return M ath.PI * radius * rad ius;

}
}

Voc pode ento utilizar esse construtor ao criar um novo objeto Circle, como mostrado aqui:
C ir c le c;
c = new Ci r c le ( 4 5 ) ;

Ouando voc compila o aplicativo, o compilador deduz qual construtor ele deve chamar com base
nos parmetros que voc especifica para o operador new. Neste exemplo, voc passou um int, portan
to, o compilador gera o cdigo que invoca o construtor que recebe um parmetro int.
Voc deve estar ciente de uma peculiaridade da linguagem C#: se voc escrever um construtor para
uma classe, o compilador no ir gerar um construtor padro. Portanto, se voc escreveu um cons
trutor que aceita um ou mais parmetros e tambm quiser um construtor padro, voc mesmo ter
de escrever o construtor padro.

168

Parte II

Entendendo a linguagem C#

Classes parciais
Uma classe pode conter vrios mtodos, campos e construtores, assim como outros itens
discutidos nos prximos captulos. Uma classe altamente funcional pode tornar-se muito
grande. Com o C#, possvel dividir o cdigo-fonte para uma classe em arquivos separados
de modo que voc possa organizar a definio de uma classe grande em partes menores
mais fceis de gerenciar. Esse recurso usado pelo Microsoft Visual Studio 2010 para aplica
tivos Windows Presentation Foundation (WPF), em que o cdigo-fonte que o desenvolvedor
pode editar mantido em um arquivo separado do cdigo que gerado pelo Visual Studio
sempre que o layout de um formulrio for alterado.
Ao dividir uma classe em mltiplos arquivos, voc define as partes da classe usando a palavra-chave partial em cada arquivo. Por exemplo, se a classe Circle fosse dividida entre dois
arquivos chamados circi .cs (contendo os construtores) e circ2.cs (contendo os mtodos e
campos), o contedo de circi .cs seria este:
p a r t ia l c la s s C ir c le

{
p u b lic C i r d e O

// co n stru to r padro

{
t h is .r a d iu s = 0;

}
p u b lic C ir c Ie C in t in it ia lR a d iu s ) // c o n struto r sobrecarregado

{
t h is . radius = in it ia lR a d iu s ;

}
}
O contedo de circ2.cs seria semelhante a este:
p a r t ia l c la s s C ir c le

{
p riv a te in t rad ius;
p u b lic double A reaO

{
return M ath.PI * t h is .r a d iu s * t h is .r a d iu s ;

}
}
Ao compilar uma classe que foi dividida em arquivos separados, voc deve fornecer todos os
arquivos para o compilador.

Nota

possvel definir as interfaces parciais e as estruturas do mesmo jeito.

Captulo 7

Criando e gerenciando classes e objetos

169

No prximo exerccio, voc ir declarar uma classe que modela um ponto no espao bidimensional.
Essa classe conter dois campos privados que contm as coordenadas x e y de um ponto e fornecer
os construtores para inicializar esses campos. Voc criar instncias da classe usando a palavra-chave new e chamando os construtores.

Escreva os construtores e crie os objetos


1. Inicie o Visual Studio 2010 se ele ainda no estiver em execuo.
2. Abra o projeto Classes, localizado na pasta \Microsoft PressWisual CSharp Step by Step\Chapter7\Classes na sua pasta Documentos.
3. No Solution Explorer, clique duas vezes no arquivo Program.cs para exibi-lo na janela Code and
Text Editor.
4. Localize o mtodo M ain na classe Program.
O mtodo M ain chama o mtodo DoWork, inserido dentro de um bloco try e seguido por uma
rotina de tratamento catch. Com esse bloco try/catch, voc pode escrever no mtodo DoWork o
cdigo que em geral entraria em Main, tendo a certeza de que ele ir capturar e tratar qualquer
exceo.
5. Exiba o arquivo Point.cs na janela Code and Text Editor.
Esse arquivo define uma classe chamada Point, que voc utilizar para representar a localizao
de um ponto definido por um par de coordenadas x ey. No momento, a classe Point est vazia.
6. Retorne ao arquivo Program.cs e localize o mtodo DoWork da classe Program . Edite o corpo do
mtodo DoWork e substitua o comentrio // to do pela instruo a seguir:
Po in t o rig in = new P o in tO ;

7. No menu Build, clique em Bui/d Solution.


O cdigo compilado sem erro porque o compilador gera o cdigo para um construtor padro
para a classe Point. Mas voc no pode ver o cdigo C# desse construtor porque o compilador
no gera qualquer instruo na linguagem fonte.
8. Retorne classe Point no arquivo Point.cs. Substitua o comentrio // to do por um construtor
public que aceita dois argumentos in t chamados x e y e que chamam o mtodo Console. WriteLine para exibir os valores desses argumentos no console, como mostrado no texto em negrito no
exemplo de cdigo seguinte. A classe Point deve ser semelhante a esta:
class Point
{
public Point(int x, int y)

{
Console.WriteLine("x:{0}, y:{l}"p x, y ) ;

}
}

170

Parte II

Entendendo a linguagem C#

9. No menu Build, clique em Build Solution.


O compilador agora informa um erro:
'C la s s e s . P o in t' does not contain a co n stru cto r th a t takes '0 ' arguments

A chamada ao construtor padro em DoWork no funciona porque no h mais um construtor


padro. Voc escreveu seu prprio construtor para a classe Point, assim o compilador no ir
mais gerar o construtor padro. Agora voc corrigir isso escrevendo seu prprio construtor
padro.
10. Edite a classe Point e adicione um construtor padro public que chama Console. WriteLine para
escrever a string default constructor called no console, como mostrado no texto em negrito no
exemplo de cdigo a seguir. A classe Point deve ser semelhante a esta:
c la s s Point

{
public PointO

{
Console.WriteLine("Default constructor called");

}
p u b lic P o in tC in t x, in t y )

{
C o n s o le .W rite L in e ("x :{ 0 } , y : { l } " ,

x, y ) ;

11. No menu Build, clique em Build Solution.


O programa agora deve ser compilado com sucesso.
12. No arquivo Program.cs, edite o corpo do mtodo DoWork. Declare uma varivel chamada bottom Right do tipo Point e inicialize-a como um novo objeto Point utilizando o construtor com
dois argumentos, como mostrado no texto em negrito no cdigo a seguir. Fornea os valores
1024 e 1280, que representam as coordenadas do canto inferior direito da tela com base na
resoluo 1024 x 1280. O mtodo DoWork agora deve se parecer com o exemplo seguinte:
s t a t i c void DoWorkQ

{
P o in t o rig in = new P o in t ( );
Point bottomRight = new Point(1024, 1280);

>

Captulo 7

Criando e gerenciando classes e objetos

171

13. No menu Debug, clique em Start W ithout Debugging.


0 programa compila e executa, escrevendo a seguinte mensagem no console:
D efau lt c o n stru cto r c a lle d
x:1024, y:1280

14. Pressione a tecla Enter para finalizar o programa e retornar ao Visual Studio 2010.
Agora voc adicionar dois campos in t classe Point para representar as coordenadas x ey de
um ponto e modificar os construtores para inicializar esses campos.
15. Edite a classe Point no arquivo Point.cs e adicione dois campos de instncia private chamados
x e y do tipo int, como mostrado no texto em negrito no cdigo a seguir. A classe Point deve ser
semelhante a esta:
c la s s Po in t

{
private int x, y;
p u b lic P o in tO

{
C o n s o le .W rite L in e ("d e fa u lt co n stru cto r c a ll e d " ) ;

}
p u b lic P o in tC in t x, in t y)

{
C o n s o le .W rite L in e ("x :{ 0 } , y : { l } " ,

x, y ) ;

}
}
Agora, voc editar o segundo construtor Point para inicializar os campos x e y para os valores
dos parmetros x ey. H uma armadilha potencial quando voc faz isso. Se voc no tiver cui
dado, o construtor ficar igual a este:
p u b lic P o in t ( in t x, in t y ) // No d ig it e is s o !

{
x = x;

y = y;
}
Embora o cdigo seja compilado, essas instrues parecem ambguas. Como o compilador sabe
que na instruo x = x-, o primeiro x o campo e o segundo x o parmetro? A resposta que
ele no sabe! Um parmetro de mtodo com o mesmo nome do campo oculta o campo para
todas as instrues no mtodo. Tudo o que esse cdigo realmente faz atribuir os parmetros a
eles mesmos; ele no modifica os campos. Isso exatamente o que no queremos.
A soluo utilizar a palavra-chave this para qualificar quais variveis so parmetros e quais
so campos. Colocar o prefixo this na varivel significa o campo neste objeto .

172

Parte II

Entendendo a linguagem C#

16. Modifique o construtor Point que recebe dois parmetros e substitua a instruo Console. WriteLine pelo seguinte cdigo mostrado em negrito:
p u b lic P o in t ( in t x, in t y)

{
this.x = x;
this.y = y;

}
17. Edite o construtor Point padro para inicializar os campos x e y em -1, como no texto em negri
to. Observe que, embora no haja parmetros para causar confuso, ainda uma boa prtica
qualificar as referncias de campo com this-.
p u b lic P o in tO

{
this.x = -1;
this.y = -1;

}
18. No menu Bui/d, clique em Bu ild Solution. Confirme se o cdigo compila sem erros ou alertas.
(Voc pode execut-lo, mas ele ainda no produz sada.)
Os mtodos que pertencem a uma classe e que operam em dados que pertencem a uma instncia
especfica de uma classe so chamados mtodos de instncia. (H outros tipos de mtodos que voc
encontrar mais adiante neste captulo.) No exerccio a seguir, voc escrever um mtodo de instn
cia para a classe Point, chamado DistanceTo, que calcula a distncia entre dois pontos.

Escreva e chame mtodos de instncia


1. No projeto Classes no Visual Studio 2010, adicione o mtodo de instncia pblico a seguir cha
mado DistanceTo classe Point depois dos construtores. O mtodo aceita um nico argumento
Point chamado other e retorna um double.
O mtodo DistanceTo deve ser semelhante a este:
c la s s Po in t

p u b lic double D istan ceT o (Po in t o th er)

{
}
}
Nos prximos passos, voc adicionar cdigo ao corpo do mtodo de instncia DistanceTo para
calcular e retornar a distncia entre o objeto Point que est sendo utilizado para fazer a chama
da e o objeto Point passado como um parmetro. Para fazer isso, voc deve calcular a diferena
entre as coordenadas x e as coordenadasj.

Captulo 7

Criando e gerenciando classes e objetos

173

2. No mtodo DistanceTo, declare uma varivel local in t chamada xD iffz inicialize-a com a diferen
a entre this.x e other.x, como mostrado aqui em negrito:
p u b lic double D istan ceT o(Poin t o th er)

{
int xDiff = this.x - other.x;

}
3. Declare outra varivel in t local chamaday D iff t inicialize-a com diferena entre this.y e other.y,
como mostrado aqui no texto em negrito:
p u b lic double D istan ceT o(Po in t o th er)

{
in t x D iff = t h is .x - o th e r.x ;
int yDiff = this.y - other.y;

}
Para calcular a distncia, utilize o teorema de Pitgoras e calcule a raiz quadrada da soma dos
quadrados dt xD iff tyD iff. A classe System .Math fornece o mtodo Sqrt que voc pode utilizar
para calcular razes quadradas.
4. Adicione a instruo return mostrada no texto em negrito no seguinte cdigo ao final do mtodo
DistanceTo para realizar o clculo:
p u b lic double D istan ceT o(Poin t o th er)

{
in t x D iff = t h is .x - o th e r.x ;
in t y D if f = t h i s . y - o th e r.y ;
return Math.Sqrt((xDiff * xDiff) + (yDiff * yDiff));

}
Agora voc testar o mtodo DistanceTo.
5. Retorne ao mtodo DoWork na classe Program . Aps as instrues que declaram e inicializam
as variveis origin e bottomRight Point, declare uma varivel chamada distance do tipo double.
Inicialize essa varivel double com o resultado obtido pela chamada ao mtodo DistanceTo no
objeto origin, passando o objeto bottomRight para ele como um argumento.
O mtodo DoWork deve agora ser semelhante a este:
s t a t i c void DoWorkO

{
Po in t o rig in = new P o in tO ;
Po in t bottomRight = new P o in t(1024, 1280);
double distance = origin.DistanceTo(bottomRight);

Nota O Microsoft IntelliSense deve exibir o mtodo DistanceTo quando voc digitar o carac
tere de ponto aps origin.

174

Parte II

Entendendo a linguagem C#

6. Adicione ao mtodo DoWork outra instruo que escreve o valor da varivel distance no console
utilizando o mtodo Console. WriteLine.
O mtodo DoWork deve ser semelhante a este:
s t a t i c void DoWorkO

{
P o in t o rig in = new P o in tO ;
P o in t bottomRight = new Point(1024, 1280);
double d ista n ce = o rig in .D ista n c e T o (b o tto m R ig h t);
Console.WriteLineC'Distance is: {0}", distance);

}
7. No menu Debug, clique em Start W ithout Debugging.
8. Confirme se o valor 1640.60537607311 escrito na janela de console.
9. Pressione Enter para fechar o aplicativo e retornar ao Visual Studio 2010.

Entendendo dados e mtodos static


No exerccio anterior, voc utilizou o mtodo Sq rt da classe M ath; da mesma forma, ao examinar a
classe Circle, voc leu o campo P I da classe M ath. Se pensar sobre isso, a maneira como voc chamou
o mtodo Sqrt ou leu o campo P I foi um pouco estranha. Voc chamou o mtodo e leu o campo na
prpria classe, no em um objeto do tipo M ath. como tentar escrever Point.DistanceTo em vez de
origin.DistanceTo no cdigo que voc adicionou no exerccio anterior. Portanto, o que est acontecen
do e como isso funciona?
Voc notar com frequncia que nem todos os mtodos pertencem a uma instncia de uma classe;
eles so mtodos utilitrios, pois fornecem uma funo til que independente de qualquer instn
cia da classe especfica. O mtodo Sqrt serve apenas como um exemplo. Se Sqrt fosse um mtodo de
instncia AeMath, voc teria de criar um objeto Math para chamar Sqrt-.
Math m = new M a t h O ;
double d = m.Sqrt(42.24);

Isso seria inconveniente. O objeto Math poderia no participar do clculo da raiz quadrada. Todos os
dados de entrada que Sqrt necessita so fornecidos na lista de parmetros e o resultado retornado
para o chamador utilizando o valor de retorno do mtodo. Os objetos no so realmente necessrios
aqui, portanto, forar Sqrt em uma instncia camisa de fora no uma boa ideia. Alm do mtodo
Sqrt e do campo P I, a classe M ath contm vrios outros mtodos matemticos utilitrios, como Sin,
Cos, Tan e Log.

Captulo 7

Criando e gerenciando classes e objetos

175

Em C#, todos os mtodos devem ser declarados dentro de uma classe. Mas se declarar um mtodo ou
um campo como static, voc pode chamar o mtodo ou acessar o campo utilizando o nome da classe.
Nenhuma instncia necessria. Veja como o mtodo Sqrt da classeM ath real declarado:
class Math

{
public static double Sqrt(double d)

{
}

Quando voc define um mtodo static, ele no tem acesso a qualquer campo de instncia definida
para a classe; ele s utiliza os campos que so marcados como static. Alm disso, ele s pode invocar
diretamente outros mtodos na classe que esto marcados como static-, mtodos no estticos (de
instncia) requerem primeiramente a criao de um objeto para cham-los.

Criando um campo compartilhado


Conforme mencionado na seo anterior, voc tambm pode utilizar a palavra-chave static ao definir
um campo. Com esse recurso, voc pode criar um nico campo que compartilhado entre todos os
objetos criados a partir de uma nica classe. (Campos no estticos so locais para cada instncia
de um objeto.) No exemplo a seguir, o campo static NumCircles na classe Circle incrementado pelo
construtor Circle toda vez que um novo objeto Circle criado:
c la s s C ir c le

{
p riv a te in t rad ius;
p u b lic s t a t i c in t NumCircles = 0;
p u b lic C ir c le O

// co n stru to r padro

{
radius = 0;
NumCi rcles++;

}
p u b lic C ir c le C in t in it ia lR a d iu s ) // c o n stru to r sobrecarregado

{
radius = i ni t i al Radius;
NumCi rcles++;

176

Parte II

Entendendo a linguagem C#

Todos os objetos Circle compartilham o mesmo campo NumCircles-, portanto, a instruo NumCir
cles + +; incrementa os mesmos dados toda vez que uma nova instncia criada. Voc acessa o
campo NumCircles especificando a classe Circle em vez de um objeto Circle. Por exemplo:
C onsole.W riteLine("Num ber o f C ir c le o b je cts: { 0 } " , C irc le .N u m C ire le s );

Dica Convm lembrar que os mtodos static tambm so chamados de mtodos de classe. Mas
os campos static no so normalmente chamados de campos de classe; eles so chamados simples
mente de campos static (ou, eventualmente, de variveis static).

Criando um campo static utilizando a palavra-chave const


Prefixando o campo com a palavra-chave const, voc pode declarar que um campo esttico, mas
que seu valor nunca pode mudar. A palavra-chave const a abreviao de constante". Um campo
const no utiliza a palavra-chave static na sua declarao, mas mesmo assim esttico. Contudo,
por razes que esto fora do escopo deste livro, voc s pode declarar um campo como const quando
esse campo por uma enumerao, um tipo numrico como in t ou double ou uma string. (Voc apren
der enumeraes no Captulo 9, Criando tipos-valor com enumerao e estruturas.) Por exemplo,
veja como a classe Math declara P I como um campo const:
c la s s Math

{
p u b lic const double P I = 3.14159265358979323846;

Classes estticas
Outro recurso da linguagem C# a capacidade de declarar uma classe como static. Uma classe static
s pode conter membros static. (Todos os objetos que voc cria utilizando essa classe compartilham
uma nica cpia desses membros.) O objetivo de uma classe static puramente atuar como um
continer de campos e mtodos utilitrios. Uma classe static no pode conter dado ou mtodos de
instncia e no faz sentido tentar criar um objeto de uma classe static usando o operador new. De
fato, voc no pode criar uma instncia de um objeto que utiliza uma classe static utilizando new
mesmo se quiser fazer isso. (O compilador informar um erro se voc tentar.) Se voc precisar execu
tar alguma inicializao, uma classe static poder ter um construtor padro desde que ele tambm
seja declarado como static. Qualquer outro tipo de construtor ilegal e ser reportado como tal pelo
compilador.
Se voc estivesse definindo sua verso prpria da classe M ath, contendo apenas membros static, ela
poderia se parecer com esta:

Captulo 7

Criando e gerenciando classes e objetos

177

p u b lic s t a t i c c la s s Math

{
p u b lic s t a t i c double Sin(d o ub le x) { . . . }
p u b lic s t a t i c double Cos(double x) { . . . }
p u b lic s t a t i c double Sq rt(d ou b le x) { . . . }

No exerccio final neste captulo, voc adicionar um campo prvate static classe Point e inicializar
o campo como 0. Voc incrementar essa contagem nos dois construtores. Por fim, voc escrever um
mtodo public static para retornar o valor desse campo prvate static. Com esse campo, voc pode
descobrir quantos objetos Point foram criados.

Escreva membros static e chame mtodos static


1. Utilizando o Visual Studio 2010, exiba a classe Point na janela Code and Text Editor.
2. Pressione Enter para adicionar um campo private static chamado objectCount do tipo in t clas
se Point, antes dos construtores. Inicialize-o como 0 ao declar-lo, da seguinte maneira:
c la s s Po in t

{
p riv a te s t a t i c in t objectCount = 0;

3. Adicione uma instruo aos dois construtores Point para incrementar o campo objectCount,
como mostrado no texto em negrito no exemplo de cdigo que segue.
Toda vez que um objeto criado, seu construtor chamado. Desde que voc incremente o
objectCount em cada construtor (incluindo o construtor padro), objectCount armazenar o
nmero de objetos criados at aqui. Essa estratgia s funciona porque objectCount um cam
po static compartilhado. Se objectCount fosse um campo de instncia, cada objeto teria seu
prprio campo objectCount pessoal que seria definido como 1.

178

Parte II

Entendendo a linguagem C#

A classe Point deve ser semelhante a esta:


c la s s Po in t

{
p riv a te in t x, y;
p riv a te s t a t i c in t objectCount = 0;
p u b lic P o in tO

{
t h is .x = -1;
t h i s . y = -1;
objectCount++;

}
p u b lic P o in t ( in t x, in t y )

{
t h is .x = x;
t h i s . y = y;
objectCount++;

}
p u b lic double D istan ceT o(Poin t o th er)

{
in t x D if f = t h is .x - o th e r.x ;
in t y D if f = t h i s . y - o th e r.y ;
return M a th .S q rt((x D iff * x D if f) + ( y D if f * y D i f f ) ) ;

}
}
Observe que voc no pode prefixar campos e mtodos static com a palavra-chave this porque
eles no pertencem instncia atual da classe. (Na verdade, eles no pertencem a instncia
alguma.)
A questo agora : como os usurios da classe Point podem descobrir quantos objetos Point
foram criados? No momento, o campo objectCount private e no est disponvel fora da classe.
Uma soluo precria seria tornar o campo objectCount publicamente acessvel. Essa estratgia
quebraria o encapsulamento da classe; voc no teria ento qualquer garantia de que seu valor
estava correto porque qualquer coisa poderia alterar o valor no campo. Uma ideia muito melhor
fornecer um mtodo public static que retorne o valor do campo objectCount. Isso o que voc
far agora.
4. Adicione um mtodo public static classe Point chamada ObjectCount que retorna um in t mas
no recebe parmetro algum. Nesse mtodo, retorne o valor do campo objectCount, como no
texto em negrito a seguir:
c la s s Po in t

{
public static int ObjectCountO

{
return objectCount;

Captulo 7

Criando e gerenciando classes e objetos

179

5. Exiba a classe Program na janela Code and TextEditor e localize o mtodo DoWork.
6. Adicione uma instruo ao mtodo DoWork para gravar o valor retornado a partir do mtodo
ObjectCount da classe Point na tela, como mostrado no texto em negrito no exemplo de cdigo a
seguir. O mtodo DoWork deve ser semelhante a este:
static void D o W o r k O

{
Point origin = new PointO;
Point bottomRight = new Point(600, 800);
double distance = origin.distanceTo(bottomRight);
Console.WriteLine("Distance is: {0}", distance);
C o n s o le .W r ite L in e (" N o o f P o in t o b je c t s :

{0 }",

P o in t .O b je c t C o u n t O ) ;

}
O mtodo ObjectCount chamado referenciando Point, o nome da classe e no o nome de uma
varivel Point (como origin ou bottom Right). Como dois objetos Point foram criados quando
ObjectCount foi chamado, o mtodo deve retornar o valor 2.
7. No menu Debug, clique em Start W ithout Debugging.
Confirme que o valor 2 foi escrito na janela do console (aps a mensagem que exibe o valor da
varivel distance).
8. Pressione Enter para terminar o programa e retorne para o Visual Studio 2010.

Classes annimas
Uma classe annima uma classe que no tem nome. Isso parece bastante estranho, mas bem til
em algumas situaes que veremos mais adiante neste livro, especialmente ao utilizar expresses de
consulta. (Voc aprender expresses de consulta no Captulo 20, Consultando dados na memria
utilizando expresses de consulta .) Por enquanto, simplesmente aceite o fato de que elas so teis.
Voc cria uma classe annima simplesmente utilizando a palavra-chave new e um par de chaves que
definem os campos e valores que voc quer que a classe contenha, assim:
myAnonymousObject = new { Name = "lohn", Age = 44 };

Essa classe contm dois campos pblicos chamados Name (inicializado para a string John ") eAge
(inicializado como o inteiro 44). O compilador infere os tipos dos campos a partir dos tipos de dados
que voc especifica para inicializ-los.
Ao definir uma classe annima, o compilador gera um nome prprio para a classe, mas ele no infor
mar qual esse nome. Portanto, classes annimas levantam um enigma potencialmente interessan
te: se voc no souber o nome da classe, como poder criar um objeto do tipo apropriado e atribuir
uma instncia da classe a ele? No exemplo de cdigo mostrado anteriormente, qual deve ser o tipo
da varivel myAnonymousObjectl A resposta que voc no sabe - esse o propsito das classes
annimas! Mas isso no um problema se voc declarar myAnonymousObject como uma varivel
implicitamente tipada utilizando a palavra-chave var, desta maneira:
var myAnonymousObject = new { Name = "3ohn", Age = 44 };

180

Parte II

Entendendo a linguagem C#

Lembre-se de que a palavra-chave var faz o compilador criar uma varivel do mesmo tipo da expres
so utilizada para inicializ-la. Nesse caso, o tipo da expresso o nome que o compilador gera para
a classe annima.
Voc pode acessar os campos no objeto utilizando a notao familiar de ponto, assim:
Console.WriteLine("Name: {0} Age: {1 }", myAnonymousObject.Name, myAnonymousObject.Age};
Voc pode at mesmo criar outras instncias da mesma classe annima, mas com valores diferentes:
var anotherAnonymousObject = new { Name = "Diana", Age = 45 };
O compilador do C# utiliza os nomes, os tipos, o nmero e a ordem dos campos para determinar
se duas instncias de uma classe annima tm o mesmo tipo. Nesse caso, as variveis myAnonymousObject e anotherAnonymousObject tm o mesmo nmero de campos, com o mesmo nome e
tipo, na mesma ordem, portanto, as duas variveis so instncias da mesma classe annima. Isso
significa que voc pode realizar instrues de atribuio como esta:
anotherAnonymousObject = myAnonymousObject;

H muitas restries quanto ao contedo de uma classe annima. Classes annimas s podem con
ter campos pblicos, todos esses campos precisam ser inicializados, eles no podem ser estticos e
voc no pode especificar mtodo algum.
Neste captulo, voc viu como possvel definir novas classes. Voc aprendeu que, por padro, os
campos e os mtodos de uma classe so privados e inacessveis ao cdigo fora da classe, mas voc
pode utilizar a palavra-chave public para expor campos e mtodos para o mundo exterior. Voc viu
como utilizar a palavra-chave new para criar uma nova instncia de uma classe, e como definir cons
trutores que podem inicializar instncias de classes. Por ltimo, voc examinou a implementao de
campos e mtodos estticos, para fornecer dados e operaes que independem de qualquer instncia
especfica de uma classe.
Se voc quiser seguir para o prximo captulo agora:
Mantenha o Visual Studio 2010 em execuo e v para o Captulo 8.
Se quiser sair do Visual Studio 2010:
No menu File, clique em Exit. Se vir uma caixa de dilogo Save, clique em Yes e salve o projeto.

Captulo 7

Criando e gerenciando classes e objetos

181

Referncia rpida do Captulo 7


Declarar uma classe

Escreva a palavra-chave c/ass, seguida pelo nome da classe, seguida por uma
chave de abertura e uma de fechamento. Os mtodos e campos da classe
so declarados entre as chaves de abertura e fechamento. Por exemplo:
class Point

Declarar um construtor

Escrever um mtodo cujo nome o mesmo nome da classe e que no tenha


qualquer tipo de retorno (nem mesmo void). Por exemplo:
class Point
{
public Point(int x, int y)

Chamar um construtor

Use a palavra-chave new e especifique o construtor com um conjunto de


parmetros apropriados. Por exemplo:
Point origin = new Point(0, 0);

Declarar um mtodo sfat/c

Escreva a palavra-chave static antes da declarao do mtodo. Por exemplo:


class Point
public static int ObjectCountO

Chamar um mtodo static

Escreva o nome da classe, seguida de um ponto, seguida do nome do mtodo. Por exemplo:
int pointsCreatedSoFar = Point.ObjectCountO;

Declarar um campo static

Escreva a palavra-chave static antes da declarao do campo. Por exemplo:


class Point

private static int objectCount;

(continua)

182

Parte II

Entendendo a linguagem C#

Para

Faa isto

Declarar um campo const

Escreva a palavra-chave const antes da declarao do campo e omita a


palavra-chave static. Por exemplo:
class Math
{
public const double PI =
}

Acessar um campo static

Escreva o nome da classe, seguido de um ponto, seguido do nome do mto


do. Por exemplo:
double area = Math.PI * radius * radius;

Captulo 8

Entendendo valores e referncias


Neste captulo, voc vai aprender a:
Explicar as diferenas entre um tipo-valor e um tipo-referncia.
Modificar a maneira como os argumentos so passados como parmetros de mtodos
utilizando as palavras-chave r e f e o u t .
Fazer o boxing de um valor inicializando ou atribuindo uma varivel do tipo o b ject.
Fazer o unboxing de um valor via casting do objeto que referencia o valor na forma boxed.

No Captulo 7, Criando e gerenciando classes e objetos , voc aprendeu a declarar suas classes e a
criar objetos utilizando a palavra-chave new. Voc tambm viu como inicializar um objeto utilizando
um construtor. Neste captulo, voc aprender qual a diferena entre as caractersticas dos tipos
primitivos, como int, double e char, e as caractersticas dos tipos de classe.

Copiando variveis de tipo-valor e classes


Tipos como in t,Jlo a t, double e char so coletivamente chamados tipos-valor. Quando voc declara
uma varivel como um tipo-valor, o compilador gera o cdigo que aloca um bloco de memria grande
o suficiente para conter um valor correspondente. Por exemplo, declarar uma varivel in t faz o com
pilador alocar 4 bytes de memria (32 bits). Uma instruo que atribui um valor (como 42) a in t faz
o valor ser copiado para esse bloco de memria.
Os tipos classe, como Circle (descrito no Captulo 7), so tratados de maneira diferente. Quando voc
declara uma varivel Circle, o compilador np gera um cdigo que aloca um bloco de memria grande
o suficiente para armazenar Circle-, tudo o que ele faz alocar uma pequena parte da memria que
possa armazenar o endereo de (ou referncia a) outro bloco de memria que contm Circle. (Um
endereo especifica a localizao de um item na memria.) A memria para o objeto Circle real s
alocada quando a palavra-chave new utilizada para criar o objeto. Uma classe um exemplo de um
tipo-referncia. Tipos-referncia contm referncias a blocos de memria. Para escrever programas
C# eficazes, que usem plenamente o Microsoft .NET Framework, voc deve saber a diferena entre
tipos-valor e tipos-referncia.

184

Parte II

Entendendo a linguagem C#

Considere a situao em que voc declara uma varivel chamada i como um in t e atribui a ela o
valor 42. Se declarar uma outra varivel chamada copyi como um in t e ento atribuir i a copyi, copyi
conter o mesmo valor que i (42). Mas embora copyi e i contenham o mesmo valor, h dois blocos de
memria contendo o valor 42: um bloco para i e outro bloco para copyi. Se voc modificar o valor de
i, o valor de copyi no ser alterado. Vejamos isso no cdigo:
int i = 42;
// declara e inicializa i
int copyi = i ; // copyi contm uma cpia dos dados em i
i++;
// incrementar i no tem efeito sobre copyi

O efeito de declarar uma varivel c como Circle (o nome de uma classe) muito diferente. Quando
voc declara c como Circle, c pode referenciar um objeto Circle. Se voc declarar refc como outro Circle,
ele tambm poder referenciar um objeto Circle. Se voc atribuir c a refc, refc ir referenciar o mesmo
objeto Circle que c referencia; h apenas um objeto Circle e tanto refc quanto c o referenciam. O que
aconteceu aqui que o compilador alocou dois blocos de memria, u m a c e outro a refc, mas o en
dereo contido em cada bloco aponta para a mesma localizao na memria que armazena o objeto
Circle real. Vejamos isso no cdigo:
Circle c = new Circle(42);
Ci rcle refc = c;

A figura a seguir ilustra ambos os exemplos. O sinal (@) nos objetos Circle representa uma referncia
a um endereo na memria:

Circle c
Circle c;
c = newCircle (42);
Circle refc;
refc = c;

Circle refc

42

Captulo 8

Entendendo valores e referncias

185

Essa diferena muito importante. Em particular, ela significa que o comportamento dos parmetros
do mtodo depende de eles serem tipos-valor ou tipos-referncia. Voc ir explorar essa diferena no
exerccio a seguir.

1. Inicie o Microsoft Visual Studio 2010 se ele ainda no estiver executando.


2. Abra o projeto Parameters localizado na pasta \Microsoft PressWisual CSharp Step by Step\Chapter 8\Parameters na sua pasta Documentos.
O projeto contm trs arquivos de cdigo C# chamados Pass.cs, Program.cs e Wrappedlnt.es.
3. Exiba o arquivo Pass.cs na janela Code and Text Editor. Adicione um mtodo public static cha
mado Value classe Pass, substituindo o comentrio // to do, como mostrado em negrito no
seguinte exemplo de cdigo. Esse mtodo deve aceitar um nico parmetro in t (um tipo-valor)
chamado param e ter um tipo de retorno void. O corpo do mtodo Value deve simplesmente
atribuir 42 a param.
namespace Parameters
{
class Pass
{
public static void Value(int param)

{
param = 42;

}
}
}
4. Exiba o arquivo-fonte Program.cs na janela Code and Text Editor e localize o mtodo DoWork da
classe Program.
O mtodo DoWork chamado pelo mtodo M ain quando o programa comea a executar. Confor
me explicado no Captulo 7, a chamada de mtodo vem dentro de um bloco try e seguida por
uma rotina de tratamento catch.

186

Parte II

Entendendo a linguagem C#

5. Adicione quatro instrues ao mtodo DoWork para executar as seguintes tarefas:


1. Declarar uma varivel local in t chamada i e inicializ-la como 0.
2. Escrever o valor de i no console utilizando Console. WriteLine.
3. Chamar Pass. Value, passando i como argumento.
4. Escrever novamente o valor de i no console.
Com as chamadas a Console. W riteLine antes e depois da chamada a Pass. Value, voc pode ver
se a chamada a Pass. Value realmente modifica o valor de i. O mtodo DoWork deve ficar assim:
static void D o W o r k O

{
int i = 0;
Console.WriteLine(i);
Pass.Vaiue(i);
Console.WriteLine(i);

}
6. No menu Debug, clique em Start Without Debugging para compilar e executar o programa.
7. Confirme se o valor 0 foi escrito na janela do console duas vezes.
A instruo de atribuio dentro do mtodo Pass. Value, que atualiza o parmetro e o define com
42, utiliza uma cpia do argumento passado e o argumento original i permanece completamen
te inalterado.
8. Pressione a tecla Enter para fechar o aplicativo.
Voc ver agora o que acontece quando voc passa um parmetro in t que est inserido dentro
de uma classe.
9. Exiba o arquivo Wrappedlnt.es na janela Code and Text Editor. Adicione um campo de instncia
public chamado Number do tipo in t classe Wrappedlnt, como mostrado em negrito aqui.
namespace Parameters

{
class Wrappedlnt

{
public int Number;

}
}
10. Exiba o arquivo Pass.cs na janela Code and Text Editor. Adicione um mtodo public static cha
mado Reference classe Pass. Esse mtodo deve aceitar um nico parmetro Wrappedlnt cha

Captulo 8

Entendendo valores e referncias

187

mado param e ter um tipo de retorno void. 0 corpo do mtodo Reference deve atribuir 42 a
param.Number, desta maneira:
public static void Reference(WrappedInt param)

{
param.Number = 42;

}
11. Exiba o arquivo Program.cs na janela Code and Text Editor. Comente o cdigo existente no m
todo DoWork e adicione mais quatro instrues para executar as seguintes tarefas:
a. Declarar uma varivel Wrappedlnt local chamada w i e inicializ-la com um novo objeto
Wrappedlnt chamando o construtor padro.
b. Escrever o valor de wi.Number no console.
c. Chamar o mtodo Pass.Reference, passando w i como um argumento.
d. Escrever o valor de wi.Number novamente no console.
Como antes, com as chamadas a Console. WriteLine, voc pode ver se a chamada a Pass.Referen
ce modifica o valor de wi.Number. O mtodo DoWork agora deve estar assim (as novas instru
es so mostradas em negrito):
static void D o W o r k O

{
//
//
//
//

int i = 0 ;
Consol e. Wri teLi ne(i);
Pass.Vaiue(i);
Console.WriteLine(i);

Wrappedlnt wi = new Wrappedlnt();


Console.Wri teLi ne(wi.Number);
Pass.Reference(wi);
Console.Wri teLi ne(wi.Number);

}
12. No menu Debug, clique em Start W ithout Debugging para compilar e executar o aplicativo.
Dessa vez, os dois valores exibidos na janela de console correspondem ao valor de wi.Number
antes e depois de Pass.Reference e voc deve ver a sada dos valores 0 e 42.
13. Pressione a tecla Enter para finalizar o programa e retornar ao Visual Studio 2010.
Para explicar o que o exerccio anterior demonstra, o valor de wi.Number inicializado como 0
pelo construtor gerado pelo compilador. A varivel w i contm uma referncia ao objeto Wrappedlnt

188

Parte II

Entendendo a linguagem C#

recm-criado (que contm um in t). A varivel w i ento copiada como um argumento para o mtodo
Pass.Reference. Como Wrappedlnt uma classe (um tipo-referncia), w i e param referenciam o mes
mo objeto Wrappedlnt. Oualquer alterao feita ao contedo do objeto por meio da varivel param
no mtodo Pass.Reference visvel utilizando a varivel w i quando o mtodo concludo. O diagra
ma a seguir ilustra o que acontece quando um objeto Wrappedlnt passado como um argumento
para o mtodo Pass.Reference-,

Entendendo valores nulos e tipos nullable


Ao declarar uma varivel, sempre uma boa ideia inicializ-la. Com tipos-valor, comum ver cdigo
como este:
int i = 0;
double d = 0.0;

Lembre-se de que para inicializar uma varivel de referncia como uma classe, voc pode criar uma
nova instncia da classe e atribuir a varivel de referncia ao novo objeto, assim:
Circle c = new Circle(42);

Tudo isso est OK, mas e se voc na verdade no quiser criar um novo objeto - talvez o propsito da
varivel seja simplesmente armazenar uma referncia a um objeto existente. No exemplo de cdigo
a seguir, a varivel Circle copy inicializada, mas depois lhe atribuda uma referncia a uma outra
instncia da classe Circle-,
Circle c = new Circle(42);
Circle copy = new Circle(99);
copy = c;

// Algum valor aleatrio, para inicializar copy


// copy e c referenciam o mesmo objeto

Depois de atribuir c a copy, o que acontece ao objeto Circle original com um raio de 99 que voc
utilizou para inicializar copyl Nada mais o referencia. Nessa situao, o runtime pode reivindicar a

Captulo 8 Entendendo valores e referncias

189

memria realizando uma operao conhecida como coleta de lixo, cujos detalhes voc conhecer no
Captulo 14, Utilizando a coleta de lixo e o gerenciamento de recursos. O importante a entender por
enquanto que a coleta de lixo uma operao potencialmente lenta.
Voc poderia argumentar que, se uma varivel receber uma referncia a um outro objeto em algum
ponto em um programa, no h sentido em inicializ-la. Mas isso uma pssima prtica de progra
mao e pode levar a problemas no seu cdigo. Por exemplo, voc encontrar inevitavelmente a si
tuao em que quer referenciar uma varivel para um objeto somente se essa varivel j no contiver
uma referncia, como mostra o seguinte exemplo de cdigo:
Circle c = new Circle (42);
Circle copy;

// No inicializada! !!

if (copy == // 0 que entra aqui?)


copy = c;
// copy e c referenciam o mesmo objeto

O propsito da instruo if testar a varivel copy para ver se ela foi inicializada, mas com qual va
lor voc deve comparar essa varivel? A resposta utilizar um valor especial chamado null.
No C#, voc pode atribuir o valor nu ll a qualquer varivel-referncia. O valor n u ll simplesmente
significa que a varivel no referencia objeto algum na memria. Voc pode utiliz-lo desta maneira:
C i r d e c = new Circle(42);
Ci r d e copy = null;

// Inicializada

if (copy == null)
copy = c;

// copy e c referenciam o mesmo objeto

Utilizando tipos nullable


0 valor null til para inicializar tipos-referncia, mas nu ll , ele prprio, uma referncia, e voc no
pode atribu-lo a um tipo-valor. A seguinte instruo , portanto, invlida no C#:
1 nt i = nul1; // i nvli do

Mas o C# define um modificador que pode ser utilizado para declarar se uma varivel um ti
po-valor nullable. Um tipo-valor nullable comporta-se de maneira semelhante ao tipo-valor original,
mas voc pode atribuir o valor null a ele. Utilize o ponto de interrogao (?) para indicar que um
tipo-valor nullable, assim:
int? i = null; // vlido

possvel determinar se uma varivel nullable contm null testando-a da mesma maneira que um
tipo-referncia:
if (i == null)

190

Parte II

Entendendo a linguagem C#

Voc pode atribuir uma expresso do tipo-valor apropriado diretamente a uma varivel nullable.
Todos os exemplos a seguir so vlidos:
int? i = null ;
int j = 99;
i = 100;
// Copia uma constante de tipo-valor para um tipo nullable
i = j;
// Copia uma varivel de tipo-valor para um tipo nullable

Voc deve observar que o contrrio no verdadeiro. Voc no pode atribuir um valor nullable a uma
varivel de tipo-valor normal. Portanto, dadas as definies das variveis i ej do exemplo anterior, a
instruo a seguir no permitida:
j = i ; // Invli do

Isso faz sentido, se voc considerar que a varivel i pode conter null, e quej um tipo-valor que no
pode conter null. Isso tambm significa que voc no pode utilizar uma varivel nullable como um
parmetro para um mtodo que espera receber um tipo-valor normal. Se voc se lembra, o mtodo
Pass. Value do exerccio anterior espera um parmetro normal int, portanto, a seguinte chamada de
mtodo no ir compilar:
int? i = 99;
Pass.Value(i);

// Erro de compilador

Entendendo as propriedades dos tipos nullable


Tipos nullable expem algumas propriedades que voc pode utilizar e que j discutimos no Captulo
6, Gerenciando erros e excees . A propriedade HasValue indica se um tipo nullable contm um
valor ou nu ll e voc pode recuperar o valor de um tipo nullable no null lendo a propriedade Value,
desta maneira:
int? i = null;
if C !i .HasValue)
i = 99;
el se
Console.WriteLine(i .Value);

Lembre-se, a partir do que foi discutido no Captulo 4, Utilizando instrues de deciso", de que o
operador NOT (!) nega um valor booleano. Esse fragmento de cdigo testa a varivel nullable i e, se
ela no tiver um valor (for null), ele atribui a essa varivel o valor 99; do contrrio, ele exibe o valor
da varivel. Neste exemplo, utilizar a propriedade HasValue no traz benefcio algum em relao
a testar quanto a um valor n u ll diretamente. Alm disso, ler a propriedade Value uma maneira
tediosa de ler o contedo da varivel. Mas essas deficincias aparentes so causadas pelo fato de
que in t? um tipo nullable muito simples. Voc pode criar tipos-valor mais complexos e utiliz-los
para declarar variveis nullable em que as vantagens da utilizao das propriedades HasValue e
Value tornam-se mais aparentes. Veremos alguns exemplos no Captulo 9, Criando tipo-valor com
numerao e estruturas .

Captulos

Entendendo valores e referncias

191

Nota A propriedade Value de um tipo nullable somente de leitura. Voc pode utilizar essa
propriedade para ler o valor de uma varivel, mas no para modific-la. Para atualizar uma varivel
nullable, utilize uma instruo de atribuio comum.

Utilizando parmetros ref e out


Em geral, quando voc passa um argumento para um mtodo, o parmetro correspondente inicializado com uma cpia do argumento. Isso verdade independentemente de o parmetro ser um
tipo-valor (como um int), um tipo nullable (como int?} ou um tipo-referncia (como um Wmppedlnt).
Esse arranjo significa que impossvel que qualquer alterao no parmetro afete o valor do argumento passado. Por exemplo, no seguinte cdigo, a sada do valor para o console 42 e no 43. O
mtodo Dolncrement incrementa uma cpia do argumento (arg) e no o argumento original:
static void Dolncrement(int param)
{
param++;
}
static void MainQ

{
int arg = 42;

Dolncrement(arg);
Console.WriteLine(arg); //

escreve 42, no 43

No exerccio anterior, voc viu que, se o parmetro para um mtodo um tipo-referncia, qualquer
alterao feita utilizando esse parmetro modifica os dados referenciados pelo argumento passado
por ele. O ponto principal que, embora os dados que foram referenciados tenham mudado, o
argumento passado como um parmetro no mudou - ele ainda referencia o mesmo objeto. Em
outras palavras, embora seja possvel modificar o objeto que o argumento referencia, no possvel modificar o argumento propriamente dito (por exemplo, para defini-lo a fim de referenciar um
objeto completamente diferente). Na maioria das vezes, essa garantia muito til e pode ajudar a
reduzir o nmero de erros em um programa. Eventualmente, porm, voc pode querer escrever um
mtodo que realmente precise modificar um argumento. O C# fornece as palavras-chave ref t out
para que voc possa fazer isso.

Criando parmetros ref


Se voc utilizar como prefixo de um parmetro a palavra-chave ref, o parmetro torna-se um alias do
(ou uma referncia ao) argumento real em vez de uma cpia do argumento. Ao utilizar um parmetro
ref, tudo o que voc fizer ao parmetro tambm ser feito ao argumento original, porque o parmetro
e o argumento referenciam o mesmo objeto. Ao passar um argumento como um parmetro ref, voc
tambm deve prefixar o argumento com a palavra-chave ref. Essa sintaxe fornece uma indicao

192

Parte II

Entendendo a linguagem C#

visual til para o programador de que o argumento pode mudar. Veja novamente o exemplo anterior,
desta vez modificado para utilizar a palavra-chave ref-.
static void DoIncrementCref int param) // utilizando ref

{
param++;

}
static void M a i n O

{
int arg = 42;
DoIncrementCref arg);
// utilizando ref
Console.WriteLine(arg); // escreve 43

}
Desta vez, voc passa para o mtodo Dolncrement uma referncia ao argumento original em vez de
uma cpia do argumento original, portanto, qualquer alterao que o mtodo faz utilizando essa
referncia tambm muda o argumento original. Essa a razo de o valor 43 ser exibido no console.
A regra de que voc deve atribuir um valor a uma varivel antes de poder utiliz-la ainda se aplica
aos argumentos ref. Por exemplo, no programa a seguir, arg no inicializada, portanto, esse cdigo
no ser compilado. Essa falha ocorre porque param + + dentro de Dolncrement na verdade arg+ +
e isso s permitido se arg tiver um valor definido:
static void DoIncrementCref int param)

{
param++;

}
static void M a i n O

{
int arg;
// no inicializado
DoIncrementCref arg);
Console.WriteLineCarg) ;

Criando parmetros out


O compilador verifica se o parmetro re f recebeu um valor antes de chamar o mtodo. Mas pode
haver ocasies em que voc queira que o mtodo inicialize o parmetro. Voc pode fazer isso com a
palavra-chave out.
A palavra-chave out semelhante palavra-chave ref. Voc pode utilizar como prefixo do parme
tro a palavra-chave out para que o parmetro se torne um alias para o argumento. Assim como ao
utilizar ref, tudo o que voc faz no parmetro tambm feito no argumento original. Ao passar um
argumento para um parmetro out, voc tambm deve prefixar o argumento com a palavra-chave
out. A palavra-chave out uma abreviao de output. Ouando voc passa um parmetro out para
um mtodo, o mtodo deve atribuir um valor a ele. O exemplo a seguir no compilado porque Dolnitialize no atribui um valor a param-.

Captulo 8

Entendendo valores e referncias

193

static void DoInitialize(out int param)

{
// No faz nada

}
Entretanto, o exemplo a seguir realmente compila porque D olnitialize atribui um valor a param :
static void DoInitializeCout int param)

{
param = 42;

}
Uma vez que um parmetro out deve receber um valor do mtodo, voc pode chamar o mtodo sem
inicializar seu argumento. Por exemplo, o cdigo a seguir chama D olnitialize para inicializar a vari
vel arg, que ento exibida no console:
static void Dolnitialize(out int param)

{
param = 42;

}
static void Main()

{
int arg;
// no inicializado
Dolnitialize(out arg);
Console.WriteLine(arg); // escreve 42

}
Examinaremos parmetros ref no prximo exerccio.

Utilize parmetros re f
1. Retorne ao projeto Parameters no Visual Studio 2010.
2. Exiba o aiquvvo Pass.cs na janVa Code and Text Editor.

3. Edite o mtodo Value para aceitar seu parmetro como um parmetro ref.
O mtodo Value deve se parecer com este:
class Pass

{
public static void Value(ref int param)

{
param = 42;

}
}

194

Parte II

Entendendo a linguagem C#

4. Exiba o arquivo Program.cs na janela Code and Text Editor.


5. Descomente as quatro primeiras instrues. Edite a terceira instruo do mtodo DoWork para
que a chamada do mtodo Pass. Value passe seu argumento como um parmetro ref.

O mtodo DoWork deve agora ser semelhante a este:


class Application

{
static void D o W o r k O

{
int i = 0;
Console.WriteLine(i);
Pass.Vaiue(ref i ) ;
Console.WriteLine(i);

}
}
6. No menu Debug, clique em Start W ithout Debugging para compilar e executar o programa.
Desta vez, os dois primeiros valores gravados na janela de console so 0 e 42. Esse resultado
mostra que a chamada ao mtodo Pass. Value modificou o argumento i com sucesso.
7. Pressione a tecla Enter para finalizar o programa e retornar ao Visual Studio 2010.

Como a memria do computador organizada


Os computadores utilizam a memria para armazenar os programas que esto sendo executados e os
dados que esses programas utilizam. Para entender as diferenas entre os tipos-valor e os tipos-referncia, til entender como os dados so organizados na memria.

Captulo 8

Entendendo valores e referncias

195

Sistemas operacionais e runtimes (ambientes de execuo) de linguagens, como os utilizados pelo


C#, frequentemente dividem a memria utilizada para armazenar dados em duas partes separadas,
cada uma gerenciada de uma maneira distinta. Esses dois blocos so tradicionalmente chamados
pilha (stack) e heap. Pilha e heap servem para propsitos muito diferentes:
Quando voc chama um mtodo, a memria necessria para seus parmetros e suas vari
veis locais sempre adquirida da pilha. Quando o mtodo termina (seja porque retornou, seja
porque lanou uma exceo), a memria adquirida para os parmetros e as variveis locais
automaticamente liberada de volta para a pilha e fica disponvel para ser reutilizada quando
outro mtodo for chamado.
Quando voc cria um objeto (uma instncia de uma classe) utilizando a palavra-chave new, a
memria necessria para compilar o objeto sempre adquirida do heap. Voc viu que o mesmo
objeto pode ser referenciado de vrios lugares utilizando variveis de referncia. Quando a lti
ma referncia a um objeto desaparece, a memria utilizada pelo objeto torna-se disponvel para
ser reutilizada (embora ela possa no ser utilizada imediatamente). O Captulo 14 inclui uma
discusso mais detalhada de como a memria heap empregada.

Os nomes pilha e heap tm origem na maneira como o runtime gerencia a memria:


A memria de pilha organizada como uma pilha de caixas sobrepostas umas sobre as outras.
Quando um mtodo chamado, cada parmetro colocado em uma caixa que disposta na
parte superior da pilha. Cada varivel local igualmente atribuda a uma caixa, e esta coloca
da no topo da pilha de caixas. Ouando um mtodo termina, todas as suas caixas so removidas
da pilha.
A memria heap literalmente um monte" de caixas espalhadas por uma sala em vez de em
pilhadas ordenadamente umas sobre as outras. Cada caixa tem um rtulo indicando se est em
uso ou no. Ouando um novo objeto criado, o runtime procura uma caixa vazia e a aloca para
o objeto. A referncia caixa armazenada em uma varivel local na pilha. O runtime monitora
o nmero por referncias a cada caixa (lembre-se de que duas variveis podem referenciar o
mesmo objeto). Quando a ltima referncia desaparece, o runtime marca a caixa como fora de
uso, e em algum ponto no futuro esvaziar a caixa e a disponibilizar para reutilizao.

196

Parte II

Entendendo a linguagem C#

Utilizando a pilha e o heap


Agora vamos examinar o que acontece quando o mtodo Method chamado a seguir:
void Method(int param)

{
Circle c;
c = new Circle(param);

}
Suponha que o argumento passado para param seja o valor 42. Quando o mtodo chamado, um
bloco de memria (grande o suficiente para um in t) alocado na pilha e inicializado com o valor
42. Quando o fluxo do programa entra no mtodo, um outro bloco de memria, grande o suficiente
para armazenar uma referncia (um endereo de memria), tambm alocado da pilha, mas perma
nece no inicializado. (Esse bloco para a varivel Circle, c.) Em seguida, outra parte da memria
grande o suficiente para um objeto Circle alocada do heap. Isso o que faz a palavra-chave new. O
construtor Circle executado para converter essa memria bruta do heap em um objeto Circle. Uma
referncia a esse objeto Circle armazenada na varivel c. A figura a seguir ilustra a situao:

PILHA

HEAP

int param
void Method (int param)
{
Circle c;
c= new Circle(param);

42

Circle c

-
Neste ponto, voc j deve ter notado duas coisas:
Embora o objeto esteja armazenado no heap, a referncia ao objeto (a varivel c) est armaze
nada na pilha.
A memria heap no infinita. Se a memria heap estiver esgotada, o operador new lanar
uma exceo OutOfUemoryException e o objeto no ser criado.

Quando o mtodo terminar, os parmetros e variveis locais sairo do escopo. A memria adquirida
para c e param so automaticamente liberadas na pilha. O runtime nota que o objeto Circle no
mais referenciado e, mais tarde, providenciar para que sua memria seja reivindicada pelo heap
(consulte o Captulo 14).

Captulo 8

Entendendo valores e referncias

197

A classe System .Object


Um dos tipos-referncia mais importante no Microsoft .NET Framework a classe Object no names
pace System. Para compreender completamente o significado da classe System .Object necessrio
que voc entenda herana, que ser descrita no Captulo 12, Trabalhando com herana . Por en
quanto, simplesmente aceite que todas as classes so tipos especializados da classe System.Object e
que voc pode utilizar System.Object para criar uma varivel que pode referenciar qualquer tipo-referncia. System.Object uma classe to importante que o C# fornece a palavra-chave object como um
alias de System.Object. No seu cdigo, voc pode utilizar object ou pode escrever System .Object; eles
significam exatamente a mesma coisa.

Dica Utilize a palavra-chave object em vez de System.Object. Ela mais direta e consistente com
outras palavras-chave que so sinnimos para classes (como string para System.String e algumas
outras que voc descobrir no Captulo 9).

No exemplo a seguir, as variveis c e o referenciam o mesmo objeto Circle. O fato de que o tipo de c
Circle e o tipo de o object (o alias de System.Object) na prtica fornece duas vises diferentes do
mesmo item na memria:
Circle c;
c = new Ci rcle(42);
object o;
o = c;

PILHA

HEAP

Circle c
Circle c;
c =new Circle(param);
object o;
o = c;

object o
@

Boxing
Como voc acabou de ver, as variveis do tipo object podem referenciar qualquer objeto de qualquer
tipo-referncia. Mas as variveis do tipo object tambm podem referenciar um tipo-valor. Por exem
plo, as duas instrues a seguir inicializam a varivel i (do tipo int, um tipo-valor) como 42 e, ento,
inicializam a varivel o (do tipo object, um tipo-referncia) como i:
int i = 42;
object o = i ;

198

Parte II

Entendendo a linguagem C#

A segunda instruo requer uma pequena explicao para compreender o que realmente est aconte
cendo. Lembre-se de que i um tipo-valor e existe na pilha. Se a referncia dentro de o referenciasse
diretamente /, ela referenciaria a pilha. Mas todas as referncias devem referenciar objetos no heap;
criar referncias a itens na pilha pode comprometer seriamente a robustez do runtime e criar uma
potencial brecha de segurana; logo, isso no permitido. Portanto, o tempo de execuo aloca uma
parte da memria a partir do heap, copia o valor do inteiro i para essa parte da memria e faz o ob
jeto o referenciar essa cpia. Essa cpia automtica de um item da pilha para o heap chamada de
boxing. A figura a seguir mostra o resultado:

PILHA

HEAP

int i

int i = 42;
object o = i;

Im p o rta n te Se voc modificar o valor original de uma varivel, o valor no heap no mudar.
Da mesma forma, se voc modificar o valor no heap, o valor original da varivel no ser alterado.

Unboxing
Como uma varivel do tipo object pode referenciar uma cpia na forma boxed de um valor, razovel
permitir que voc acesse o valor boxed por meio da varivel. Voc poderia esperar conseguir acessar
o valor in t na forma boxed que a varivel o referencia utilizando uma simples instruo de atribuio
como esta:
int i = o;

Mas se tentar essa sintaxe, voc receber um erro de tempo de compilao. Se voc pensar no assun
to, muito lgico que voc no possa utilizar a sintaxe i nt i = o;. Afinal de contas, o pode estar
referenciando qualquer coisa e no apenas um int. Considere o que aconteceria no cdigo a seguir se
essa instruo fosse permitida:

Captulo 8

Entendendo valores e referncias

199

Circle c = new Circ l eO;


i nt i = 42;
object o;
0 = c;
1 = o;

/ / o referencia um circulo
/ / o que est armazenado em i?

Para obter o valor da cpia boxed, voc deve utilizar o que conhecido como casting, uma operao
que verifica se seguro converter um tipo em outro e ento faz a converso. Voc coloca o nome do
tipo como prefixo da varivel object entre parnteses, como neste exemplo:
int i = 42;
object o = i ; // faz o boxing
i = (int)o;
// compila corretamente

O efeito desse casting sutil. O compilador nota que voc especificou o tipo in t no casting. Em segui
da, o compilador gera um cdigo para verificar o que o realmente referencia em tempo de execuo.
Poderia ser realmente qualquer coisa. S porque seu casting diz que o referencia um int, isso no sig
nifica que ele realmente um int. Se o realmente referencia um in t na forma boxed e tudo coincide,
o casting bem-sucedido e o cdigo gerado pelo compilador extrai o valor do in t na forma boxed e o
copia em i. (Neste exemplo, o valor boxed ento armazenado em i.) Isso chamado de unboxing. O
diagrama a seguir mostra o que est acontecendo:

PILHA

HEAP

object o
object o = 42;
int i = (int)o;

Mas se o no referencia um valor in t na forma boxed, h uma incompatibilidade de tipos, fazendo o


casting falhar. O cdigo gerado pelo compilador lana uma InvalidCastException em tempo de execu
o. Veja o exemplo de um casting para uma operao de unboxing que falha:
Circle c = new Circle(42);
object o = c;
// no ocorre boxing porque Circle uma varivel-referncia
int i = (int)o;
// compila, mas lana uma exceo em tempo de execuo

Voc utilizar boxing e unboxing em exerccios posteriores. Lembre-se de que boxing e unboxing so
operaes caras devido quantidade de verificao requerida e necessidade de alocar memria heap
adicional. O boxing tem suas utilidades, mas o uso imprudente pode prejudicar seriamente o desempe
nho de um programa. Voc ver uma alternativa ao boxing no Captulo 18, Apresentando genricos .

200

Parte II

Entendendo a linguagem C#

lana InvalidCastException

Casting de dados seguro


Utilizando um casting, voc pode especificar que, em sua opinio, os dados referenciados por um
objeto tm um tipo especfico e que seguro referenciar o objeto utilizando esse tipo. A expresso-chave aqui em sua opinio . 0 compilador C# confiar em voc ao compilar o aplicativo, mas o
runtime mais desconfiado e realmente verificar se esse o caso quando o aplicativo executar. Se
o tipo de objeto na memria no corresponder ao casting, o runtime lanar uma InvalidCastExcep
tion, como descrito na seo anterior. Voc deve estar preparado para capturar essa exceo e trat-la
apropriadamente se ela ocorrer.
Mas capturar uma exceo e tentar se recuperar dela, caso o tipo de um objeto no seja aquele que
voc esperava, uma abordagem bastante inepta. O C# fornece dois operadores bem mais teis que
podem ajudar a fazer um casting de uma maneira muito mais elegante, os operadores is e as.

O operador is
Utilize o operador is para verificar se o tipo de um objeto aquele que voc espera, desta maneira:
Wrappedlnt wi = new W r a p p e d l n t O ;
object o = w i ;
if (o is Wrappedlnt)

{
Wrappedlnt temp = (Wrappedlnt)o; // Isso seguro; o um Wrappedlnt

}
O operador is aceita dois operandos: uma referncia a um objeto esquerda e o nome de um tipo
direita. Se o tipo do objeto referenciado no heap tiver o tipo especificado, is ser avaliado como
true-, caso contrrio, ser avaliado comofalse. O cdigo anterior tenta fazer o casting da referncia
varivel object o somente se ele souber que o casting ser bem-sucedido.

O operador as
O operador <zs desempenha um papel semelhante a is, mas de uma maneira ligeiramente mais abre
viada. Voc utiliza o operador as desta maneira:

Captulo 8

Entendendo valores e referncias

201

Wrappedlnt wi = new W r a p p e d l n t O ;
object o = w i ;
Wrappedlnt temp = o as Wrappedlnt;
if (temp != nuTI)
/ / O casting foi bem-sucedido

Como ocorre com o operador is, o operador as recebe um objeto e um tipo como seus operandos. 0
runtime tenta fazer o casting do objeto para o tipo especificado. Se o casting for bem-sucedido, o
resultado ser retornado, e, neste exemplo, ele atribudo varivel Wrappedlnt temp. Se o casting
for malsucedido, o operador ser avaliado como o valor nutt e atribuir isso a temp.
H um pouco mais sobre operadores is e as do que descrito aqui e iremos discuti-los novamente no
Captulo 12.

Ponteiros e cdigo inseguro


Esta seo serve apenas para sua informao e dirige-se aos desenvolvedores que esto fami
liarizados com C ou C+ + . Se voc um iniciante em programao, sinta-se livre para pular
esta seo!
Se voc j desenvolveu programas em linguagens como C ou C++, deve estar familiarizado
com grande parte da discusso deste captulo sobre referncias a objetos. Embora nem o C
nem o C++ tenham tipos-referncia explcitos, as duas linguagens tm uma construo que
fornece uma funcionalidade semelhante - os ponteiros.
Um ponteiro uma varivel que armazena o endereo ou uma referncia a um item na me
mria (no heap ou na pilha). Uma sintaxe especial usada para identificar uma varivel como
um ponteiro. Por exemplo, a instruo a seguir declara a varivel p i como um ponteiro para
um nmero inteiro:
int *pi ;

Embora a varivel p i seja declarada como um ponteiro, na verdade ela no apontar para lu
gar algum at que voc a inicialize. Por exemplo, para fazer p i apontar para a varivel do tipo
inteiro i, voc pode usar as instrues a seguir e o operador de endereo (&), o que retorna
0 endereo de uma varivel:
1 nt *pi ;
i nt i = 99;
pi = & i ;

Voc pode acessar e modificar o valor mantido na varivel / por meio da varivel ponteiro pi,
como mostrado aqui:
*pi = 100;

Esse cdigo atualiza o valor da varivel i para 100, uma vez que p i aponta para a mesma
posio da memria que a varivel /'.

202

Parte II

Entendendo a linguagem C#

Um dos principais problemas que os desenvolvedores que aprendem C e C++ tm enten


der a sintaxe usada pelos ponteiros. O operador * tem pelo menos dois significados (alm
de ser o operador aritmtico da multiplicao) e sempre h uma grande confuso sobre
quando usar & em vez de *. A outra questo com os ponteiros que fcil apontar para
algo invlido ou simplesmente esquecer de apontar para algo, e ento tentar referenciar
esse algo. O resultado ser lixo ou um programa que falhar com um erro porque o sistema
operacional detecta uma tentativa de acessar um endereo invlido na memria. Tambm
h toda uma srie de falhas de segurana em muitos sistemas existentes que resultam de
um gerenciamento inadequado dos ponteiros; alguns ambientes (no o Microsoft Windows)
falham em impor a verificao de que um ponteiro no referencia a memria pertencente a
outro processo, abrindo a possibilidade de que dados confidenciais sejam comprometidos.
As variveis de referncia foram adicionadas ao C# para evitar todos esses problemas. Se
realmente quiser, voc pode continuar a utilizar ponteiros no C#, mas deve marcar o cdigo
como unsafe. A palavra-chave unsafe pode ser usada para marcar um bloco de cdigo ou
um mtodo inteiro, como mostrado aqui:
public static void Main(string [] args)

{
i nt x = 99, y = 100;
unsafe

{
swap (&x, & y ) ;

>
Console.WriteLine("x is now {0}, y is now {1}", x, y ) ;

}
public static unsafe void swap(int *a, int *b)

{
int temp;
temp = *a;
*a = *b;
*b = temp;

}
Ao compilar programas que contm um cdigo inseguro, voc deve especificar a opo /unsafe.
Um cdigo inseguro tambm afeta a maneira como a memria gerenciada; objetos criados
em cdigo inseguro so chamados de no gerenciados. Discutiremos esse problema com
mais detalhe no Captulo 14.

Neste captulo, voc aprendeu algumas diferenas importantes entre tipos-valor, que armazenam
seus valores diretamente na pilha, e tipos-referncia, que referenciam indiretamente seus objetos no
heap. Voc tambm aprendeu a utilizar as palavras-chave re ft out nos parmetros de mtodo para
obter acesso aos argumentos. Voc j viu como a atribuio de um valor (por exemplo, o in t 42) a
uma varivel da classe System. Object cria uma cpia boxed do valor no heap e ento faz a varivel
System .Object referenciar essa cpia. Voc tambm viu como a atribuio de uma varivel de um
tipo-valor (como um in t) a uma varivel da classe System.Object copia o (ou faz o unbox do) valor na
classe System.Object para a memria utilizada pelo int.
Se voc quiser seguir para o prximo captulo agora:
Mantenha o Visual Studio 2010 em execuo e v para o Captulo 9.
Se quiser sair do Visual Studio 2010:
No menu File, clique em Exit. Se vir uma caixa de dilogo Save, clique em Yes e salve o projeto.

Captulo 8

203

Entendendo valores e referncias

Referncia rpida do Captulo 8


Para

Faa isto

Copiar uma varivel de tipo-valor

Simplesmente faa uma cpia. Como a varivel um


tipo-valor, voc ter duas cpias da mesma varivel.
Por exemplo:
i nt i = 42;
i nt copyi = i ;

Copiar uma varivel de tipo-referncia

Simplesmente faa uma cpia. Como a varivel um


tipo-referncia, voc ter duas referncias ao mesmo
objeto. Por exemplo:
C i r c l e c = new C i r c l e ( 4 2 ) ;
Ci r c le re fc = c;

Declarar uma varivel que possa armazenar um tipo-valor ou


o valor null

Declare a varivel utilizando o modificador ? com o


tipo. Por exemplo:
i n t ? i = n u ll ;

Passar um argumento a um parmetro ref

Prefixe o argumento com a palavra-chave ref. Isso


torna o parmetro um alias para o argumento real
em vez de uma cpia do argumento. 0 mtodo pode
mudar o valor do parmetro e essa mudana ser efe
tuada no argumento real e no em uma cpia local.
Por exemplo:
s t a t i c void MainO
{
i n t arg = 42;
DoWorkCref a r g ) ;
C o n s o le . W r i t e L i n e ( a r g ) ;
}

Passar um argumento a um parmetro out

Prefixe o argumento com a palavra-chave out. Isso


torna o parmetro um alias para o argumento real
em vez de uma cpia do argumento. 0 mtodo deve
atribuir um valor ao parmetro e esse valor se torna o
argumento real. Por exemplo:
s t a t i c void MainO
{
i n t arg = 42;
DoWork(out a r g ) ;
C o n s o le . W r i t e L i n e ( a r g ) ;
}

Fazer o boxing de um valor

Inicialize ou atribua uma varivel do tipo object ao


valor. Por exemplo:
o bject o = 42;

Fazer o unboxing de um valor

Faa o casting da referncia de objeto que referencia o


valor boxed para o tipo da varivel. Por exemplo:
in t i = (in t)o ;
(continua)

204

Parte II

Entendendo a linguagem C#

Para

Faa isto

Fazer o casting de um objeto de forma segura

Utilize o operador is para testar se o casting vlido.


Por exemplo:
Wrappedlnt wi = new WrappedlntC);
ob je ct o = wi ;
i f (o i s Wrappedlnt)

{
Wrappedlnt temp = (Wrappedlnt)o;

}
Outra alternativa usar o operador as para fazer o
casting e testar se o resultado nuul. Por exemplo:
Wrappedlnt wi = new WrappedlntC);
ob ject o = w i ;
Wrappedlnt temp = o as Wrappedlnt;
i f (temp != n u l l )

Captulo 9

Criando tipos-valor com


enumeraes e estruturas
Neste captulo, voc vai aprender a:
D eclarar um tip o en u m erad o .
Criar e utilizar um tip o en u m erad o .
D eclarar um tipo-estrutura.
Criar e utilizar um tipo-estrutura.
Explicar as d iferenas de c o m p o rta m e n to en tre u m a estru tu ra e um a classe.

No Captulo 8, Entendendo valores e referncias", voc aprendeu sobre os dois tipos fundamentais
do Microsoft Visual C#: os tipos-valor e os tipos-referncia. Uma varivel de tipo-valor armazena seu
valor diretamente na pilha, enquanto uma varivel de tipo-referncia armazena uma referncia a
um objeto no heap. No Captulo 7, Criando e gerenciando classes e objetos, voc aprendeu a criar
seus prprios tipos-referncia definindo classes. Neste captulo, voc aprender a criar seus prprios
tipos-valor.
0 C# suporta duas espcies de tipos-valor: enumeraes e estruturas. Veremos uma de cada vez.

Trabalhando com enum eraes


Suponha que voc queira representar as estaes do ano em um programa. Voc poderia utilizar os
valores inteiros 0, 1, 2 e 3 para descrever a primavera, o vero, o outono e o inverno, respectivamen
te. Esse sistema funcionaria, mas no muito intuitivo. Se voc usasse o valor inteiro 0 no cdigo,
no seria bvio que um 0 representa primavera. Alm disso, no seria uma soluo muito slida. Por
exemplo, se voc declarar uma varivel int chamada season, no h como impedir que voc atribua
a ela um valor inteiro vlido alm de 0, 1, 2 ou 3. O C# oferece uma soluo melhor. Voc pode criar
uma enumerao (s vezes chamada de tipo enum ), cujos valores esto limitados a um conjunto de
nomes simblicos.

Declarando uma enumerao


Defina uma enumerao utilizando a palavra-chave enum , seguida por um conjunto de smbolos que
identificam os valores vlidos que o tipo pode ter, includos entre chaves. Veja como declarar um tipo

206

Parte II

Entendendo a linguagem C#

enumerado chamado Season, cujos valores literais esto limitados aos nomes simblicos Spring,
Summer, Fall e Winter-.
enum Season { S prin g, Summer, F a l l , Winter }

Utilizando uma enumerao


Depois que voc declarar um tipo enumerado, poder utiliz-lo exatamente como qualquer outro
tipo. Se o nome da enumerao for Season, voc pode criar variveis do tipo Season, campos do tipo
Season e parmetros do tipo Season, como mostrado neste exemplo:
enum Season { S p rin g , Summer, F a l l , Winter }
class Example

{
p u b lic void MethodCSeason parameter)

{
Season l o c a lV a r ia b le ;

}
p r i v a t e Season currentSeason;

}
Para que o valor de uma varivel de tipo enumerado possa ser lido, necessrio atribuir-lhe um
valor. Voc s pode atribuir um valor definido pela enumerao a uma varivel do tipo enumerado.
Por exemplo:
Season c o l o r f u l = S e a so n .F a ll;
C o n sole.W riteLine(co lorful); //escreve ' F a l l '

Observe que voc tem de escrever Season.Fall em vez de Fali. Todos os nomes literais de enumeraes
tm escopo definido pelo seu tipo enumerado. Isso muito til porque permite que diferentes tipos
enumerados coincidentemente contenham literais com o mesmo nome.
Alm disso, observe que, quando voc exibe uma varivel de tipo enumerado utilizando Console. WriteLine, o compilador gera um cdigo que escreve o nome do literal cujo valor corresponde ao valor da
varivel. Se necessrio, voc pode converter explicitamente uma varivel de tipo enumerado em uma
string que representa seu valor atual utilizando o mtodo ToString predefinido que todos os tipos
enumerados automaticamente contm. Por exemplo:

Captulo 9

Criando tipos-valor com enumeraes e estruturas

207

s t r i n g name = c o l o r f u l . T o S t r i n g O ;
C ons ole .W ri te Line(nam e);
// tambm escreve ' F a l i '

Muitos dos operadores padro que podem ser utilizados em variveis do tipo inteiro tambm podem
ser empregadas em variveis de enumerao (exceto os operadores bit a bit e os operadores de des
locamento, que sero abordados no Captulo 16, Utilizando indexadores). Por exemplo, voc pode
comparar a igualdade de duas variveis de enumerao do mesmo tipo utilizando o operador de
igualdade (==) e ainda executar clculos aritmticos em uma varivel de tipo enumerado (embora
o resultado talvez nem sempre tenha um significado!).

Escolhendo valores literais de enumerao


Internamente, uma enumerao associa um valor inteiro a cada elemento da enumerao. Por pa
dro, a numerao inicia em 0 para o primeiro elemento e sobe em incrementos de 1. possvel
recuperar o valor inteiro subjacente de uma varivel de tipo enumerado. Para isso, voc deve fazer
um casting para seu tipo subjacente. Lembre-se da discusso sobre unboxing no Captulo 8, em que
o casting converte os dados de um tipo em outro, desde que a converso seja vlida e significativa.
O fragmento de cdigo a seguir escreve o valor 2 e no a palavra Fali (na enumerao de Season,
Spring 0, Summer 1, Fall 2 e Winter 3):
enum Season { S prin g, Summer, F a l l , Winter }
Season c o l o r f u l = S e a so n .F a ll;
C o n s o l e . W r i t e L i n e C ( i n t ) c o l o r f u l ) ; // escreve ' 2 '

Se preferir, voc pode associar uma constante inteira especfica (como 1) a um literal de enumerao
(como Spring), como no exemplo a seguir:
enenum Season { Spring = 1, Summer, F a l l , Winter }

Importante

O valor inteiro com o qual voc inicializa um literal de enumerao deve ser um valor

constante em tempo de compilao (como 1).

Se voc no fornecer explicitamente um valor inteiro constante a um literal de enumerao, o com


pilador fornecer um valor que 1 unidade maior do que o valor do literal de enumerao anterior,
exceto para o primeiro literal de enumerao, ao qual o compilador fornece o valor padro 0. No
exemplo anterior, os valores subjacentes de Spring, Summer, Fali e Winter so atualmente 1, 2, 3 e 4.
Voc pode dispor de mais de um literal de enumerao ao mesmo valor subjacente. Por exemplo,
no Reino Unido, o outono (Fali) chamado deAutumn. Voc pode agradar s duas culturas como
mostrado a seguir:
enum Season { S prin g, Summer, F a l i , Autumn = F a l i , Winter }

208

Parte II

Entendendo a linguagem C#

Escolhendo o tipo subjacente de uma enumerao


Quando voc declara uma enumerao, os literais de enumerao recebem valores do tipo int. Voc
tambm pode escolher basear sua enumerao em um tipo inteiro subjacente diferente. Por exemplo,
para declarar que o tipo subjacente de Season um short em vez de um int, voc pode escrever o
seguinte:
enum Season : sh ort { S p rin g , Summer, F a l i , Winter }

A principal razo para fazer isso economizar memria; um int ocupa mais memria do que um
short e, se voc no precisa de todo o intervalo de valores disponveis para um int, pode fazer senti
do utilizar um tipo menor de dados.
Voc pode basear uma enumerao em qualquer um dos oito tipos de inteiro: byte, sbyte, short,
ushort, int, uint, long ou ulong. Os valores de todos os literais de enumerao devem estar dentro do
intervalo do tipo base escolhido. Por exemplo, se basear uma enumerao no tipo de dado byte, voc
poder ter no mximo 256 literais (comeando em zero).
Agora que voc sabe como declarar uma enumerao, a prxima etapa utiliz-la. No exerccio a
seguir, voc trabalhar com um aplicativo de Console para declarar e utilizar uma classe de enume
rao que representa os meses do ano.
Crie e utilize u m a e n um era o
1. Inicie o Microsoft Visual Studio 2010 se ele ainda no estiver executando.
2. Abra o projeto StructsAndEnums, localizado na pasta \Microsoft PressWisual CSharp Step by
Step\Chapter 9\StructsAndEnums na sua pasta Documentos.
3. Na janela Code and Text Editor, exiba o arquivo Month.cs.
O arquivo-fonte contm um namespace vazio chamado StructsAndEnums.
4. Adicione uma enumerao chamada Month para modelar os meses do ano dentro do namespace
StructsAndEnums, como mostrado em negrito aqui. Os 12 literais de enumerao para Month
so January a December.
namespace StructsAndEnums

{
enum Month

{
January, February, March, A p r i l ,
May, June, J u l y , August,
September, October, November, December

}
}

Captulo 9

Criando tipos-valor com enumeraes e estruturas

209

5. Exiba o arquivo Program.cs na janela Code and Text Editor.


Como nos exerccios dos captulos anteriores, o mtodo Main chama o mtodo DoWork e captura
todas as excees que ocorrerem.
6. Na janela Code and Text Editor, adicione uma instruo ao mtodo DoWork para declarar uma
varivel chamadafir s t do tipo Month e inicialize-a como Month.January. Adicione outra instru
o para escrever o valor da primeira varivel no console.
O mtodo DoWork deve ser semelhante a este:
s t a t i c void DoWorkO

{
Month f i r s t = Month.January;
Console.Wri te L in e C fi r s t ) ;

7. No menu Debug, clique em Start Without Debugging.

0 Visual Studio 2010 compila e executa o programa. Confirme que a palavra January est es
crita no console.
8. Pressione Enter para fechar o programa e retornar ao ambiente de programao do Visual Stu
dio 2010.
9. Adicione mais duas instrues ao mtodo DoWork para incrementar a varivel first e exibir seu
novo valor no console, como mostrado aqui:
s t a t i c void DoWorkO

{
Month f i r s t = Month.January;
Console.Wri teLi n e ( f i r s t ) ;
first++;
Console.WriteLine(first);

}
10. No menu Debug, clique em Start Without Debugging.
0 Visual Studio 2010 compila e executa o programa. Confirme se as palavrasJanuary t February
esto escritas no console.
Observe que realizar uma operao matemtica (como a operao de incremento) em uma va
rivel de tipo enumerado altera o valor inteiro interno da varivel. Quando a varivel escrita
no console, o valor de enumerao correspondente exibido.

210

Parte II

Entendendo a linguagem C#

11. Pressione Enter para fechar o programa e retornar ao ambiente de programao do Visual Studio 2010.
12. Modifique a primeira instruo no mtodo DoWork para inicializar a primeira varivel para
Month.December, como mostrado em negrito aqui:
s t a t i c void DoWorkO

{
Month f i r s t = Month.December;
C o n so le.W riteLi n e (f i r s t ) ;
f i rst++;
C o n so le.W riteLin e C fi r s t ) ;

}
13. No menu Debug, clique em Start Without Debugging.
O Visual Studio 2010 compila e executa o programa. Desta vez, a palavra December escrita
no console, seguida pelo nmero 12. Embora voc possa realizar a aritmtica em uma enume
rao, se os resultados dessa operao estiverem fora do intervalo dos valores definidos para
o enumerador, tudo o que o runtime pode fazer interpretar o valor da varivel como o valor
inteiro correspondente.
14. Pressione Enter para fechar o programa e retornar ao ambiente de programao do Visual Stu
dio 2010.

Trabalhando com estruturas


Voc viu no Captulo 8 que as classes definem tipos-referncia, que so sempre criados no heap. Em
alguns casos, a classe pode conter to poucos dados que a sobrecarga de gerenciamento do heap se
torna desproporcional. Nesses casos, melhor definir o tipo como uma estrutura. Uma estrutura
um tipo-valor. Como as estruturas so armazenadas na pilha, desde que a estrutura seja razoavel
mente pequena, a sobrecarga de gerenciamento da memria frequentemente reduzida.
Como uma classe, uma estrutura pode ter campos, mtodos e (com uma exceo importante, discuti
da mais adiante neste captulo) construtores prprios.

Tipos-estrutura comuns
Talvez voc no tenha percebido isso, mas j usou estruturas em exerccios anteriores neste
livro. No C#, os tipos numricos primitivos int, long e float so aliases das estruturas System.

Int32, System.Int64 e System.Single, respectivam ente. Essas estruturas tm campos e m to


dos, e voc pode de fato cham ar m todos nas variveis e literais desses tipos. Por exemplo,
todas essas estruturas fornecem um m todo ToString que pode converter um valor numrico
na sua representao de string. As instrues a seguir so todas elas vlidas no C#:

Captulo 9

Criando tipos-valor com enumeraes e estruturas

211

in t i = 9 9 ;
Console.Wri t e L i n e ( i . T o S t r i n g O ) ;
Console.Wri t e L i n e ( 5 5 . T o S t r i n g ( ) ) ;
f l o a t f = 98.765F;
Console.W rite Li n e ( f . T o S t r i n g O ) ;
C o n s ole .W riteLine(9 8.765F . T o S t r i n g O ) ;
Voc no v com frequncia esse uso do m todo ToString, porque o m todo Console.Wri

teLine o cham a auto m aticam ente quando ele necessrio. O uso dos m todos estticos
expostos por essas estruturas m uito mais com um . Por exemplo, nos captulos anteriores
voc utilizou o m todo esttico lnt32.Parse para converter uma string no seu valor inteiro
correspondente. 0 que voc est fazendo invocar o m todo Parse da estrutura Int32:
s t r i n g s = "42";
int i = in t.P a r s e (s );

// exatamente o mesmo que Int32.P ars e

Essas estruturas tam bm incluem alguns cam pos estticos teis. Por exemplo, lnt32.MaxVa-

lue o valor mximo que um int pode arm azenar e lnt32.MinValue o m enor valor que voc
pode armazenar em um int.
A tabela a seguir mostra os tipos prim itivos no C # e seus tipos equivalentes no M icrosoft
NET Framework. Observe que os tipos string e object so classes (tipos-referncia) em vez
de estruturas.

Palavra-chave

Tipo quivalente

Classe ou estrutura

bool

System.Boolean

Estrutura

byte

System. Byte

Estrutura

decimal

System. Decimal

Estrutura

double

System. Double

Estrutura

float

System.Single

Estrutura

int

System.Int32

Estrutura

long

System.Int64

Estrutura

object

System. Object

Classe

sbyte

System.SByte

Estrutura

short

System. Int 16

Estrutura

string

System.String

Classe

uint

System. Ulnt32

Estrutura

ulong

System. UInt 64

Estrutura

ushort

System.Ulntl 6

Estrutura

212

Parte II

Entendendo a linguagem C#

Declarando uma estrutura


Para declarar seu tipo-estrutura, voc utiliza a palavra-chave struct seguida pelo nome do tipo, se
guido pelo corpo da estrutura entre chaves de abertura e fechamento. Por exemplo, eis uma estrutura
chamada Time que contm trs campos public int chamados hours, minutes e seconds:
s t r u c t Time

{
p u b l ic i n t hours, minutes, seconds;

}
Assim como nas classes, na maioria dos casos no recomendvel tornar public os campos de uma
estrutura; no h como controlar os valores armazenados nos campos public. Por exemplo, qualquer
pessoa poderia configurar o valor de minutes ou seconds como um valor maior que 60. Uma ideia
melhor tornar os campos private e fornecer sua estrutura com construtores e mtodos para inicializar e manipular esses campos, como mostrado neste exemplo:
s t r u c t Time

{
p u b l ic T i m e ( i n t hh, i n t mm, i n t ss)

{
hours = hh % 24;
minutes = mm % 60;
seconds = ss % 60;

}
pu b lic i n t Hours O

{
re turn hours;

}
p r i v a t e i n t hours, minutes, seconds;

Utilize estruturas para implementar conceitos simples cujas caractersticas principais so seus valo
res. Ao copiar uma varivel do tipo-valor, voc obtm duas cpias do valor. Por outro lado, ao copiar
uma varivel do tipo-referncia, voc obtm duas referncias ao mesmo objeto. Em resumo, use
estruturas para valores pequenos de dados em que elas sejam to eficientes, ou quase to eficientes
para copiar o valor quanto seriam para copiar um endereo. Utilize classes para dados mais comple
xos a fim de copiar com eficincia.

Captulo 9

Criando tipos-valor com enumeraes e estruturas

213

Entendendo as diferenas entre estrutura e classe


Uma estrutura e uma classe so sintaticamente semelhantes, mas existem algumas diferenas importantes. Vamos examinar algumas dessas diferenas.
Voc no pode declarar um construtor padro (um construtor sem parmetros) para uma estrutura. O exemplo a seguir seria compilado se Time fosse uma classe, mas, como Time uma
estrutura, a compilao falha:
struct Time
{

public TimeQ { ... } // erro de tempo de compilao

A razo por que voc no pode declarar seu prprio construtor padro em uma estrutura que
o compilador sempre gera um. Em uma classe, o compilador s gera o construtor padro se voc
no escrever seu prprio construtor. O construtor padro gerado pelo compilador para uma estrutura sempre define os campos como Ojalse ou nutt- assim como para uma classe. Portanto,
voc deve garantir que um valor de estrutura criado pelo construtor padro se comporte logicamente e faa sentido com esses valores padro. Se no quiser utilizar esses valores padro, voc
pode inicializar os campos como valores diferentes fornecendo um construtor no padro. Mas
se voc no inicializar um campo no seu construtor no padro, o compilador no o inicializar
para voc. Isso significa que voc deve inicializar explicitamente todos os campos em todos os
construtores de estrutura ou receber um erro de tempo de compilao. Por exemplo, embora o
exemplo seguinte seja compilado e inicialize silenciosamente seconds como O se Time fosse uma
classe, como Time uma estrutura, a compilao falha:
struct Time
{
private int hours, minutes, seconds;
public Ti me (int hh, int mm)
{
this. hours = hh;

thi s. minutes = mm;


// erro de tempo de compilao: seconds no inicializado

Em uma classe, voc pode inicializar os campos de instncia no seu ponto de declarao. Em
uma estrutura, isso no possvel. O exemplo a seguir compilaria se Time fosse uma classe,
mas, como Time uma estrutura, ele causa um erro de tempo de compilao:
struct Time
{
private int hours = 0; // erro de tempo de compilao
private int minutes;
private int seconds;

214

Parte II

Entendendo a linguagem C#

A tabela abaixo resume as principais diferenas entre uma estrutura e uma classe.

Pergunta

Estrutura

Classe

Esse um tipo-valor ou um tipo-referncia?

Uma estrutura um tipo-valor.

Uma classe um tipo-referncia.

As instncias so colocadas na pilha ou no


heap?

As instncias de estrutura so
chamadas valores e residem
na pilha.

As instncias de classe so
chamadas objetos e so colo
cadas no heap.

Voc pode declarar um construtor padro?

No

Sim

Se voc declarar seu construtor, o compila


dor ainda gerar o construtor padro?

Sim

No

Se voc no inicializar um campo no seu


construtor, o compilador o inicializar auto
maticamente para voc?

No

Sim

Voc pode inicializar os campos de instn


cia em seus pontos de declarao?

No

Sim

Existem outras diferenas entre classes e estruturas no que se refere herana. Essas diferenas
sero abordadas no Captulo 12.

Declarando variveis de estrutura


Aps ter definido um tipo-estrutura, voc pode utiliz-lo exatamente da mesma maneira que qual
quer outro tipo. Por exemplo, se voc definiu a estrutura Time, pode criar variveis, campos e par
metros do tipo Time, como mostrado neste exemplo:
s t r u c t Time

{
p r i v a t e i n t hours, minutes, seconds;

}
cla ss Example

{
p r i v a t e Time cu rre ntT im e;
p u b l ic void Method(Time parameter)

{
Time l o c a lV a r ia b le ;

}
}

Time? currentTime = n u l l ;

Captulo 9

Criando tipos-valor com enumeraes e estruturas

215

Entendendo a inicializao de estruturas


Anteriormente neste captulo, vimos como os campos em uma estrutura podem ser inicializados
utilizando um construtor. Se voc chamar um construtor, as vrias regras descritas anteriormente
garantiro que todos os campos na estrutura sejam inicializados:
Time now = new Time ( )

O grfico a seguir ilustra os campos dessa estrutura:

Entretanto, como as estruturas so tipos-valor, voc pode criar variveis de estrutura sem chamar
um construtor, como exemplo a seguir:
Time now;

Dessa vez, a varivel criada, mas seus campos permanecem no estado no inicializado. O grfico a
seguir ilustra o estado dos campos na varivel now. Qualquer tentativa de acessar os valores conti
dos nesses campos resultar em um erro de compilao:

Observe que, em ambos os casos, a varivel Time criada na pilha.


Se escreveu seu prprio construtor de estrutura, voc pode tambm utiliz-lo para inicializar uma
varivel de estrutura. Conforme explicado anteriormente neste captulo, um construtor de estrutura
deve sempre inicializar explicitamente todos os seus campos. Por exemplo:
s t r u c t Time

{
p ri v a te i n t hours, minutes, seconds;

216

Parte II

Entendendo a linguagem C#

p u b lic T i m e ( i n t hh, i n t mm)

{
hours = hh;
minutes = mm;
seconds = 0;

}
}
O exemplo a seguir inicializa now ao chamar um construtor definido pelo usurio:
Time now = new T i me(12, 3 0 );

A ilustrao a seguir mostra o efeito desse exemplo:

Est na hora de colocar esse conhecimento em prtica. No exerccio a seguir, voc ir criar e utilizar
uma estrutura para representar uma data.
Crie e utilize u m tip o -e s tru tu ra
1. No projeto StructsAndEnums, exiba o arquivo Date.cs na janela Code and Text Editor.

2. Adicione uma estrutura chamada Date dentro do namespace StructsAndEnums.


Essa estrutura deve conter trs campos privados: umy ea r nomeado do tipo int, um month no
meado do tipo Month (utilizando a enumerao que voc criou no exerccio anterior) e um day
nomeado do tipo int. A estrutura Date deve ser igual a esta:
s t r u c t Date

{
p r i v a t e i n t year;
p r i v a t e Month month;
p r i v a t e i n t day;

}
Agora considere o construtor padro que o compilador ir gerar para Date. Esse construtor defin ty ea r como O, month como O (o valor de January) e day como O. O valory ear 0 no vlido
(porque no h ano 0) e o valor day tambm no vlido (porque cada ms comea no dia 1).
Uma maneira de corrigir esse problema converter os valoresy ear e day implementando a es
trutura Date para que, quando o campoy ear contiver o valor Y, esse valor represente o ano Y +
1900 (ou voc pode selecionar um sculo diferente se preferir) e, quando o campo day contiver

Captulo 9

Criando tipos-valor com enumeraes e estruturas

217

o valor D, esse valor represente o dia D + 1.0 construtor padro ir ento configurar os trs
campos como valores que representam a data de 1 de janeiro de 1900.
3. Adicione um construtor public estrutura Date. Esse construtor deve receber trs parmetros:
um int chamado ccyy parayear, um Month chamado mm para month e um int chamado dd para
day. Utilize esses trs parmetros para inicializar os campos correspondentes. Um campoy ear
com o valor Y representa o ano Y + 1900, portanto, voc precisa inicializar o campo y ea r com
o valor ccyy - 1900. Um campo day com o valor D representa o dia D + 1, assim voc precisa
inicializar o campo day com o valor dd - 1.
A estrutura Date agora deve se parecer com isto (o construtor mostrado em negrito):
s t r u c t Date

{
p ri v a te i n t year;
p ri v a te Month month;
p ri v a te i n t day;
public Date(int ccyy, Month mm, int dd)

{
this.year = ccyy - 1900;
t h i s . m o n t h = mm;

this.day = dd - 1;

}
}
4. Adicione um mtodo public chamado ToString estrutura Date aps o construtor. Esse mtodo
no recebe argumento algum, e retorna uma representao da data na forma de string. Lembre
te, o valor do campoy ear representay ear + 1900 e o valor do campo day representa day + 1.

O mtodo ToString deve ser semelhante a este:


pu b lic o v e rrid e s t r i n g T o S t r i n g O

{
s t r i n g data = S t r i n g . F o r m a t (" { 0 } { 1 } { 2 } " , th is .m onth , t h i s . d a y + 1,
t h i s . y e a r + 1900);
return data;

218

Parte II

Entendendo a linguagem C#

O mtodo Format da classe SCrng permite formatar dados, e opera de modo semelhante ao
do mtodo Console. WriteLine, exceto pelo fato de que, em vez de exibir dados no console, ele
retorna o resultado formatado como uma string. Neste exemplo, os parmetros posicionais so
substitudos pelas representaes de texto dos valores do campo month, a expresso this.day
+ / , e a expresso this.year + 1900. O mtodo ToStrng retorna a string formatada como seu
resultado.
5. Exiba o arquivo Program.cs na janela Code and TextEditor.
6. No mtodo DoWork, transforme em comentrios as quatro instrues existentes, adicione um
cdigo ao mtodo DoWork para declarar uma varivel local chamada defaultDate e inicialize-a
como um valor Date construdo por meio do construtor Date padro. Adicione outra instruo ao
mtodo DoWork para escrever a varivel defaultDate no console chamando Console. WriteLine.

O mtodo DoWork agora deve ser semelhante a este:


s t a t i c void DoWorkO

{
Date defau ltDate = new Date O ;
C ons ole. W r it e L i n e (d e f a u l t D a t e ) ;

}
7. No menu Debug, clique em Start Without Debugging para compilar e executar o programa. Ve
rifique se a data January 1 1900 foi escrita no console.
8. Pressione a tecla Enter para retornar ao ambiente de programao do Visual Studio 2010.
9. Na janela Code and Text Editor, retorne ao mtodo DoWork e adicione mais duas instrues.
Na primeira instruo, declare uma varivel local chamada weddingAnniversary e inicialize-a
como July 4, 2010. (Ironicamente, eu me casei no Dia da Independncia e, consequentemente,
perdi a minha independncia!) Na segunda instruo, escreva o valor de weddingAnniversary
no console.
O mtodo DoWork deve agora ser semelhante a este:
s t a t i c void DoWorkO

{
Date weddingAnniversary = new Date(2010, M on th .J u ly , 4 ) ;
Console. Wri te L i ne(weddi ngAnni v e r s a r y ) ;

Captulo 9

Criando tipos-valor com enumeraes e estruturas

219

10. No menu Debug, dique em Start Without Debugging. Confirme se/uly 4, 2010 est escrito no
console abaixo da informao anterior.
11. Pressione Enter para fechar o programa.

Copiando variveis de estrutura


Voc s pode inicializar ou atribuir uma varivel de estrutura a outra varivel de estrutura se a do
lado direito estiver totalmente inicializada (ou seja, se todos os seus campos estiverem preenchidos
com valores vlidos e no com valores indefinidos). O exemplo a seguir compilado porque now est
completamente inicializada. O grfico mostra os resultados da atribuio.
Time now = new Ti me(12, 30);
Time copy = now;

PILHA

Time now = new Time(12, 30);

now.hours

12

now.minutes

30

now.seconds

Time copy = now;

copy.hours

12

copy.minutes

30

copy.seconds

A compilao do exemplo a seguir falha porque now no est inicializada:


Time now;

Time copy = now; // erro de tempo de compilao: now no foi atribuda

Quando voc copia uma varivel de estrutura, cada campo posicionado no lado esquerdo definido
diretamente a partir do campo correspondente no lado direito. Essa cpia ocorre como uma operao
simples e rpida, que copia o contedo da estrutura inteira e que nunca lana uma exceo. Compare
esse comportamento com a ao equivalente caso Time fosse uma classe, em que as duas variveis
(now e copy) terminariam fazendo referncia ao mesmo objeto no heap.

Nota Os programadores C++ devem observar que esse comportamento de cpia no pode ser
personalizado.

220

Parte II

Entendendo a linguagem C#

No ltimo exerccio deste captulo, voc vai comparar o comportamento da cpia de uma estrutura
com o de uma classe.
C o m p a re o c o m p o rta m e n to de u m a e strutu ra co m o de u m a classe
1. No projeto StructsAndEmms, exiba o arquivo Date.cs na janela Code and Text Editor.
2. Adicione o seguinte mtodo estrutura Date. Esse mtodo adianta a data na estrutura em um
ms. Se aps avanar a data o valor do campo month ultrapassar o ms de dezembro, esse c
digo redefinir o ms com janeiro e incrementar o valor do campoy ear em 1.
p u b lic void AdvanceMonthO

{
this.month++;
i f (th is .m o nth == Month.December + 1)

{
this.month = Month.January;
t h is .y e a r + + ;

}
}
3. Exiba o arquivo Program.cs na janela Code and Text Editor.
4. No mtodo DoWork, transforme em comentrios as duas primeiras instrues que criam e exi
bem o valor da varivel defaultDate.
5. Adicione o seguinte cdigo, mostrado em negrito, ao final do mtodo DoWork. Esse cdigo gera
uma cpia da varivel weddingAnniversary, chamada weddingAnniversaryCopy, e imprime o
valor dessa nova varivel.
s t a t i c void DoWorkO

{
Date weddingAnniversaryCopy = weddingAnniversary;
Console.WriteLineC'O valor da cpia {0}", weddingAnniversaryCopy);

}
6. Adicione as seguintes instrues ao final do mtodo DoWork, que chamam o mtodo AdvanceMonth da varivel weddingAnniversary, e depois exibem o valor das variveis weddingAnniver
sary e weddingAnniversaryCopy.
s t a t i c void DoWorkO

{
weddi ngAnni v ers ary Copy. AdvanceMonthO ;
Con sole.W rite LineC'O novo v a lo r de weddingAnniversary { 0 } " , weddingAnniversary);
Console .W ri te LineC 'O v a l o r da cpia { 0 } " , weddingAnniversaryCopy);

Captulo 9

Criando tipos-valor com enumeraes e estruturas

221

7. No menu Debug, clique em Start Without Debugging para construir e executar o aplicativo.
Verifique se a janela do console exibe as seguintes mensagens:
J u l y 4 2010
0 v a lo r da cpia J u l y 4 2010
0 novo v a lo r de weddingAnniversary J u l y 4 2010
O v a lo r da cpia August 4 2010

A primeira mensagem mostra o valor inicial da varivel weddingAnniversary (July 4 2010). A


segunda mensagem exibe o valor da varivel weddingAnniversaryCopy. Observe que ela contm
uma cpia da data armazenada na varivel weddingAnniversary (July 4 2010). A terceira men
sagem exibe o valor da varivel weddingAnniversary aps mudar o ms da varivel weddingAn
niversaryCopy para August 4 2010. Observe que seu valor original de July 4 2010 no mudou.
A ltima mensagem exibe o valor da varivel weddingAnniversaryCopy. Voc pode ver que esse
valor mudou para August 4 2010.
8. Pressione Enter e retorne ao Visual Studio 2010.
9. Exiba o arquivo Date.cs na janela Code and Text Editor.
10. Mude a estrutura Date para uma classe, como mostrado em negrito no exemplo de cdigo a
seguir:
class Date

{
}
11. No menu Debug, clique em Start Without Debugging para construir e executar o aplicativo no
vamente. Verifique se a janela de console exibe as seguintes mensagens:
J u l y 4 2010
0 v a lo r da cpia J u l y 4 2010
0 novo v a lo r de weddingAnniversary August 4 2010
0 v a lo r da cpia August 4 2010

As duas primeiras mensagens e a quarta mensagem so as mesmas anteriores. Entretanto, a


terceira mensagem mostra que o valor da varivel weddingAnniversary mudou para August
4 2010. Lembre-se de que uma estrutura um tipo-valor, e ao copiar uma varivel tipo-valor,
voc cria uma cpia de todos os dados contidos na varivel. Contudo, uma classe um tipo-referncia, e ao copiar uma varivel tipo-referncia, voc copia uma referncia varivel original.
Se os dados contidos em uma varivel de classe forem alterados, todas as referncias a essa
varivel vero as alteraes.
12. Pressione Enter para voltar ao Visual Studio 2010.
Neste captulo, vimos como criar enumeraes e estruturas. Voc conheceu algumas semelhanas e
diferenas entre uma estrutura e uma classe e vimos como definir construtores para inicializar os
campos em uma estrutura. Voc tambm aprendeu a representar uma estrutura como uma string,
substituindo o mtodo ToString.
Se voc quiser seguir para o prximo captulo agora:
Mantenha o Visual Studio 2010 em execuo e v para o Captulo 10.
Se quiser sair do Visual Studio 2010:
No menu File, clique em Exit. Se vir uma caixa de dilogo Save, clique em Yes e salve o projeto.

222

Parte II

Entendendo a linguagem C#

Referncia rpida do Captulo 9


Para

Faa isto

Declarar uma enumerao

Escreva a palavra-chave enum, seguida pelo nome do tipo,


seguido por um par de chaves contendo uma lista separada
por vrgulas dos nomes literais da enumerao. Por exemplo:
enum Season { S prin g, Summer, F a l i , Winter }

Declarar uma varivel de tipo enumerado

Escreva o nome da enumerao esquerda seguido pelo


nome da varivel, seguido por ponto e vrgula. Por exemplo:
Season currentSea son;

Atribuir uma varivel de tipo enumerado a um valor

Escreva o nome do literal de enumerao em combinao


com o nome da enumerao qual ele pertence. Por exem
plo:
currentSeason = S prin g;
// erro
currentSeason = Season.Spring; // c orreto

Declarar um tipo-estrutura

Escreva a palavra-chave struct, seguida pelo nome do tipo-estrutura, seguido pelo corpo da estrutura (os construtores,
mtodos e campos). Por exemplo:
s t r u c t Time
{
p u b l ic T i m e ( i n t hh, i n t mm, i n t ss)
{ ... }
p r i v a t e i n t hours, minutes, seconds;
}

Declarar uma varivel de estrutura

Escreva o nome do tipo-estrutura, seguido pelo nome da va


rivel, seguido por um ponto e vrgula. Por exemplo:
Time now;

Inicializar uma varivel de estrutura com um valor

Inicialize a varivel com um valor de estrutura criado chaman


do o construtor de estrutura. Por exemplo:
Time lunch = new Time(12, 30, 0 ) ;

Captulo 10

Utilizando arrays e colees


Neste captulo, voc vai aprender a:
Declarar, inicializar e utilizar variveis de array.
Declarar, inicializar e utilizar variveis d e diversos tip o s de co leo.

Voc j viu como criar e utilizar tipos diferentes de variveis. Mas todos os exemplos de variveis
vistos at agora tm algo em comum - eles armazenam informaes sobre um nico item (in tjlo a t ,
Circle, Date e assim por diante). O que acontece se voc precisar manipular um conjunto de itens?
Uma soluo criar uma varivel para cada item do conjunto, mas isso levanta vrias questes
adicionais: de quantas variveis voc precisa? Como voc deve nome-las? Se precisasse executar
a mesma operao em cada item do conjunto (como incrementar cada uma das variveis em um
conjunto de inteiros), como evitaria a repetio excessiva de cdigo? Essa soluo pressupe que ao
escrever o programa voc saiba, de quantos itens precisar, mas com que frequncia isso acontece?
Por exemplo, se voc estiver escrevendo um aplicativo que l e processa os registros de um banco de
dados, quantos registros esto no banco de dados e qual a probabilidade de esse nmero mudar?
Arrays e colees fornecem mecanismos que ajudam a resolver os problemas colocados por essas
questes.

O que um array?
Um array uma sequncia no ordenada de elementos. Todos os elementos em um array tm o
mesmo tipo (ao contrrio dos campos em uma estrutura ou classe, que tm tipos diferentes). Os
elementos de um array residem em um bloco contguo da memria e so acessados por meio de um
ndice inteiro (ao contrrio dos campos em uma estrutura ou classe, que so acessados pelo nome).

Declarando variveis de array


Voc declara uma varivel de array especificando o nome do tipo de elemento, seguido por um par
de colchetes, seguido pelo nome da varivel. Os colchetes significam que a varivel um array. Por
exemplo, para declarar um array de variveis int chamada pins, voc escreveria:
i n t [ ] pins; // Personal I d e n t i f i c a t i o n Numbers

Os programadores em Microsoft Visual Basic devem perceber o uso de colchetes no lugar de parn
teses. Os programadores C e C++ devem observar que o tamanho do array no faz parte da decla
rao. Os programadores Java devem observar que voc tem que colocar os colchetes antes do nome
da varivel.

224

Parte II

Entendendo a linguagem C#

Criando uma instncia de array


Os arrays so tipos-referncia, independentemante do tipo dos seus elementos. Isso significa que
uma varivel de array referencia um bloco contguo de memria armazenando elementos de array
no heap, assim como uma varivel de classe referencia um objeto no heap, e esse bloco de memria
no armazena seus elementos de array diretamente na pilha, como uma estrutura armazena. (Para
recapitular valores e referncias, e as diferenas entre pilha e heap, consulte o Captulo 8, Enten
dendo valores e referncias.) Lembre-se de que, quando voc declara uma varivel de classe, a me
mria no alocada para o objeto at que voc crie a instncia utilizando new. Os arrays seguem as
mesmas regras - ao declarar uma varivel de array, voc no declara seu tamanho. Voc especifica o
tamanho de um array somente quando realmente cria uma instncia do array.
Para criar uma instncia de array, voc utiliza a palavra-chave new seguida pelo tipo de elemento,
seguido pelo tamanho do array que voc est criando entre colchetes. Criar um array tambm ini
cializa seus elementos utilizando os valores padro agora familiares (O, null oufa ls e , dependendo
se o tipo numrico, uma referncia ou um booleano, respectivamente). Por exemplo, para criar e
inicializar um novo array de quatro inteiros para a varivel pins declarada anteriormente, voc es
creve o seguinte:
pins = new i n t [ 4 ] ;

A ilustrao a seguir oferece uma representao visual dos efeitos dessa instruo:

PILHA

HEAP

int [ ] pins
int [ ] pins;
int [ ] pins
pins = new int[4];

0
_

0
.

Captulo 10

Utilizando arrays e colees

225

O tamanho de uma instncia de array no tem de ser uma constante; ela pode ser calculada em tem
po de execuo, como mostrado neste exemplo:
i n t s iz e = i n t . P a r s e ( C o n s o l e . R e a d L i n e O ) ;
i n t [ ] pins = new i n t [ s i z e ] ;

Voc pode criar um array cujo tamanho 0. Isso talvez parea estranho, mas til em situaes em
que o tamanho do array determinado dinamicamente e pode at ser 0. Um array de tamanho 0 no
um array null.

Inicializando variveis de array


Quando voc cria uma instncia de array, todos os elementos dessa instncia so inicializados com
um valor padro dependendo do seu tipo. Se preferir, voc pode modificar esse comportamento e inicializar os elementos de um array com valores especficos. Voc consegue isso fornecendo uma lista
de valores separados por vrgula e entre chaves. Por exemplo, para inicializarpins como um array de
quatro variveis int cujos valores so 9 , 3 , 7 e 2, escreva isto:
i n t [ ] pins - new i n t [ 4 ] { 9, 3, 7, 2 } ;

Os valores entre as chaves no precisam ser constantes. Eles podem ser valores calculados em tempo
de execuo, como neste exemplo:
Random r = new RandomO;
i n t [ ] pins = new i n t [ 4 ] { r . N e x t O % 10, r . N e x t O % 10,
r . N e x t O % 10, r . N e x t O % 10 } ;

O nmero de valores entre as chaves deve corresponder exatamente ao tamanho da instncia do


array que est sendo criado:
i n t [ ] pins = new i n t [ 3 ] { 9, 3, 7, 2 } ; // erro de tempo de compilao
i n t [ ] pins = new i n t [ 4 ] { 9, 3, 7 } ; // erro de tempo de compilao
i n t [ ] pins = new i n t [ 4 ] { 9, 3, 7, 2 } ; // ok

226

Parte II

Entendendo a linguagem C#

Ao inicializar uma varivel de array, voc pode omitir a expresso new e o tamanho do array. O com
pilador calcula o tamanho a partir do nmero de inicializadores e gera o cdigo para criar o array.
Por exemplo:
i n t [ ] pins = { 9 , 3, 7, 2 } ;

Se criar um array de estruturas, voc poder inicializar cada estrutura do array chamando o constru
tor da estrutura, como mostrado neste exemplo:
T im e [] schedule = { new T im e (1 2 ,3 0 ) , new T im e (5 ,3 0 ) } ;

Criando um array implicitamente tipado


Quando voc declara um array, o tipo do elemento precisa corresponder ao tipo dos elementos que
voc quer armazenar no array. Por exemplo, se declarar/rs como um array de int, como mostrado
nos exemplos anterior, voc no poder armazenar um double, uma strng, uma struct ou qualquer
coisa que no seja um int nesse array. Se especificar uma lista de inicializadores ao declarar um
array, voc poder deixar o compilador C# inferir o tipo real dos elementos no array para voc,
assim:
v ar names = n e w [ ] { " J o h n " , "D ia na", "James", " F r a n c e s c a " };

Neste exemplo, o compilador C# determina que a varivel names um array de strings. Vale indicar
algumas peculiaridades sintticas nessa declarao. Primeiro, voc omite os colchetes do tipo; a
varivel names nesse exemplo declarada simplesmente como var e no var[J. Segundo, voc deve
especificar o operador new e os colchetes antes da lista inicializadora.
Se utilizar essa sintaxe, voc precisar assegurar que todos os inicializadores tm o mesmo tipo. O
prximo exemplo causa o erro de tempo de compilao No best type found for implicitly typed ar
ray (No foi possvel encontrar um tipo melhor para o array implicitamente tipado):
var bad = n e w [] { " Jo h n " , "D ia na", 99, 100};

Mas, em alguns casos, o compilador converter elementos em um tipo diferente se isso fizer sentido.
No cdigo a seguir, o array numbers um array de double porque as constantes 3 .5 e 99.999 so
ambas double e o compilador C# pode converter os valores inteiros / e 2 em valores double-.
var numbers = n e w [ ] { l , 2, 3 .5 , 9 9.999 };

Geralmente, melhor evitar misturar tipos esperando que o compilador converta-os para voc.
Arrays implicitamente tipados so mais teis quando voc est trabalhando com tipos annimos,
descritos no Captulo 7, Criando e gerenciando classes e objetos. O cdigo a seguir cria um array de
objetos annimos, cada um contendo dois campos que especificam o nome e a idade dos membros
da minha famlia (sim, sou mais jovem que minha esposa):

Captulo 10

va r names = new[] { new


new
new
new

{
{
{
{

Name
Name
Name
Name

=
=
=
=

Utilizando arrays e colees

227

" Jo h n ", Age = 44 } ,


"Dia na", Age = 45 } ,
"James", Age = 17 } ,
"France sca ", Age = 1 5 } } ;

Os campos nos tipos annimos devem ser os mesmos para cada elemento do array.

Acessando um elemento individual de um array


Para acessar um elemento individual de um array, voc deve fornecer um ndice indicando que ele
mento voc quer. Por exemplo, voc pode ler o contedo do elemento 2 do array pins para armazen-lo em uma varivel int utilizando o cdigo a seguir:
i n t myPin;
myPin = p i n s [ 2 ] ;

Da mesma maneira, voc pode alterar o contedo de um array atribuindo um valor a um elemento
indexado:
myPin = 1645;
pins [2 ] = myPin;

Os ndices do array so baseados em zero. O elemento inicial de um array reside no ndice 0 e no no


ndice 1. Um valor de ndice de 1 acessa o segundo elemento.
Todo acesso aos elementos do array verificado quanto aos limites. Se voc especificar um ndice
menor que 0 ou maior ou igual ao tamanho do array, o compilador lanar uma IndexOutOfRangeException, como neste exemplo:
try

{
i n t [ ] pins = { 9, 3, 7, 2 } ;
C o n s o l e . W r i t e L i n e ( p i n s [ 4 ] ) ; // e r r o , o 4 o e ltim o elemento est no i n d ic e 3

}
catch (IndexOutOfRangeException ex)

{
}

Iterando por um array


Todos os arrays so instncias da classe System.Array no Microsoft .NET Framework, e essa clas
se define algumas propriedades e mtodos teis. Por exemplo, voc pode consultar a propriedade
Length para descobrir quantos elementos um array contm e iterar por todos os elementos de um
array utilizando uma instruofo r . O cdigo de exemplo a seguir escreve os valores dos elementos
do array pins no console:

228

Parte II

Entendendo a linguagem C#

i n t [ ] pins = { 9, 3, 7, 2 } ;
f o r ( i n t index = 0; index < p in s.Le n g th ; index++)

{
i n t pin = p i n s [ i n d e x ] ;
Console.W riteLine(pin);

comum que novos programadores se esqueam de que arrays iniciam no elemento 0 e que o ltimo
elemento est na posio Length - 1. O C# fornece a instruoforeach para que voc possa iterar
pelos elementos de um array sem se preocupar com essas questes. Por exemplo, veja a instruo/br
anterior reescrita como uma instruoforeach equivalente;
i n t [ ] pins = { 9, 3, 7, 2 } ;
foreach ( i n t pin in p ins)

{
Console .Wri t e L i n e ( p i n ) ;

}
A varivelforeach declara uma varivel de iterao (neste exemplo, int pin ) que recebe automati
camente o valor de cada elemento do array. O tipo dessa varivel deve corresponder ao tipo dos
elementos no array. A instruoforeach a maneira preferida de iterar por um array; ela expressa a
inteno do cdigo diretamente, e toda a estrutura do loop jor desaparece. Mas em alguns casos voc
ver que melhor reverter para uma instruofor-,
Uma instruoforeach sempre itera por todo o array. Se voc quiser iterar apenas por uma parte
conhecida de um array (por exemplo, a primeira metade) ou pular certos elementos (por exem
plo, a cada trs elementos), mais fcil utilizar uma instruqy&r.
Uma instruoforeach sempre itera do ndice 0 ao ndice Length - 1. Se voc quiser iterar de
trs para frente ou em alguma outra sequncia, mais fcil utilizar uma instruqojbr.
Se o corpo do loop precisa saber o ndice do elemento em vez do valor do elemento, voc ter de
utilizar uma instruofo r.
Se precisar modificar os elementos do array, voc ter de utilizar uma instruofo r . Isso acon
tece porque a varivel de iterao da instruoforeach uma cpia somente-leitura de cada
elemento do array.
Voc pode declarar a varivel de iterao como var e deixar o compilador C# deduzir o tipo da
varivel a partir do tipo dos elementos no array. Isso especialmente til se voc no conhecer o
tipo dos elementos no array, por exemplo, quando o array contm objetos annimos. O exemplo

Captulo 10

Utilizando arrays e colees

229

a seguir demonstra como possvel iterar pelo array dos membros da famlia mostrado anterior
mente:
v a r names = new[] { new {
new {
new {
new {
foreach ( v a r familyMember

Name = "Joh n", Age = 44 } ,


Name = "Dia na", Age = 45 } ,
Name = "James", Age = 17 } ,
Name = "France sca ", Age = 15 } } ;
in names)

{
Console.WriteLine("Nam e: { 0 } , Age: { 1 } " , familyMember.Name, familyMember.Age);

Copiando arrays
Os arrays so tipos-referncia. (Lembre-se de que um array uma instncia da classe System.Array.)
Uma varivel de array contm uma referncia a uma instncia do array. Isso significa que, ao copiar
uma varivel do array, voc realmente acaba ficando com duas referncias mesma instncia do
array - por exemplo:
i n t [ ] pins = { 9, 3, 7, 2 } ;
i n t [ ] a l i a s = pins; // a l i a s e pins referenciam a mesma i n s t n c ia de a rra y

Neste exemplo, se voc modificar o valor em p in sflj, a modificao tambm ser visvel na leitura
de alias[1],
Se quiser fazer uma cpia da instncia do array (os dados no heap) qual uma varivel de array se
refere, voc ter de fazer duas coisas. Primeiro, voc precisar criar uma nova instncia do mesmo
tipo e do mesmo tamanho do array que est sendo copiado, e depois copiar cada elemento de dado
do array original para o novo array, como neste exemplo:
i n t [ ] pins = { 9, 3, 7, 2 >;
i n t [ ] copy = new i n t [ p i n s . L e n g t h ] ;
f o r ( i n t i = 0 ; i < copy.Leng th; i+ + )

{
co p y[i] = p i n s [ i ] ;

}
Observe que esse cdigo utiliza a propriedade Length do array original para especificar o tamanho
do array.
Copiar um array na verdade um requisito comum de muitos aplicativos - to comum que a classe

System.Array fornece alguns mtodos teis que voc pode empregar para copiar um array em vez de
escrever seu prprio cdigo. Por exemplo, o mtodo CopyTo, que copia o contedo de um array para
outro partindo de um determinado ndice inicial:
i n t [ ] pins = { 9, 3, 7, 2 } ;
i n t [ ] copy = new i n t [ p i n s . L e n g t h ] ;
pi ns.C opyTo (copy, 0 ) ;

230

Parte II

Entendendo a linguagem C#

Outra maneira de copiar os valores utilizar o mtodo esttico da classe System.Array chamado
Copy. Como ocorre com CopyTo, voc deve inicializar o array alvo antes de chamar Copy.
i n t [ ] pins = { 9, 3, 7, 2 } ;
i n t [ ] copy = new i n t [ p i n s . L e n g t h ] ;
Arra y.C opyC pins, copy, cop y. L e n g th );

Ainda outra alternativa utilizar o mtodo de instncia System.Array chamado Clone. Voc pode
chamar esse mtodo para criar um array completo e copi-lo em uma nica ao:
i n t [ ] pins = { 9, 3, 7, 2 } ;
i n t [ ] copy = ( i n t [ ] ) p i n s . C l o n e ( ) ;

Utilizando arrays multidimensionais


Os arrays apresentados at agora abrangiam apenas uma dimenso, e voc pode consider-los listas
simples de valores. possvel criar arrays com mais de uma dimenso. Por exemplo, para criar um
array bidimensional, especifique um array que necessite de dois ndices de inteiros. O cdigo a seguir
gera um array bidimensional de 24 inteiros, chamado item s. Se ajudar, voc pode visualizar um
array bidimensional como uma tabela, onde a primeira dimenso especifica o nmero de linhas e a
segunda especifica o nmero de colunas.
I n t [ , ] items = new i n t [4 , 6 ] ;

Para acessar um elemento no array, fornea dois valores de ndice para especificar a clula que
armazena o elemento. (Uma clula a interseo entre uma linha e uma coluna.) O cdigo a seguir
mostra alguns exemplos que utilizam o array item s:
it e m s[2, 3] = 9 9 ;
it e m s[2, 4] = items [ 2 , 3 ] ;
it em s[2, 4]+ +;

// define o elemento na c l u l a ( 2 , 3) como 99


// copia o elemento con tid o na c l u l a ( 2 , 3 ) para a c l u l a ( 2 , 4 )
// incrementa o v a l o r i n t e i r o na c l u l a ( 2 , 4)

No h limite para o nmero de dimenses que podem ser especificadas para um array. O prximo
exemplo de cdigo cria e utiliza um array chamado cube, que contm trs dimenses. Observe que
necessrio especificar trs ndices para acessar cada elemento do array.
i n t [ , , ] cube = new i n t [ 5 , 5, 5 ] ;
c u b e [ l , 2, 1] = 101;
c u b e [ l , 2, 1] = c u b e [ l, 2, 1] * 3;

Captulo 10

Utilizando arrays e colees

231

Neste estgio, recomendamos cuidado ao criar arrays com mais de trs dimenses. Em termos
especficos, os arrays podem consumir muita memria. O array cube contm 125 elementos (5 *
5 * 5). Um array quadridimensional, em que cada dimenso tem um tamanho de 5, contm 625
elementos. Geralmente, voc deve sempre estar preparado para capturar e tratar as excees de
OutO/MemoiyException, ao utilizar arrays multidimensionais.

Utilizando arrays para jogar cartas


No exerccio a seguir, voc utilizar arrays para implementar um aplicativo que distribui as cartas
como parte de um jogo de cartas. O aplicativo exibe um formulrio WPF (Windows Presentation
Foundation) com quatro mos de cartas dadas aleatoriamente a partir de um baralho comum (52
cartas). Voc concluir o cdigo que d as cartas de cada mo.
Use arrays para im p le m e n ta r u m jo g o de cartas
1. Inicialize o Microsoft Visual Studio 2010, se ele ainda no estiver em execuo.
2. Abra o projeto Cards, localizado na pasta \Microsoft PressWisual CSharp Step By Step\Chapter
10\Cards Using Arrays, contida em sua pasta Documentos.
3. No menu Debug, clique em Start Without Debugging para construir e executar o aplicativo.
Ser exibido um formulrio WPF com a legenda Card Game, quatro caixas de texto (intituladas
North, South, East e West), e um boto com a legenda Deal (distribuir).
4. Clique em Deal.
Ser exibida uma mensagem como texto DealCardFromPack - TBD. Voc ainda no implemen
tou o cdigo que d as cartas.
5. Clique em OK, e feche a janela Card Game para retornar ao Visual Studio 2010.
6. Na janela Code and Text Editor, exiba o arquivo Value.cs.
Esse arquivo contm uma enumerao chamada Value, que representa os diversos valores que
uma carta pode ter, em ordem crescente:
enum Value { Two, Thre e, Four, F iv e , S i x , Seven, E ig h t, Nine, Ten, Jack, Queen, King,
Ace }

7. Exiba o arquivo Suit.cs na janela Code and Text Editor.


Esse arquivo contm uma enumerao chamada Suit, que representa os naipes das cartas con
tidas em um baralho comum:
enum S u it { Clu bs , Diamonds, He arts, Spades }

8. Exiba o arquivo PlayingCard.es na janela Code and Text Editor.


Esse arquivo contm a classe PlayingCard e essa classe modela uma nica classe do jogo.

232

Parte

II Entendendo a linguagem C#

class PlayingCard

{
p r i v a t e readonly S u i t s u i t ;
p r i v a t e readonly Value valu e;
pu b lic P la y in g C a rd (S u it s, Value v )

{
t h i s . s u i t = s;
t h i s . v a l u e = v;

}
p u b l ic o v e r rid e s t r i n g T o S t r i n g O

{
s t r i n g r e s u l t = s t r i n g . F o r m a t ( " { 0 } of { 1 } " , t h i s . v a l u e , t h i s . s u i t ) ;
re turn r e s u l t ;

p u b lic S u i t C a r d S u it O
return t h i s . s u i t ;

p u b l ic Value CardValu eO

{
return t h i s . v a l u e ;

}
}
Esta classe tem dois campos readonly que representam o valor e o naipe da carta. O construtor
inicializa esses campos.

A classe contm um par de mtodos, chamados CardValue e CardSuit, que retornam essas infor
maes, e ela substitui o mtodo ToString para retornar uma representao de string da carta.

9. Abra o arquivo Pack.cs na janela Code and Text Editor.

Captulo 10

Utilizando arrays e colees

233

Esse arquivo contm a classe Pack, que modela um baralho. No incio da classe Pack, existem
dois campos pblicos const int, chamados NumSuits e CardsPerSuit. Esses dois campos especi
ficam o nmero de naipes contidos em um baralho, e o nmero cartas de cada naipe. A varivel
privada CardPack um array bidimensional de objetos PlayingCard. (Voc usar a primeira
dimenso para especificar o naipe e a segunda para especificar o valor da carta no naipe.) A
varivel randomCardSelector um objeto Random. A classe Random um gerador de nmeros
aleatrios, e voc utilizar randomCardSelector para embaralhar as cartas antes de serem dis
tribudas em cada mo.
class Pack

{
p u b lic const i n t NumSuits = 4;
p u b lic const i n t CardsPerSuit = 13;
p ri v a te P1a y ing Ca rd[, ] cardPack;
p ri v a te Random randomCardSelector = new RandomO;

}
10. Localize o construtor padro da classe Pack. Atualmente, esse construtor est vazio, exceto por
um comentrio to do (a fazer). Exclua o comentrio e adicione a instruo mostrada em negrito
para instanciar o array cardPack com o nmero correto de elementos:
pu b lic PackO

{
this.cardPack = new PlayingCard[NumSuits, CardsPerSuit];

}
11. Adicione ao construtor Pack o seguinte cdigo mostrado em negrito. O loopJ o r externo itera
sobre a lista de valores na enumerao Suit, e o loop interno itera sobre os valores que cada
carta pode ter em cada naipe. O loop interno gera um novo objeto PlayingCard do naipe e valor
especificados e o adiciona ao elemento pertinente no array cardPack.
f o r ( S u i t s u i t = S u i t . C l u b s ; s u i t <= S u it.Spades; s u it+ + )

{
f o r (Value value = Value.Two; value <= Value.Ac e; value++)

{
th is .c a rd P a c k [(in t)s u it,

( i n t ) v a l u e ] = new P la y i n g C a r d ( s u it , v a l u e ) ;

}
}

12. Procure o mtodo DealCardFromPack na classe Pack. O objetivo desse mtodo escolher uma
carta aleatria no baralho, retorn-la e depois remover a carta do baralho, para impedir que ela
seja novamente selecionada.

234

Parte

II Entendendo a linguagem C#

A primeira tarefa desse mtodo escolher um naipe qualquer. Exclua o comentrio e a instruo
que lana a exceo NotlmplementedException desse mtodo, e substitua-a pela seguinte instru
o, mostrada em negrito:
p u b lic PlayingCard DealCardFromPackO

{
S u i t s u i t = (S u it)ra n dom C a rdS e le ctor.N ex t(N u m Su it s );

}
Essa instruo utiliza o mtodo Next do objeto gerador de nmeros aleatrios, randomCardSelector, para retornar um nmero aleatrio correspondente a um naipe. O parmetro para o
mtodo Next especifica o limite mximo exclusivo do intervalo a ser utilizado; o valor selecio
nado est entre 0 e esse valor menos 1. Observe que o valor retornado um int, de modo que
necessrio convert-lo para depois atribu-lo a uma varivel Suit.
H sempre a possibilidade de que no existam mais cartas no baralho do naipe selecionado.
Resolva essa situao e escolha outro naipe, se necessrio.
13. Localize o mtodo IsSuitEmpty. O objetivo desse mtodo aceitar um parmetro Suit e retornar
um valor booleano que indica se ainda existem outras cartas desse naipe no baralho. Exclua o
comentrio e a instruo que lana a exceo NotlmplementedException desse mtodos, e adi
cione o seguinte cdigo, que aparece em negrito:
p r i v a t e bool I s S u i tE m p ty (S u i t s u i t )

{
bool r e s u l t = tr u e ;
f o r (Value value = Value.Two; value <= Valu e.Ace; value++)

{
i f (!Is C a rd A lre a d y D e a lt(s u it, v a lu e ))

{
r e s u l t = f a ls e ;
break;

}
}
re tu rn r e s u l t ;

}
Esse cdigo itera sobre os possveis valores de cartas e determina se ainda existe alguma carta
no array cardPack, com o naipe e o valor especificados, por meio do mtodo IsCardAlreadyDealt,
o que encerrar a etapa seguinte. Se o loop encontrar uma carta, o valor da varivel result ser
definido comofa lse, e a instruo break encerrar o loop. Se o loop terminar sem encontrar uma
carta, a varivel result permanecer definida com seu valor inicial, true. O valor da varivel
result passado de volta como o valor de retorno do mtodo.

Captulo 10

Utilizando arrays e colees

235

14. Procure o mtodo IsCardAlreadyDealt. O objetivo desse mtodo determinar se a carta com o
naipe e o valor especificados j foi distribuda e retirada do baralho. Voc ver mais adiante
que, ao dar uma carta, o mtodo DecdFromPack a retira do array cardPack e define o elemento
correspondente com null. Substitua o comentrio e a instruo que lana a exceo NotlmplementedException nesse mtodo pelo cdigo apresentado em negrito:
priv a te bool IsC a r d A Ire a d y D e a lt(S u it s u i t , Value v alu e )

{
return (this.cardPack[(int)suit, (int)value] == null);

}
Essa instruo retorna true se o elemento no array cardPack correspondente ao naipe e ao valor
for null, e retornafa ls e caso contrrio.
15. Volte ao mtodo DealCardFromPack. Adicione o seguinte loop while depois do cdigo que sele
ciona um naipe aleatoriamente. Esse loop chama o mtodo IsSuitEmpty para detectar se ainda
existem cartas do naipe especificado, no baralho. Se no existirem, ele selecionar outro nai
pe aleatoriamente (ele pode at escolher o mesmo naipe novamente) e verificar novamente.
O loop repetir esse processo at encontrar um naipe com pelo menos uma carta existente.
pu b lic PlayingCard DealCardFromPackO

{
S u it s u i t = (S u i t)ra n dom C ardS e le ctor.N ex t(N u m S u it s );
while (this.IsSuitErapty(suit))

{
suit = (Suit)randomCardSelector.Next(NumSuits);

}
}
16. Voc acabou de selecionar um naipe aleatoriamente, com pelo menos uma carta ainda existente.
A prxima tarefa escolher uma carta aleatria desse naipe. Voc pode utilizar o gerador de
nmeros aleatrios para selecionar um valor de carta, mas, como anteriormente, no possvel
assegurar que a carta com o valor escolhido ainda no tenha sido dada. Entretanto, voc pode
utilizar a mesma soluo de antes, chamar o mtodo IsCardAlreadyDealt para detectar se a
carta j foi dada, e, nesse caso, escolher outra carta aleatria, e tentar novamente, repetindo o
processo at que a carta seja encontrada. Para isso, adicione as seguintes instrues ao mtodo

DealCardFromPack:
pu b lic PlayingCard DealCardFromPackO

{
Value value = (Value)randomCardSelector.Next(CardsPerSuit);
while (this.IsCardAIreadyDealtCsuit, value))

{
value = (Value)randomCardSelector.Next(CardsPerSuit);

}
}

236

Parte

II Entendendo a linguagem C#

17. Voc acabou de selecionar uma carta aleatria que ainda no foi distribuda. Adicione o se
guinte cdigo para retornar essa carta e definir com null o elemento correspondente no array
cardPack:
p u b lic PlayingCard DealCardFromPackO

{
PlayingCard card = this.cardPack[(int)suit, (int)value];
this.cardPack[(int)suit, (int)value] = null;
return card;

}
18. A prxima etapa adicionar a carta selecionada a uma mo. Abra o arquivo Hand.cs e exiba-o
na janela Code and Text Editor. Esse arquivo contm a classe Hand, que implementa uma mo
de cartas (ou seja, todas as cartas distribudas para um nico jogador).
Esse arquivo contm um campo public const int, chamado HandSize, definido com tamanho de
uma mo de cartas (13); o arquivo tambm contm um array de objetos PlayingCard, inicializado por meio da constante HandSize. O campo playingCardCount utilizado por seu cdigo
para rastrear a quantidade de cartas contidas atualmente na mo medida que preenchida.
class Hand

{
p u b lic const i n t HandSize = 13;
p r i v a t e P la y in g C a rd [] cards = new PlayingCard [ H a n d S iz e ];
p r i v a t e i n t playingCardCount = 0;

O mtodo ToString gera uma representao de string das cartas da mo. Ele utiliza o loopforeach
para iterar sobre os itens do array de cartas e chama o mtodo ToString em cada objeto Playing
Card encontrado. Essas strings so concatenadas com um caractere de nova linha (o caractere
\n) para fins de formatao.
p u b l ic o v e r rid e s t r i n g T o S t r i n g O

{
strin g res ult =
foreach (Pla yingCard card i n t h i s . c a r d s )

{
r e s u l t += c a r d . T o S t r i n g O + "\n ;

>
return r e s u l t ;

Captulo 10

Utilizando arrays e colees

237

19. Localize o mtodo AddCard.ToHa.ndt na classe Hand. O objetivo desse mtodo adicionar mo
a carta especificada como parmetro. Adicione a esse mtodo as instrues que aparecem em
negrito:
p u b lic void AddCardToHand(P1ayingCard cardDealt)

{
if (this.playingCardCount >= HandSize)

{
throw new ArgumentException("Too many cards");

>
this.cards[this.playingCardCount] = cardDealt;
thi s .piayi ngCardCount++;

}
Esse cdigo verifica primeiramente se a mo ainda no est cheia e lana uma exceo ArgumentException, se ela existir. Caso contrrio, a carta adicionada ao array cards no ndice espe
cificado pela varivel playingCardCount, e essa varivel , ento, incrementada.
20. No Solution Explorer, expanda o n Game.xaml e abra o arquivo Game.xaml.es na janela Code
and Text Editor. Esse o cdigo para a janela Card Game. Localize o mtodo dealClick. Esse
mtodo executado quando o usurio clica no boto Deal. O cdigo semelhante ao seguinte:
p ri v a te void d e a l C l i c k ( o b j e c t sender, RoutedEventArgs e)

{
try

{
pack = new P a ck O ;
f o r ( i n t handNum = 0; handNum < NumHands; handNum++)

{
hands[handNum] = new H a n d();
f o r ( i n t numCards = 0; numCards < Hand.HandSize; numCards++)

{
PlayingCard cardDealt = pack.DealCardFromPackO;
hands[handNum]. AddCardToHand(cardD ealt);

}
}
north.Text = hands[0].ToString();
south.Text = hands[1].ToString();
east.Text = hands[2] .ToStringQ ;
west.Text = hands[3].ToString();

}
catch (E xception ex)

{
MessageBox.Show(ex.Message, " E r r o r " , MessageBoxButton.OK,
essageBoxImage. E r r o r ) ;

238

Parte

II Entendendo a linguagem C#

A primeira instruo no bloco try cria um novo baralho. O loopfo r externo gera quatro mos
desse baralho e as armazena em um array chamado hands. O loopfo r interno preenche cada
mo por meio do mtodo DealCardFromPack, para recuperar uma carta aleatria do baralho, e,
por meio do mtodo AddCardToHand, para adicionar essa carta mo.
Quando todas as cartas estiverem distribudas, cada mo ser exibida nas caixas de texto do
formulrio. Essas caixas de texto so chamadas de north, south, east e west. O cdigo utiliza o
mtodo ToString de cada mo para formatar a sada.
Se ocorrer uma exceo nesse ponto, a rotina de manipulao catch exibir uma caixa de men
sagem com a mensagem de erro da exceo.
21. No menu Debug, clique em Start Without Debugging. Quando a janela Card Game for exibida,
clique em Deal. A carta do baralho deve ser dada aleatoriamente a cada mo, e as cartas de cada
mo devem ser exibidas no formulrio, como mostra a imagem a seguir:

N orth

South

West

East

Te n o f Clubs

Ace o f Hearts

Te n o f Spades

T w o o f Diamonds

Seven of Hearts

Thre e of Diamonds

Four of Diamonds

Seven o f Spades

Jack of Diamonds

Queen o f Clubs

Six of Clubs

N ine of Clubs

Jack of Spades

T w o of Clubs

Jack of Clubs

Thre e of Hearts

Ace of Clubs

Jack of Hearts

Seven of Clubs

Queen of Hearts

Seven of Diamonds

King of Clubs

King of Spades

Six o f Hearts

Eight of Diamonds

Five o f Spades

T w o of Hearts

Te n o f D iam onds

Thre e of Spades

Four of Spades

Five of Hearts

N ine of Spades

Ace of Diamonds

N ine o f D iamonds

Q u e en of Spades

Four of Hearts

Eight of Hearts

Four of Clubs

King of Hearts

Six o f Diam onds

Ace of Spades

Five of Diam onds

Six of Spades

Te n o f Hearts

Eight of Clubs

Three of Clubs
Five o f Clubs

Eight of Spades
T w o of Spades

King o f Diamonds
Queen of Diam ond

N ine o f Hearts

22. Clique em Deal novamente. Um novo conjunto de mos distribudo, e as cartas de cada mo
mudam.
23. Feche a janela Card Game e retorne ao Visual Studio.

O que so classes de coleo?


Arrays so teis, mas tm suas limitaes - uma das mais bvias que voc precisa utilizar um
ndice inteiro para acessar os elementos no array. Felizmente, arrays so apenas uma das maneiras
de colecionar elementos do mesmo tipo. 0 Microsoft .NET Framework fornece diversas classes que
tambm colecionam elementos de outras maneiras especializadas. So as classes Collection, que
residem no namespace System.Collections e seus subnamespaces.
Alm da questo dos ndices, existe outra diferena bsica entre um array e uma coleo. Um array
pode armazenar tipos-valor. As classes de coleo bsicas aceitam, armazenam e retornam seus
elementos como tipos-objetos - isto , o tipo de elemento de uma classe de coleo um object. Para
entender suas implicaes, til comparar um array de variveis int (int tipo-valor) com um array

Captulo 10

Utilizando arrays e colees

239

de objetos (iobject um tipo-referncia). Como int um tipo-valor, um array de variveis int armaze
na seus valores int diretamente, como mostrado na imagem a seguir:

Agora considere o efeito quando o array um array de objetos. Voc ainda pode adicionar valores inteiros
a esse array. (Na realidade, voc pode adicionar valores de qualquer tipo a ele.) Quando voc acrescenta
um valor inteiro, ele automaticamente sofre boxing, e o elemento do array (uma referncia a um objeto)
referencia a cpia na forma boxed do valor inteiro. De modo semelhante, ao remover um valor de um
array de objetos, voc deve fazer unboxing utilizando um casting. (Para obter mais detalhes sobre como
fazer um boxing, consulte o Captulo 8.) A imagem a seguir mostra um array de object preenchido com
valores inteiros:

As sees a seguir fornecem uma viso geral muito rpida das quatro classes de coleo mais teis.
Consulte a documentao do Microsoft .NET Framework Class Library para obter mais detalhes sobre
cada classe.

240

Parte II

Entendendo a linguagem C#

A classe de coleo ArrayList


ArrayList uma classe til para reunir elementos em um array. H certas ocasies em que um array
comum pode ser muito restritivo:
Se quiser redimensionar um array, voc ter de criar um novo array, copiar os elementos (omitir
alguns se o novo array for menor) e ento atualizar qualquer referncia ao array original para
que ela se refira ao novo array.
Se quiser remover um elemento de um array, voc precisar mover todos os elementos finais
uma posio para cima. Isso tambm no funciona muito bem porque voc terminar com duas
cpias do ltimo elemento.
Se quiser inserir um elemento em um array, voc ter de mover os elementos uma posio para
baixo para criar um espao vazio. Mas a voc perde o ltimo elemento do array!
A classe de coleo ArrayList oferece os seguintes recursos para ajudar a superar essas restries:
Voc pode remover um elemento de um ArrayList utilizando o mtodo Remove. A classe Ar
rayList reordena automaticamente seus elementos.
Voc pode adicionar um elemento ao final de um ArrayList utilizando o mtodo Add. Voc forne
ce o elemento a ser adicionado. OArrayList se redimensiona, se necessrio.
Voc pode inserir um elemento no meio de um ArrayList utilizando o mtodo Insert. Novamen
te, o ArrayList se redimensiona, se necessrio.
Voc pode referenciar um elemento existente em um objeto ArrayList utilizando a notao de
array normal, com colchetes e o ndice do elemento.

Observe um exemplo que mostra como voc pode criar, manipular e iterar pelos contedos de um

ArrayList:
using System;
using System.C o ll e c t i o n s ;
A r r a y L i s t numbers = new A r r a y L i s t O ;
// preenche o A r r a y L i s t
foreach ( i n t number i n new i n t [ 1 2 ] { 1 0 , 9, 8, 7, 7, 6, 5, 10, 4, 3, 2, 1

{
numbers. Add(number);

Captulo 10

Utilizando arrays e colees

// insere um elemento na penltima posio da l i s t a e move o ltim o item para cima


// (o prim e iro parmetro a posio;
// o segundo parmetro o v a l o r sendo i n s e r id o )
n u m bers.Insert(n um be rs .C ount-l, 9 9 );
// remove o pri m eiro elemento cujo v a l o r 7 (o elemento 4, i n d i c e 3)
numbers. Remove(7);
// remove o elemento que agora o 7o elemento, i n d ic e 6 (10)
numbers. RemoveAt(6);
// i t e r a pelos 10 elementos restantes u t i l i z a n d o uma instru o f o r
fo r ( i n t i = 0; i < numbers.Count; i+ + )

{
i n t number = ( i n t ) n u m b e r s [ i ] ; // observe o c a s t in g , que faz o unboxing do v a lo r
C onsole .W rite Line (n u m ber);

}
// i t e r a pelos 10 elementos restantes u t i l i z a n d o uma instru o foreach
foreach ( i n t number i n numbers) // nenhum castin g necessrio

{
C ons ole .W riteLine(nu m be r);

}
A sada deste cdigo mostrada aqui:
10
9
8

7
6
5
4
3
2

99

1
10
9
8

6
5
4
3
2

99

241

242

Parte II

Entendendo a linguagem C#

A classe de coleo Queue


A classe Queue implementa um mecanismo FIFO jfirst-infirst-out-, primeiro a entrar, primeiro a sair).
Um elemento inserido no final da fila (a operao de enfileiramento) e removido no incio da fila
(a operao de desenfileiramento).
Observe o exemplo de uma fila e suas operaes:
using System;
using System.C o l l e c t i o n s ;
Queue numbers = new QueueO;
// preenche a f i l a
foreach ( i n t number i n new i n t [ 4 ] { 9 , 3, 7, 2 } )

{
numbers.Enqueue(number);
Console.WriteLine(number + " has jo in ed the queue "); //entrou na f i l a

}
// i t e r a pela f i l a
foreach ( i n t number i n numbers)

{
C o n s ole .W rite Line(n u m ber);

}
// esvazia a f i l a
w hil e (numbers.Count > 0)

{
i n t number = (int)numbers.Dequeu eO ; // casting necessrio para faz er o unboxing do v alo r
Consol e.W riteLine(number + " has l e f t the queue "); // saiu da f i l a

}
A sada desse cdigo :
9
3
7
2
9
3
7

has
has
has
has

join ed
join ed
join ed
join ed

has
has
has
has

le ft
le ft
le ft
le ft

the
the
the
the

queue
queue
queue
queue

9
3
7
2

the
the
the
the

queue
queue
queue
queue

A classe de coleo Stack


A classe Stack implementa um mecanismo LIFO (last-inJirst-out; ultimo a entrar, primeiro a sair).
Um elemento entra na pilha pelo alto (a operao de insero na pilha) e sai da pilha tambm por

Captulo 10

Utilizando arrays e colees

243

cima (a operao de remoo da pilha). Para visualizar isso, imagine uma pilha de pratos: novos
pratos so adicionados parte superior e os pratos so removidos da parte superior, fazendo com
que o ltimo prato a ser inserido na pilha seja o primeiro a ser removido. (O prato na parte inferior
raramente utilizado e inevitavelmente precisar ser lavado antes de voc poder colocar qualquer
comida nele, uma vez que estar completamente sujo!) Observe o exemplo:
using System;
using System.C o ll e c t i o n s ;
Stack numbers = new S t a c k O ;
// preenche a p ilh a
foreach ( i n t number i n new i n t [ 4 ] { 9 , 3, 7, 2 } )

{
numbers. Push(number);
Console.WriteLine(number + " has been pushed on the s t a c k " ) ; //empilhou o v a lo r

}
// i t e r a pel a p ilh a
foreach ( i n t number in numbers)

{
C ons ole .W riteLine(nu m be r);

}
// esvaz ia a p ilh a
while (numbers.Count > 0)

{
i n t number = (in t)n u m b e r s . P o p ( ) ;
Console.WriteLine(number + " has been popped o f f the s t a c k " ) ; //desempilhou o v a lo r

}
A sada do programa :
9 has
3 has
7 has
2 has

been
been
been
been

pushed
pushed
pushed
pushed

on
on
on
on

the
the
the
the

been
been
been
been

popped
popped
popped
popped

off
off
off
off

stack
stack
stack
stack

7
3
9
2
7
3
9

has
has
has
has

the
the
the
the

stack
stack
stack
stack

A classe de coleo Hashtable


Os tipos array e ArrayList fornecem um meio de mapear um ndice inteiro para um elemento. Voc
fornece um ndice inteiro dentro de colchetes (por exemplo, [4]) e obtm de volta o elemento no ndice
4 (que na realidade o quinto elemento). Contudo, eventualmente, voc pode querer fornecer um
mapeamento em que o tipo de origem do mapeamento no ser um int, mas algum outro tipo qual-

244

Parte II

Entendendo a linguagem C#

quer, como string, double ou Time. Em outras linguagens, isso costuma ser chamado de array asso
ciativo. A classe H ashtable fornece essa funcionalidade mantendo internamente dois arrays object,
um para as chaves de origem do mapeamento e uma para os valores de destino do mapeamento.
Quando voc insere um par chave/valor em um H ashtable, ela determina automaticamente qual cha
ve pertence a qual valor e permite que voc recupere o valor que est associado chave especificada
rpida e facilmente. H algumas consequncias importantes no projeto da classe Hashtable-,
Uma H ashtable no pode conter chaves duplicadas. Se chamar o mtodo Add para adicionar
uma chave que j est presente no array de chaves, voc obter uma exceo. Voc pode, porm,
utilizar a notao de colchetes para adicionar um par chave/valor (como mostrado no exemplo
a seguir), sem correr o risco de lanar uma exceo, mesmo se a chave j tiver sido adiciona
da. Voc pode testar se uma H ashtable j contm uma chave especfica utilizando o mtodo

ContainsKey.
Internamente, uma H ashtable uma estrutura de dados esparsa que opera melhor quando tem
bastante memria para funcionar. O tamanho de uma H ashtable na memria pode aumentar
rapidamente medida que voc insere mais elementos.
Ouando utiliza uma instruoforeach para iterar por uma Hashtable, voc obtm uma DictionaryEntry. A classe DictionaryEntty fornece acesso aos elementos chave e valor em ambos os
arrays por meio das propriedades Key e Value.
Veja um exemplo que associa as idades dos membros da minha famlia aos seus nomes e ento im
prime essas informaes:
using System;
using S y ste m .C olle ctions;
Hashtable ages = new H a s h ta b le O ;
// preenche a Hashtable
a g e s [" ]o h n " ] = 44;
ag es["Diana"] = 45;
ages["James"] = 17;
ag es [" France sca "] = 15;
// i t e r a u t i l i z a n d o uma instru o foreach
// o i t e r a d o r gera um ob jeto D i c t i o n a r y E n t r y contendo um par chave/valor
foreach ( D i c t i o n a r y E n t r y element i n ages)

{
s t r i n g name = ( s tr in g )e l e m e n t.K e y ;
i n t age = ( i n t ) e le m e n t .V a lu e ;
Console.WriteLine("Name: { 0 } , Age: { 1 } " , name, ag e);

}
A sada do programa :
Name:
Name:
Name:
Name:

Diana, Age: 45
James, Age: 17
Francesca, Age: 15
Dohn, Age: 44

Captulo 10

Utilizando arrays e colees

245

A classe de coleo SortedList


A classe SortedList muito semelhante classe Hashtable no sentido de que permite associar chaves
a valores. A principal diferena que o array de chaves est sempre ordenado. (Afinal de contas, ele
se chama SortedList, ou seja, lista ordenada.)
Quando voc insere um par chave/valor em uma SortedList, a chave inserida no array de chaves no
ndice correto para manter as chaves ordenadas. O valor ento inserido no array de valores no mesmo ndice. A classe SortedList garante automaticamente que as chaves e valores sejam alinhados,
mesmo quando voc adiciona e remove elementos. Isso significa que voc pode inserir pares chave/
valor em uma SortedList em qualquer sequncia; eles sempre so ordenados com base no valor das
chaves.
Como a classe Hashtable, uma classe SortedList no pode conter chaves duplicadas. Quando voc
utiliza uma instruojbreach para iterar por uma SortedList, recebe uma DictionaryEntry. Mas os
objetos de DictionaryEntry sero retornados classificados pela propriedade Key.
Veja o mesmo exemplo que associa a idade dos membros da minha famlia a seus nomes e, depois,
imprime as informaes, mas essa verso foi ajustada para utilizar uma classe SortedList em vez de
uma Hashtable:
using System;
using System.Collections;


SortedList ages = new SortedListQ;
// preenche a SortedList
ages["]ohn"] = 44;
ages["Diana"] = 45;
ages["James"] = 17;
ages["Francesca"] = 15;
// itera utilizando uma instruo foreach
// o iterador gera um objeto DictionaryEntry contendo um par chave/valor
foreach (DictionaryEntry element i n ages)

{
string name = (string)element.Key;
int age = (int)element.Value;
Conso1e.WriteLine("Name: {0}, Age: {!}", name, age);
}

A sada desse programa est ordenada alfabeticamente pelos nomes dos membros da minha famlia:
Name:
Name:
Name:
Name:

Diana, Age: 45
Francesca, Age: 15
James, Age: 17
John, Age: 44

246

Parte II

Entendendo a linguagem C#

Utilizando inicializadores de coleo


Os exemplos nas subsees anteriores mostraram como adicionar elementos individuais a uma co
leo utilizando o mtodo mais apropriado para essa coleo l/dd para um ArrayList, Enqueue para
um Queue, Push para uma Stack e assim por diante). Voc tambm pode inicializar alguns tipos de
coleo ao declar-los, utilizando uma sintaxe muito semelhante quela suportada por arrays. Por
exemplo, a instruo a seguir cria e inicializa o objeto ArrayList chamado numbers mostrado ante
riormente, demonstrando uma tcnica alternativa de chamar repetidamente o mtodo Add-.
A r r a y L i s t numbers = new A r r a y L i s t ( ) { 1 0 , 9, 8, 7, 7, 6, 5, 10, 4, 3, 2, 1 } ;

Internamente, o compilador C# converte essa inicializao em uma srie de chamadas ao mtodo

Add. Consequentemente, voc s pode utilizar essa sintaxe para colees que realmente suportam o
mtodo Add. (As classes Stack e Queue no suportam.)
Para colees mais complexas como Hashtable, que recebem pares chave/valor, voc pode especificar
cada par chave/valor como um tipo annimo na lista inicializadora, como mostrado aqui:
Hashtable ages =
new H a s h t a b l e ( ) { { " J o h n " , 4 4 }, { " D i a n a " , 4 5 }, {" Jam es", 17 }, {" Frances ca", 1 5 } } ;

O primeiro item em cada par a chave e o segundo o valor.

Comparando arrays e colees


Veja um resumo das principais diferenas entre arrays e colees:
Um array declara o tipo dos elementos que ele armazena, enquanto a coleo no faz isso devi
do ao fato de as colees armazenarem seus elementos como objetos.
Uma instncia de array tem um tamanho fixo e no pode aumentar ou diminuir. Uma coleo
pode redimensionar dinamicamente seu tamanho, conforme a necessidade.
Um array pode ter mais de uma dimenso. Uma coleo linear. Entretanto, os itens em uma
coleo podem eles prprios ser colees, permitindo que voc simule um array multidimensio
nal como uma coleo de colees.

Utilizando classes de coleo para jogar cartas


No prximo exerccio, voc converter o jogo de cartas desenvolvido no exerccio anterior para utili
zar colees em vez de arrays.
Use colees para im p le m e n ta r u m jo g o de cartas
1. Volte ao projeto Cards do exerccio anterior.

Captulo 10

Utilizando arrays e colees

247

2. Exiba o arquivo Pack.cs, na janela Code and Text Editor. Observe a seguinte instruo using,
perto do incio do arquivo.
using S y ste m .C olle ctions;

As classes de coleo esto localizadas nesse namespace.


3. Na classe Pack , mude a definio do array bidimensional cardPack para um objeto HashTable,
como mostrado em negrito a seguir:
cla ss Pack

{
private Hashtable cardPack;

}
Lembre-se de que a classe H ashtable define uma coleo de tipos-objeto, e voc no especifica o
tipo PlayingCard. Alm disso, o array original tinha duas dimenses, enquanto uma H ashtable
s tem uma. Voc emular um array bidimensional ao utilizar os objetos da coleo SortedList
como elementos na Hashtable.
4. Localize o construtor dzPack. Modifique a primeira instruo nesse construtor para instanciar
a varivel cardPack como um novo objeto H ashtable , em vez de um array, como mostrado a
seguir, em negrito:
pu b lic PackO

{
this.cardPack = new HashtableO;

}
5. No loop externofo r , declare um objeto de coleo SortedList, chamado cardsInSuit. Mude o cdi
go no loopyr interno, de modo a adicionar o novo objeto PlayingCard a essa coleo, e no ao
array. Aps o loopfo r interno, adicione o objeto SortedList H ashtable cardPack, e especifique o
valor da varivel suit como a chave para esse item. (O objeto SortedList contm todas as cartas
no baralho do naipe especificado, t a Hashtable abrange uma coleo desses objetos SortedList.)
O cdigo a seguir mostra o construtor finalizado, com as mudanas destacadas em negrito:
p u b lic Pack()

{
th i s. cardPack = new H a s h ta b le O ;
f o r ( S u i t s u i t = S u i t . C l u b s ; s u i t <= Suit.Spades; su it+ + )

248

Parte

II Entendendo a linguagem C#

SortedList cardsInSuit = new S o r t e d L i s t O ;


f o r (Value value = Value.Two; value <= Value.Ace; value++)

{
cardsInSuit.Add(value, new PIayingCard(suit, value));

}
this.cardPack.Add(suit, cardsInSuit);

}
}
6. Encontre o mtodo DealCardFromPack. Lembre-se de que esse mtodo escolhe uma carta alea
toriamente no baralho, remove a carta do baralho e retorna essa carta. A lgica para selecionar
a carta no exige quaisquer alteraes, mas as instrues no final do mtodo, que recuperam a
carta e a removem do array, devem ser atualizadas de modo a utilizar, em substituio, a cole
o Hashtable.
Modifique o cdigo depois da chave de fechamento do segundo loop while, como mostrado no
cdigo a seguir, em negrito;
p u b lic PlayingCard DealCardFromPackO

{
S u i t s u i t = (S u i t)ra n dom C a rdS e le ctor.N ex t(N u m S u it s );
w hil e ( t h i s . I s S u i t E m p t y ( s u i t ) )

{
s u i t = (S u it)ran dom C a rdS e le ctor.N ex t(N u m Su it s );

}
Value value = ( V a l u e ) ra n dom C ard S ele cto r.Ne xt(C ardsP erS uit);
whil e ( t h i s . I s C a r d A l r e a d y D e a l t ( s u i t , v a l u e ) )

{
value = (V a lu e)ra n d o m C a rd S e le cto r.N e x t(C a rd sP e rS u it);

}
SortedList cardsInSuit = (SortedList) cardPack[suit];
PlayingCard card = (PIayingCard)cardsInSuit[vaiue];
cardsInSuit.Remove(value);
return card;

}
A H ashtable contm uma coleo de objetos SortedList, um para cada naipe de cartas. Esse
novo cdigo recupera a SortedList para a carta do naipe selecionado aleatoriamente na Hashta
ble , e depois recupera a carta com o valor selecionado nessa SortedList. A ltima instruo nova
remove a carta da SortedList.

7. Localize o mtodo IsCardAlreadyDealt. Esse mtodo determina se a carta j foi distribuda, ao


verificar se o elemento correspondente no array foi definido com null. Voc deve modificar esse
mtodo para determinar se existe uma carta com o valor especificado na SortedList para o naipe
na cardPack Hashtable. Atualize o mtodo como mostrado em negrito:
p r i v a t e bool I s C a r d A lr e a d y D e a lt(S u i t s u i t , Value v alu e)

{
SortedList cardsInSuit = (SortedList)this.cardPack[suit];
return (!cardsInSuit.ContainsKey(value));

Captulo 10

Utilizando arrays e colees

249

8. Exiba o arquivo Hand.cs na janela Code and Text Editor. Essa classe usa um array para arma
zenar as cartas da mo. Modifique a definio do array cards, de modo a utilizar uma coleo
ArrayList, como mostrado em negrito:
class Hand

{
p u b lic const i n t HandSize = 13;
private ArrayList cards = new ArrayListO;

}
9. Localize o mtodo AddCardToHand. Esse mtodo verifica se a mo est cheia e, se no estiver,
ele adiciona a carta fornecida como o parmetro do array cards, no ndice especificado pela va
rivel playingCardCount.
Atualize esse mtodo de modo a utilizar, em substituio, o mtodo Add da classe ArrayList.
Essa mudana tambm evita a necessidade de rastrear explicitamente a quantidade de cartas
que a coleo armazena, porque voc pode utilizar, em vez disso, a propriedade Count. Modifi
que a instruo if, que verifica se a mo est cheia, para fazer referncia a essa propriedade, e
exclua a varivel playingCardCount da clsse.
O mtodo finalizado deve ficar semelhante a este:
p u b lic void AddCardToHand(P1ayingCard cardDealt)

{
i f ( t h i s . cards.Count >= HandSize)

{
throw new ArgumentException("Too many c a r d s " ) ;

}
t h i s . c a r d s . A d d ( c a r d D e a lt );

>
10. No menu Debug, clique em Start Without Debugging para construir e executar o aplicativo.
11. Quando a janela Card Game for exibida, clique em Deal. Verifique se as cartas so distribudas
e se as mos preenchidas aparecem como anteriormente. Clique em Deal novamente para gerar
outro conjuntos de mos aleatoriamente.
12. Feche o formulrio e retorne ao Visual Studio 2010.
Neste captulo, voc aprendeu a criar e utilizar arrays para manipular conjuntos de dados. Viu tam
bm como utilizar algumas das classes de coleo comuns para armazenar e acessar dados.
Se voc quiser seguir para o prximo captulo agora:
Mantenha o Visual Studio 2010 em execuo e v para o Captulo 11.
Se quiser sair do Visual Studio 2010:
No menu File, clique em Exit. Se vir uma caixa de dilogo Save, clique em Yes e salve o projeto.

250

Parte II

Entendendo a linguagem C#

Referncia rpida do Captulo 10


Para

Faa isto

Declarar uma varivel de array

Escreva o nome do tipo de elemento, seguido por col


chetes, seguidos pelo nome da varivel, seguido por um
ponto e vrgula. Por exemplo:
bool [ ] f la g s ;

Criar uma instncia de um array

Escreva a palavra-chave new, seguida pelo nome do tipo


de elemento, seguido pelo tamanho do array entre col
chetes. Por exemplo:
b o o l[ ] fla g s = new b o o l[1 0 ];

Inicializar os elementos de um array (ou de uma coleo


que suporta o mtodo Add) com valores especficos

Para um array, escreva os valores especficos em uma


lista separada por vrgulas incluindo-os entre chaves. Por
exemplo:
b o o l[ ] f la g s = { t r u e , f a ls e , tru e , fa lse } ;
Para uma coleo, utilize o operador new e o tipo de
coleo com os valores especficos em uma lista separada
por vrgulas includa entre chaves. Por exemplo:
A r r a y L i s t numbers = new A r r a y L i s t ( ) { 1 0 , 9, 8,
7, 6, 5 };

Descobrir o nmero de elementos de um array

Utilize a propriedade Length. Por exemplo:


i n t [ ] fla g s = . . . ;
i n t noOfElements = f la g s .L e n g th ;

Descobrir o nmero de elementos em uma coleo

Utilize a propriedade Count. Por exemplo:


A r r a y L i s t f la g s = new A r r a y L i s t O ;
i n t noOfElements = f la g s .C o u n t;

Acessar um elemento individual de um array

Escreva o nome da varivel do array, seguida pelo ndice


inteiro do elemento entre colchetes. Lembre-se de que a
indexao de array comea em 0, no em 1. Por exemplo:
bool i n i t i a l E l e m e n t = f l a g s [ 0 ] ;

Iterar pelos elementos de um array ou de uma coleo

Utilize uma instruo for ou uma instruo foreach. Por


exemplo:
b o o l[ ] f la g s = { tr u e , f a l s e , tr u e , fa lse } ;
f o r ( i n t i = 0; i < f la g s .L e n g th ; i+ + )

{
C o n s o le .W rite Lin e (fla g s[i]);

}
foreach (bool f l a g in f la g s )

{
C o n s o le .W rite Lin e (fla g );

Captulo 11

Entendendo arrays de
parmetros
Neste captulo, voc vai aprender a:
Escrever um mtodo que pode aceitar qualquer nmero de argumentos utilizando a palavra-chave params.
Escrever um mtodo que pode aceitar qualquer nmero de argumentos de qualquer tipo
utilizando a palavra-chave params em combinao com o tipo object.
Explicar a diferenas entre os mtodos que aceitam arrays de parmetros e os que admitem parmetros opcionais.

Os arrays de parmetros so teis se voc quer escrever mtodos que podem receber qualquer nmero de argumentos, possivelmente de tipos diferentes, como parmetros. Se voc est familiarizado
com os conceitos de orientao a objetos, talvez voc esteja rangendo os dentes de frustrao. Afinal
de contas, o enfoque orientado a objetos para solucionar esse problema definir mtodos sobrecarregados.
Sobrecarregar o termo tcnico utilizado para declarar dois ou mais mtodos com o mesmo nome no
mesmo escopo. Ser capaz de sobrecarregar um mtodo muito til nos casos em que voc quer realizar a mesma ao em argumentos de tipos diferentes. O exemplo clssico de sobrecarga no Microsoft
Visual C# Console. WrteLine. O mtodo WriteLine sobrecarregado vrias vezes para permitir passar qualquer argumento de tipo primitivo:
class Console
public static void WriteLine(int parameter)
public static void WriteLine(double parameter)
public static void WriteLine(decimal parameter)

Embora muito til, a sobrecarga no cobre todos os casos. Particularmente, a sobrecarga no trata
facilmente uma situao em que o tipo dos parmetros no varia, mas o nmero de parmetros, sim.
Contudo, e se voc quisesse escrever muitos valores no console, por exemplo? Voc teria de fornecer
verses do Console.WriteLine que pudessem receber dois parmetros, outras verses que pudessem
receber trs parmetros e assim por diante? Isso rapidamente se tornaria tedioso. E a duplicao
macia de todos esses mtodos sobrecarregados no o preocuparia? Deveria. Felizmente, h uma
maneira de escrever um mtodo que recebe um nmero varivel de argumentos: voc pode utilizar
um array de parmetros (um parmetro declarado com a palavra-chave params}.
Para entender como os arrays de params resolvem esse problema, til compreender primeiramente
as utilizaes e deficincias dos arrays comuns.

252

Parte II

Entendendo a linguagem C#

Utilizando argum entos de arrays


Suponha que voc queira escrever um mtodo para determinar o valor mnimo em um conjunto de
valores passados como parmetros. Uma maneira seria utilizar um array. Por exemplo, para desco
brir o menor valor em diversos valores int, voc poderia escrever um mtodo chamado Min com um
nico parmetro representando um array de valores int-.
class U t i l

{
p u b lic s t a t i c i n t M i n ( i n t [ ] paramList)

{
i f (param List == n u ll

|| paramList.Length == 0)

{
throw new Argu m entE xcepti on(" Uti 1 .Min: not enough arguments");

}
i n t currentMin = paramList [ 0 ] ;
foreach ( i n t i i n paramList)

{
i f ( i < curre ntM in)

{
currentMi n = i ;

>
}
re turn curre ntM in ;

}
}

Para utilizar o mtodo Min a fim de descobrir o mnimo de dois valores int, escreva o seguinte:
i n t [ ] a rra y = new i n t [ 2 ] ;
arra y [0 ] = f i r s t ;
a r r a y [1] = second;
i n t min = U t i l .M i n (a r r a y ) ;

E para utilizar o mtodo Min a fim de descobrir o mnimo de trs valores int, escreva o seguinte:
i n t [ ] a rr a y = new i n t [ 3 ] ;
arr a y [0 ] = f i r s t ;
a r r a y t l ] = second;
arra y [2 ] = th ir d ;
i n t min = U t i l . M i n ( a r r a y ) ;

Voc pode ver que essa soluo evita a necessidade de um grande nmero de sobrecargas, mas ela
tem um preo: voc tem de escrever um cdigo adicional para preencher o array que voc passa.
Entretanto, voc pode fazer o compilador escrever um pouco desse cdigo para voc utilizando a
palavra-chave param s para declarar um arrayparam s.

Captulo11

Entendendo arrays de parmetros

253

Declarando um array params


Voc utiliza a palavra-chave param s como um modificador dos parmetros de array. Por exemplo, eis
o mtodo Min novamente, desta vez com seu parmetro de array declarado como um array param s
.
class U t i l

{
p u b lic s t a t i c i n t Min(params i n t [ ] paramList)

{
// cdigo exatamente como o a n t e r io r

}
}
O efeito da palavra-chave param s no mtodo Min ela permitir que voc o chame utilizando qual
quer nmero de argumentos inteiros. Por exemplo, para descobrir o mnimo de dois valores inteiros,
escreva:
i n t min = U t i l . M i n ( f i r s t , seco nd);

O compilador traduz essa chamada em um cdigo semelhante a este:


i n t [ ] a rra y = new i n t [ 2 ] ;
arra ytO ] = f i r s t ;
a r r a y [1 ] = second;
i n t min = U t i l . M i n ( a r r a y ) ;

Para descobrir o mnimo de trs valores inteiros, voc poderia escrever o cdigo mostrado aqui, que
tambm convertido pelo compilador no cdigo correspondente que utiliza um array:
i n t min = U t i l . M i n ( f i r s t , second, t h i r d ) ;

Ambas as chamadas ao mtodo M n (uma chamada com dois argumentos e outra com trs argumen
tos) determinam o mesmo mtodo Min com a palavra-chave param s. E como voc provavelmente
pode imaginar, possvel chamar esse mtodo Min com qualquer nmero de argumentos int. O
compilador apenas conta o nmero de argumentos int, cria um array int desse tamanho, preenche o
array com os argumentos e ento chama o mtodo passando o nico parmetro de array.

H vrios pontos sobre os arrays param s que vale a pena mencionar:


Voc no pode utilizar a palavra-chave param s em arrays multidimensionais. O cdigo no
exemplo a seguir no ir compilar:
// e rro de tempo de compilao
p u b lic s t a t i c i n t Min(params i n t [ , ]

table)

254

Parte

II Entendendo a linguagem C#

Voc no pode sobrecarregar um mtodo baseado apenas na palavra-chave param s. A pala


vra-chave param s no faz parte da assinatura do mtodo, como mostrado neste exemplo:
// e rr o de tempo de compilao: declarao duplicada
p u b lic s t a t i c i n t M i n ( i n t [ ] paramList)
p u b lic s t a t i c i n t Min(params i n t [ ] paramList)

Voc no pode especificar o modificador refou out com arrays param s , como mostrado neste
exemplo:
// e rr o de tempo de compilao
p u b lic s t a t i c i n t M in ( r e f params i n t [ ] paramList)
p u b lic s t a t i c i n t Min(out params i n t [ ] paramList)

Um array param s deve ser o ltimo parmetro. (Isso significa que voc s pode ter um array
param s por mtodo.) Considere este exemplo:
// e rr o de tempo de compilao
p u b l ic s t a t i c i n t Min (params i n t [ ] paramList, i n t i )

Um mtodo no param s sempre tem prioridade sobre um mtodo param s. Isso significa que, se
quiser, voc ainda pode criar uma verso sobrecarregada de um mtodo para os casos comuns.
Por exemplo:
p u b l ic s t a t i c i n t Min ( i n t le ftH andSide, i n t rightHandSide)
p u b l ic s t a t i c i n t Min(params i n t [ ] paramList)

A primeira verso do mtodo Min empregada quando chamada por meio da utilizao de dois
argumentos int. A segunda verso usada se algum outro nmero de argumentos int for forne
cido. Isso inclui o caso em que o mtodo chamado sem argumentos.
A adio do mtodo de array sem param s pode ser uma tcnica de otimizao til porque o
compilador no precisar criar e preencher muitos arrays.
O compilador detecta e rejeita todas as sobrecargas potencialmente ambguas. Por exemplo, os
dois mtodos Min a seguir so ambguos; no est claro qual deles deve ser chamado se voc
passar dois argumentos int:
// e rro de tempo de compilao
p u b l ic s t a t i c i n t Min(params i n t [ ] paramList)
p u b l ic s t a t i c i n t M i n ( i n t , params i n t [ ] paramList)

Captulo 11

Entendendo arrays de parmetros

255

Utilizando params objectf ]


Um array de parmetros do tipo int muito til porque ele permite passar qualquer nmero de argumentos int para uma chamada de mtodo. Contudo, e se no s o nmero de argumentos variar, mas
tambm o tipo de argumento? O C# tem uma maneira de resolver esse problema tambm. A tcnica
baseada no fato de que object a raiz de todas as classes e que o compilador pode gerar cdigo que
converte tipos-valor (coisas que no so classes) em objetos utilizando boxing, conforme descrito no
Captulo 8, "Entendendo valores e referncias". Voc pode utilizar um array de parmetros do tipo
object para declarar um mtodo que aceita qualquer nmero de argumentos object, permitindo que
os argumentos passados sejam de qualquer tipo. Veja este exemplo:
class Black
{
public static void Hole(params object [] paramList)

Atribu a esse mtodo o nome Black.Hole ("buraco negro") porque nenhum argumento pode escapar
dele:
Voc pode passar o mtodo sem nenhum argumento, caso em que o compilador passar um
array de objetos cujo comprimento 0:
Black.Hole ();
// convertido em Black.Hole(new object[0]);

Dica perfeitamente seguro tentar iterar por um array de comprimento zero utilizando uma
instruo foreach.

Voc pode chamar o mtodo Black.Hole passando null como o argumento. Um array um tiporeferncia, portanto, voc pode inici-lo com null.
Black. Hole(null);
Voc pode passar o mtodo Black.Hole para um array real. Em outras palavras, voc pode criar
manualmente o array que normalmente gerado pelo compilador:
object[] array = new object[2] ;
array[0] = "forty two";
array[1] = 42;
Black.Hole(array);
Voc pode passar para o mtodo Black.Hole qualquer outro argumento de tipo diferente, e esses
argumentos sero automaticamente encapsulados dentro de um array object:
Black.Hole("forty two", 42);
//convertido em Black.Hole(new objectf]{"forty two", 42});

256

Parte II

Entendendo a linguagem C#

O mtodo Console.WriteLine
A classe Console contm muitas sobrecargas para o m todo WriteLine. Uma dessas sobrecar
gas sem elhante a esta:
p u b lic s t a t i c void W r i t e L i n e ( s t r i n g format, params o b j e c t [ ] a r g ) ;
Essa sobrecarga perm ite que o m todo WriteLine suporte um argum ento de string de for
m ato que contm espaos reservados, cada um dos quais pode ser substitudo em tem po
de execuo por um a varivel de qualquer tipo. Eis um exem plo de uma cham ada a esse
mtodo:
Console .W ri te Line( Forename:{0}, Middle I n i t i a l : { l } ,
mi, lname, age);

Last name: { 2 } , Age: { 3 } , fname,

O com pilador resolve essa cham ada para o seguinte


C ons ole .W riteLine ( Firename: { 0 } , Middle I n i t i a l :
new o b j e c t [4 ] {fname, mi, lname, a g e }) ;

{1 >, Last name: { 2 } , Age: { 3 } " ,

Utilizando um array params


No exerccio a seguir, voc ir implementar e testar um mtodo static chamado Util.Sum. 0 objetivo
desse mtodo calcular a soma de um nmero varivel de argumentos int passados para ele, re
tornando o resultado como um int. Voc far isso escrevendo Util.Sum para aceitar um parmetro
param s intff. Voc implementar duas verificaes no parmetro param s a fim de assegurar que o
mtodo Util.Sum completamente robusto. Voc ento chamar o mtodo Util.Sum com diversos
argumentos diferentes para test-lo.
Escreva u m m to d o de a rray params
1. Inicie o Microsoft Visual Studio 2010 se ele ainda no estiver executando.
2. Abra o projeto ParamsArray, localizado na pasta \Microsoft PressWisual CSharp Step by Step\
Chapter 1l\SwitchStatement na sua pasta Documentos.
3. Exiba o arquivo Util.cs na janela Code and Text Editor.
O arquivo Util.cs contm uma classe vazia chamada Utl no namespace ParamsArray.
4. Adicione um mtodo esttico pblico chamado Sum classe Util.
O mtodo Sum retorna um int e aceita um array param s de valores int. O mtodo Sum deve ser
semelhante a este:
p u b l ic s t a t i c i n t SumCparams i n t [ ] paramList)

{
}
A primeira etapa na implementao do mtodo Sum verificar o parmetro param List. Alm
de conter um conjunto vlido de nmeros inteiros, ele pode tambm ser null ou um array de
comprimento zero. Em ambos os casos, difcil calcular a soma, portanto, a melhor opo
acionar uma ArgumentException. (Voc poder argumentar que a soma dos inteiros em um

Captulo 11

Entendendo arrays de parmetros

257

array de comprimento zero 0, mas, neste exemplo, trataremos essa situao como uma
exceo.)
5. Adicione cdigo a Sum que lana uma ArgumentException se param List for null.
O mtodo Sum agora deve se parecer com isso:
p u b lic s t a t i c i n t Sum(params i n t [ ] paramList)

{
i f (paramList == n u l l )

{
throw new A r g u m e n tE x c e p tio n (" U til.Sum: n u ll parameter l i s t " ) ;

}
}
6. Adicione cdigo ao mtodo Sum para lanar uma ArgumentException se o comprimento do ar
ray for 0, como mostrado em negrito aqui:
pu b lic s t a t i c i n t Sum(params i n t [ ] paramList)

{
i f (paramList == n u l l )

{
throw new A r g u m e n tE x c e p ti o n (" U til.Sum: n u ll parameter l i s t " ) ;

}
if (paramList.Length == 0)

{
throw new ArgumentExceptionC'Util.Sum: empty parameter list");

}
}
Se o array passar nesses dois testes, a prxima etapa ser adicionar todos os elementos juntos
ao array.
7. Voc pode utilizar uma instruoforeach para adicionar todos os elementos juntos. Voc preci
sar de uma varivel local para armazenar o total. Declare uma varivel do tipo inteiro chama
da sumTotal e a inicialize como 0, aps o cdigo do passo anterior. Adicione uma instruo//i?ach ao mtodo Sum para iterar pelo arrays paramList. O corpo desse \oopJbreach deve adicionar
cada elemento ao array a sumTotal. No final do mtodo, retorne o valor de sumTotal utilizando
uma instruo return, como mostrado em negrito a seguir:
class U t i l

{
p u b lic s t a t i c i n t Sum(params i n t [ ] paramList)

{
int sumTotal = 0;
foreach (int i in paramList)

{
sumTotal += i ;

}
return sumTotal;

>
}
8. No menu Build, clique em Build Solution. Confirme se sua soluo compilada sem nenhum
erro.

258

Parte II

Entendendo a linguagem C#

Teste o m to d o Util.Sum
1. Exiba o arquivo Program.cs na janela Code and Text Editor.

2. Na janela Code and Text Editor, localize o mtodo DoWork na classe Program.
3. Adicione a seguinte instruo ao mtodo DoWork-,
C o n s o le . W r i t e L i n e ( U t i l . S u m ( n u l l ) ) ;

4. No menu Debug, clique em Start Without Debugging. O programa compila e executa, escreven
do a seguinte mensagem no console:
Exception: U til.S u m : n u ll parameter l i st

Isso confirma que a primeira verificao no mtodo funciona.


5. Pressione a tecla Enter para finalizar o programa e retornar ao Visual Studio 2010.
6. Na janela Code and Text Editor, altere a chamada a Console. WriteLine em DoWork como mostra
do aqui:
C o n s o le . W r i t e L i n e (U t i l . S u m Q ) ;

Dessa vez, o mtodo est sendo chamado sem nenhum argumento. O compilador converter a
lista de argumentos vazia em um array vazio.
7. No menu Debug, clique em Start Without Debugging. O programa compila e executa, escreven
do a seguinte mensagem no console:
Exception: U ti l.S u m : empty parameter l i st

Isso confirma que a segunda verificao no mtodo funciona.


8. Pressione a tecla Enter para finalizar o programa e retornar ao Visual Studio 2010.
9. Altere a chamada a Console. WriteLine em DoWork como a seguir:
C o n s o le .W rite L in e (U til.S u m C lO , 9, 8, 7, 6, 5, 4, 3, 2, 1 ) ) ;

10. No menu Debug, clique em Start Without Debugging.


Verifique se o programa compila e executa e escreve o valor 55 no console.
11. Pressione Enter para fechar o aplicativo e voltar ao Visual Studio 2010.

Com parando arrays de parm etros e parm etros


opcionais
No Capitulo 3, Escrevendo mtodos e aplicando escopo", vimos como possvel definir mtodos
que aceitam parmetros opcionais. Em princpio, parece existir um nvel de sobreposio entre os
mtodos de usam arrays de parmetros e aqueles que aceitam parmetros opcionais. Entretanto, h
diferenas bsicas entre eles:

Captulo 11

Entendendo arrays de parmetros

259

Um mtodo que aceita parmetros opcionais tambm tem uma lista de parmetros fixos, e voc
no pode passar uma lista arbitrria de argumentos. O compilador gera um cdigo que insere
os valores padro na pilha para os argumentos ausentes, antes da execuo do mtodo, e o
mtodo no reconhece os argumentos fornecidos pelo chamador, nem os padres gerados pelo
compilador.
Um mtodo que utiliza um array de parmetros realmente possui uma lista totalmente arbitr
ria de parmetros, e nenhum deles tem valores padro. Alm disso, o mtodo pode determinar
exatamente quantos argumentos o chamador forneceu.
Em geral, os arrays de parmetros so utilizados nos mtodos que aceitam qualquer nmero de pa
rmetros (inclusive nenhum), enquanto os parmetros opcionais so utilizados somente quando no
conveniente instruir um chamador a fornecer um argumento para cada parmetro.
Ainda existe uma ltima questo que compensa considerar. Se voc definir um mtodo que aceita
uma lista de parmetros e fornecer uma sobrecarga que aceita parmetros opcionais, nem sempre
se torna evidente qual verso do mtodo ser chamada se a lista de argumentos na instruo de
chamada corresponder s assinaturas dos dois mtodos. Voc examinar essa situao no ltimo
exerccio deste captulo.
C o m p a re u m arra y de p ar m e tro s e p a r m e tro s opcionais
1. Retorne soluo ParamsArray no Visual Studio 2010, e exiba o arquivo Util.cs na janela Code

and Text Editor.


2. Adicione a seguinte instruo Console. WriteLine, mostrada em negrito, ao incio do mtodo
Sum, na classe Util.
pu b lic s t a t i c i n t Sum(params i n t [ ] paramList)

{
Console.WriteLine("Using parameter list");

}
3. Adicione outra implementao do mtodo Sum classe Util. Essa verso deve aceitar quatro
parmetros int opcionais, com um valor padro 0. No corpo do mtodo, d sada mensagem
Using optional parameters", e depois calcule e retorne a soma dos quatro parmetros. O mto
do concludo deve ficar parecido com o seguinte:
p u b lic s t a t i c i n t Sum(int paraml = 0, i n t param2 = 0, i n t param3 = 0, i n t param4 = 0)

{
C o n s o le .W ri te L i n e (Using opti onal paramete rs ");
i n t sumTotal = paraml + param2 + param3 + param4;
return sumTotal;

}
4. Exiba o arquivo Program.cs na janela Code and Text Editor.
5. No mtodo DoWork, comente o cdigo existente e adicione a seguinte instruo:
C o n s o le .W ri te L i n e ( U ti l.S u m (2 , 4, 6, 8 ) ) ;

260

Parte II

Entendendo a linguagem C#

Essa instruo chama o mtodo Sum e passa quarto parmetros int. Essa chamada combina
com ambas as sobrecargas do mtodo Sum.
6. No menu Debug, clique em Start Without Debugging para construir e executar o aplicativo.
Quando o aplicativo for executado, ele exibir as seguintes mensagens:
Using opti onal parameters
20

Nesse caso, o compilador gerou um cdigo que chamou o mtodo que aceita quatro parmetros
opcionais. Essa a verso do mtodo que mais corresponde chamada do mtodo.
7. Pressione Enter e retorne ao Visual Studio.
8. No mtodo Do Work, mude a instruo que chama o mtodo Sum, como mostrado a seguir:
C o n s o l e . W r i t e L i n e ( U t i l , Sum(2, 4, 6 ) ) ;

9. No menu Debug, clique em Start Without Debugging para construir e executar o aplicativo.
Quando o aplicativo for executado, ele exibir as seguintes mensagens:
Using optio nal parameters
12

O compilador ainda gerou um cdigo que chamou o mtodo que aceita parmetros opcionais,
embora a assinatura do mtodo no correspondesse exatamente chamada. Diante da escolha
entre utilizar um mtodo que aceita parmetros opcionais e um mtodo que aceita uma lista de
parmetros, o compilador usar o mtodo que aceita parmetros opcionais.
10. Pressione Enter e retorne ao Visual Studio.
11. No mtodo DoWork, mude a instruo que chama o mtodo Sum novamente.
C o n s o l e . W r i t e L i n e ( U t i l , Sum(2, 4, 6, 8, 1 0 ) ) ;

12. No menu Debug, clique em Start Without Debugging para construir e executar o aplicativo.
Quando o aplicativo for executado, ele exibir as seguintes mensagens:
Using parameter l i st
30

Dessa vez, h mais parmetros do que o mtodo que aceita parmetros opcionais especifica, de
modo que o compilador gerou um cdigo que chama o mtodo que aceita um array de parme
tros.
13. Pressione Enter e retorne ao Visual Studio.
Neste captulo, voc aprendeu a utilizar um array param s para definir um mtodo que pode receber
quantidades variveis de argumentos. Voc tambm viu como utilizar um array param s dos tipos
object para criar um mtodo que aceita qualquer nmero de argumentos de qualquer tipo. Voc tam
bm viu como o compilador resolve chamadas de mtodo quando ele pode escolher entre chamar um
mtodo que aceita um array de parmetros e chamar um mtodo que aceita parmetros opcionais.
Se voc quiser seguir para o prximo captulo agora:
Mantenha o Visual Studio 2010 em execuo e v para o Captulo 12.
Se quiser sair do Visual Studio 2010:
No menu File, clique em Exit. Se vir uma caixa de dilogo Save, clique em Yes e salve o projeto.

Captulo 11

Entendendo arrays de parmetros

261

Referncia rpida do Captulo 11


Para

Faa isto

Escrever um mtodo que aceita qualquer nmero de


argumentos de um dado tipo

Escreva um mtodo cujo parmetro um array params do tipo


dado. Por exemplo, um mtodo que aceita qualquer nmero
de argumentos bool declarado desta maneira:
someType MethodCparams bool [] fla g s )
{
}

Escrever um mtodo que aceita qualquer nmero de


argumentos de qualquer tipo

Escreva um mtodo cujo parmetro um array params de elementos do tipo object. Por exemplo:
someType MethodCparams o b je c t [] param List)

{
}

Captulo 12

Trabalhando com herana


Neste captulo, voc vai aprender a:
Criar u m a classe d eriva d a q u e herda recursos de u m a classe base.
C o n tro lar a o c u lta o e so b recarg a d e m to d o s utilizando as palavras-chave n e w , virtual
e override.
Lim itar a acessib ilid ad e d e n tro de u m a hierarq u ia de h eranas utilizan d o a palavra-chave

protected.
D efinir m to d o s d e extenso c o m o um m eca n ism o alte rn a tiv o ao uso de herana.

Herana um conceito-chave no mundo da orientao a objetos. Voc pode us-la como uma fer
ramenta para evitar a repetio ao definir classes diferentes com vrias caractersticas em comum
e que esto muito claramente relacionadas entre si. Talvez sejam classes diferentes do mesmo tipo,
cada uma delas com sua caracterstica prpria e distinta - por exemplo, gerentes, operrios e todos
os empregados de uma fbrica. Se voc estiver escrevendo um aplicativo para simular a fbrica,
como especificaria que os gerentes e operrios tm vrias caractersticas comuns, mas tambm so
diferentes? Por exemplo, todos tm um nmero de referncia de funcionrio, mas os gerentes tm
responsabilidades diferentes e executam tarefas diferentes dos operrios.
a que a herana se prova til.

O que herana?
Se perguntar a vrios programadores experientes o que eles entendem por herana , em geral, voc
obter respostas diferentes e contraditrias, o que deriva do fato de que a prpria palavra herana
tem vrios significados sutilmente diferentes. Se algum deixar algo para voc em um testamento,
dizemos que voc herdou. Da mesma maneira, voc herda metade dos seus genes da sua me e a
outra metade do seu pai. Esses dois usos da palavra herana tm pouco a ver com a herana em
programao.
Herana em programao essencialmente classificao - uma relao entre classes. Por exemplo,
quando estava no colgio, voc provavelmente aprendeu sobre mamferos e percebeu que cavalos e
baleias so exemplos de mamferos. Cada um tem todos os atributos que um mamfero tem (respira
ar, amamenta seus filhotes, tem sangue quente e assim por diante), mas cada um tambm tem suas
prprias caractersticas (um cavalo tem cascos, uma baleia tem barbatanas e uma cauda).

264

Parte II

Entendendo a linguagem C#

Como voc poderia modelar um cavalo e uma baleia em um programa? Uma maneira seria criar duas
classes distintas chamadas Horse e Whale. Cada classe poderia implementar os comportamentos que
so nicos a esse tipo de mamfero, como Trot (trotar, para um cavalo) ou Swim (nadar, para uma
baleia), de uma maneira especfica. Como voc trataria os comportamentos que so comuns a um
cavalo e a uma baleia, como Breathe (respirar) ou SuckleYoung (amamentar)? Voc poderia adicionar
mtodos duplicados com esses nomes s duas classes, mas essa situao torna-se um pesadelo de
manuteno, especialmente se voc tambm decidir comear a modelar outro tipos de mamferos,
por exemplo, Human ou Aardvark (tamandu).
No C#, voc pode utilizar a herana de classe para resolver essas questes. Um cavalo, uma baleia,
um humano e um tamandu so todos tipos de mamferos, portanto, crie uma classe chamada
Mammal que fornece a funcionalidade comum exibida por esses tipos. Voc pode ento declarar que
todas as classes Horse, Whale, Human e Aardvarks herdam de Mammal. Essas classes forneceriam
automaticamente a funcionalidade da classe Mammal [Breathe, SuckleYoung e assim por diante),
mas voc tambm poderia adicionar a funcionalidade peculiar de um tipo de mamfero particular
classe correspondente - o mtodo Trot para a classe Horse, e o mtodo Swim para a classe Whale. Se
precisar modificar a maneira como um mtodo comum como Breathe funciona, voc precisar alter-lo apenas em um nico lugar, a classe Mammal.

Utilizando a herana
Voc declara que uma classe herda de outra classe, ao utilizar a seguintes sintaxe:
class DerivedClass : BaseClass {

}
A classe derivada herda da classe base, e os mtodos na classe base tambm se tornam parte da clas
se derivada. No C#, uma classe pode ser derivada de, no mximo, uma classe base; uma classe no
pode ser derivada de duas ou mais classes. Mas, a menos que DerivedClass seja declarada como sea
led, voc pode criar outras classes derivadas que herdam de DerivedClass utilizando a mesma sintaxe
(discutiremos classes seladas no Captulo 13, Criando interfaces e definindo classes abstratas").
class DerivedSubClass : DerivedClass {

Importante

Todas as estruturas herdam de uma classe abstrata, chamada System.ValueType.

(Voc conhecer as classes abstratas no Captulo 13.) Este apenas um detalhe de implementao
do modo como o .BET Framework define o comportamento comum de tipos-valor baseados em
pilha. No possvel definir uma hierarquia de herana prpria com estruturas - s possvel definir
uma estrutura derivada de uma classe ou de outra estrutura.

Captulo 12

Trabalhando com herana

265

No exemplo descrito anteriormente, voc poderia declarar a classe Mammal como mostrado aqui.
Os mtodos Breathe e SuckleYoung so comuns a todos os mamferos.
class Mammal

{
p u b lic void Bre ath eO

{
}
p u b lic void SuckleYoungO

{
}
}
Ento voc poderia definir classes para cada tipo diferente de mamfero, acrescentando outros mto
dos, conforme necessrio. Por exemplo:
cla ss Horse : Mammal

{
public void TrotO

{
}
}
class Whale : Mammal

{
p u b lic void SwimO

{
}
}

Lembre-se de que a classe System.Object a classe raiz de todas as classes. Todas as classes derivam
implicitamente da classe System.Object. Consequentemente, o compilador C# reescreve discretamen
te a classeMammal como o cdigo a seguir (podendo ser escrita explicitamente se quiser):
class Mammal : System.Object

{
}

266

Parte II

Entendendo a linguagem C#

Qualquer mtodo na d a sst System.Object automaticamente rebaixado na cadeia de herana para


classes que derivam de Mammal, como Horse e Whale. Em termos prticos, significa que todas as
classes que voc define automaticamente herdam as caractersticas da classe System.Object. Inclui
mtodos, como ToString (j discutido no Captulo 2, Trabalhando com variveis, operadores e
expresses), que utilizado para converter um objeto em uma string, geralmente para propsitos
de exibio.

Chamando construtores da classe base


Alm dos mtodos por ela herdados, uma classe derivada contm automaticamente todos os campos
da classe base. Esses campos vo precisar de inicializao quando um objeto for criado. Esse tipo
de inicializao normalmente realizado em um construtor. Lembre-se de que todas as classes tm
pelo menos um construtor (se voc no fornecer um, o compilador vai gerar um construtor padro).
Uma boa prtica o construtor em uma classe derivada chamar o construtor para sua classe base
como parte da inicializao. Voc pode especificar a palavra-chave base para chamar um construtor
de classe base ao definir um construtor para uma classe herdeira, como mostrado neste exemplo:
class Mammal // classe base

{
p u b lic Mammal(string name)

// co n s tru to r para a classe base

{
}
>
class Horse : Mammal // classe derivada

{
p u b l ic H o r s e (s t r i n g name)
: base(name) // chama Mammal(name)

{
}
}
Se voc no chamar explicitamente um construtor da classe base em um construtor da classe de
rivada, o compilador tentar inserir silenciosamente uma chamada ao construtor padro da classe
base antes de executar o cdigo no construtor da classe derivada. Considerando o exemplo anterior,
o compilador reescreve este cdigo:
class Horse : Mammal

{
p u b l ic H o rs e f s tr in g name)

{
}
}

Captulo 12

Trabalhando com herana

267

assim:
class Horse : Mammal

{
p u b lic H o rs e (s tr i n g name)
: baseO

{
}
}
Isso funcionar st Mammal tiver um construtor padro pblico. Mas nem todas as classes tm um
construtor padro pblico (por exemplo, lembre-se de que o compilador gera apenas um construtor
padro, caso voc no escreva construtores no padro), em cujo caso esquecer de chamar o constru
tor correto da classe base resulta em um erro de compilao.

Atribuindo classes
Nos exemplos anteriores, voc viu como declarar uma varivel utilizando um tipo classe e ento
como utilizar a palavra-chave new para criar um objeto. Viu tambm como as regras de verificao
de tipo do C# impedem que voc atribua um objeto de um tipo a uma varivel declarada como um
tipo diferente. Por exemplo, dadas as definies das classes Mammal, Horse e Whales mostradas
aqui, o cdigo depois dessas definies invlido:
class Mammal

{
}
class Horse : Mammal

{
}
class Whale : Mammal

{
}
Horse myHorse = new H o rs e (" N e d d y " ); // 0 co n s tru to r mostrado antes espera um nome!
Whale myWhale = myHorse;
// erro - t i p o s d i feren te s

Mas possvel referenciar um objeto a partir de uma varivel de um tipo diferente desde que o tipo
utilizado seja uma classe que esteja acima na hierarquia de heranas. Portanto, as instrues a se
guir so legais:
Horse myHorse = new H o rs e (" N e d d y " );
Mammal myMammal = myHorse; // v l i d o , Mammal a classe base de Horse

268

Parte II

Entendendo a linguagem C#

Se pensar em termos lgicos, todos os Horses so Mammals, portanto, possvel atribuir de uma
maneira segura um objeto do tipo Horse a uma varivel do tipo Mammal. A hierarquia de herana
significa que voc pode imaginar um Horse como um tipo especial de Mammal; ele tem tudo que um
Mammal tem, com algumas caractersticas a mais, definidas por todos os mtodos e campos que voc
adicionar classe Horse. Voc tambm pode fazer uma varivel Mammal referenciar um objeto Wha
le. Mas h uma limitao significativa - ao referenciar um objeto Horse ou Whale utilizando uma
varivel Mammal, voc s pode acessar mtodos e campos definidos pela classe Mammal. Qualquer
mtodo adicional definido pela classe Horse ou Whale no visvel pela classe Mammal.
Horse myHorse = new H o rs e (" N e d d y " );
Mammal myMammal = myHorse;
myMammal . B r e a t h e Q ; // OK - Breathe parte da cla sse Mammal
myMammal . T r o t O ; // erro - T r o t no parte da classe Mammal

Preste ateno porque a situao inversa no verdadeira. Voc no pode atribuir um objeto Mam

m al irrestritamente a uma varivel Horse-,


Mammal myMammal = newMammal("Mammalia");
Horse myHorse = myMammal; // err o

Isso parece uma restrio estranha, mas lembre-se de que nem todos os objetos Mammals so Horse s - alguns podem ser Whales. possvel atribuir um objeto Mammal a uma varivel Horse con
tanto que voc verifique primeiro se Mammal realmente um Horse, utilizando o operador as ou
is ou utilizando um casting. 0 exemplo de cdigo a seguir emprega o operador as para verificar se
myMammal referencia um Horse e, em caso afirmativo, a atribuio a myHorseAgain resulta em
myHorseAgain referenciando o mesmo objeto Horse. Se myMammal referenciar alguns outros tipos
de Mammal, o operador as retornar, em vez disso, null.
Horse myHorse = new H o rs e (" N e d d y " );
Mammal myMammal = myHorse;
Horse myHorseAgain = myMammal as Horse;

// myMammal re fe re n cia um Horse


// OK - myMammal era um Horse

Whale myWhale = new Whale("Moby D i c k " ) ;


myMammal = myWhale;
myHorseAgain = myMammal as Horse;

// re torna n u ll - myMammal era uma Whale

Captulo 12

Trabalhando com herana

269

Declarando mtodos new


Um dos problemas mais difceis no campo da programao a tarefa de inventar nomes exclusivos
e significativos para os identificadores. Se voc est definindo um mtodo para uma classe e essa
classe faz parte de uma hierarquia de herana, mais cedo ou mais tarde, voc tentar reutilizar um
nome que j est em uso por uma das classes de nvel mais elevado na hierarquia. Se acontecer de
uma classe base e uma classe derivada declararem dois mtodos que tm a mesma assinatura, voc
receber um aviso ao compilar o aplicativo.

O mtodo na classe derivada mascara (ou oculta) o mtodo na classe base que tem a mesma assina
tura. Por exemplo, se voc compilar o cdigo a seguir, o compilador ir gerar uma mensagem de aviso
informado que Horse. Talk oculta o mtodo herdadoMammal.Talk-.
cla ss Mammal

{
p u b lic void T a l k O // pressupe que todos os mamiferos podem f a l a r

{
}
}
class Horse : Mammal

{
p u b lic void T a l k O

// cavalos falam diferentemente dos outros mamiferos!

{
}
}
Embora seu cdigo seja compilado e executado, voc deve considerar seriamente esse aviso. Se outra
classe derivar de Horse e chamar o mtodo Talk, ela talvez esteja esperando que o mtodo imple
mentado na classe Mammal seja chamado. Mas o mtodo Talk na classe Horse oculta o mtodo Talk
na classe Mammal e, em vez disso, o mtodo Horse. Talk ser chamado. Na maioria das vezes, essa
coincidncia um engano e voc deve pensar em renomear os mtodos para evitar conflitos. Mas
se tiver certeza de que quer que os dois mtodos tenham a mesma assinatura, ocultando assim o
mtodo Mammal. Talk, voc pode silenciar o aviso utilizando a palavra-chave new como a seguir:

270

Parte II

Entendendo a linguagem C#

class Mammal

{
p u b l ic void T a l k O

{
}
}
cla ss Horse : Mammal

{
new p u b l ic void T a l k ( )

{
}
}
Dessa maneira o uso da palavra-chave new no altera o fato de que os dois mtodos no tm relao
alguma e de que a ocultao continua ocorrendo. Ela simplesmente desativa o aviso. Na verdade,
a palavra-chave new diz: Eu sei o que estou fazendo, portanto, pare de me mostrar esses avisos.

Declarando mtodos virtuais


s vezes, voc quer ocultar a maneira como um mtodo implementado em uma classe base. Como
exemplo, considere o mtodo ToString em System.Object. A finalidade de ToString converter um
objeto em sua representao string. Como esse mtodo muito til, ele um membro da classe Sys
tem.Object-, portanto, fornece automaticamente um mtodo ToString a todas as classes. Mas como
a verso de ToString implementada por System.Object sabe como converter uma instncia de uma
classe derivada em uma string? Uma classe derivada poder conter qualquer nmero de campos
com valores interessantes que devero fazer parte da string. A resposta que a implementao de
ToString em System.Object , na verdade, um pouco simplista. Tudo o que ele pode fazer converter
um objeto em uma string que contm o nome do seu tipo, como Mammal ou Horse". No final das
contas, no muito til. Ento, por que fornecer um mtodo to intil? A resposta para essa segun
da pergunta exige que voc pense um pouco mais detalhadamente.
Obviamente, ToString uma boa ideia como um conceito, e todas as classes devem fornecer um
mtodo que possa ser utilizado para converter objetos em strings para propsitos de exibio ou de
purao. Mas s a implementao que problemtica. De fato, voc no precisa chamar o mtodo
ToString definido por System.Object - ele simplesmente um espao reservado. Em vez disso, voc
deve fornecer sua prpria verso do mtodo ToString em cada classe que definir, desconsiderando a
implementao padro em System.Object. A verso em System.Object s est l como uma rede de
segurana, no caso de uma classe no implementar seu prprio mtodo ToString. Dessa maneira,
voc pode ter certeza de que possvel chamar ToString em qualquer objeto e que o mtodo retornar
uma string contendo algo.

Captulo 12

Trabalhando com herana

271

Um mtodo escrito para ser redefinido chamado mtodo virtual. Voc precisa saber a diferena
entre redefinir um mtodo e ocultar um mtodo. Redefinir um mtodo um mecanismo para forne
cer diferentes implementaes do mesmo mtodo - os mtodos esto todos relacionados porque se
destinam a executar a mesma tarefa, mas de uma maneira especfica classe. Ocultar um mtodo
um meio de substituir um mtodo por outro - os mtodos em geral no esto relacionados e podem
executar tarefas totalmente diferentes. Sobrescrever um mtodo um conceito til de programao;
ocultar um mtodo normalmente um erro.
Voc pode marcar um mtodo como virtual utilizando a palavra-chave virtual. Por exemplo, o mtodo
ToString na classe System.Object definido assim:
namespace System

{
class Object

{
p u b lic v i r t u a l s t r i n g T o S t r i n g O

{
}
}
}

Declarando mtodos override


Se uma classe base declara que um mtodo virtual, uma classe derivada pode utilizar a pala
vra-chave override para declarar outra implementao desse mtodo. Por exemplo:
class Horse : Mammal

{
p u b lic o v e rrid e s t r i n g T o S t r i n g O

{
}
}
A nova implementao do mtodo na classe derivada pode chamar a implementao original do
mtodo na classe base utilizando a palavra-chave base , assim:
p u b lic o v e rrid e s t r i n g T o S t r i n g O

{
b a s e .To S trin g O ;
}

272

Parte

II Entendendo a linguagem C#

H algumas regras importantes que voc precisa seguir ao declarar mtodos polimrficos (como
discutido na barra lateral, Mtodos virtuais e polimorfismo) utilizando as palavras-chave virtual
e override:
Voc no pode declarar um mtodo privado utilizando a palavra-chave virtual ou override. Se
tentar, haver um erro de tempo de compilao. Privado realmente privado.
As duas assinaturas de mtodo devem ser idnticas - isto , elas devem ter o mesmo nome e o
mesmo nmero e tipo de parmetros. Alm disso, o dois mtodos devem retornar o mesmo tipo.
Os dois mtodos devem ter o mesmo nvel de acesso. Por exemplo, se um dos dois mtodos for
pblico, o outro tambm dever ser pblico (mtodos tambm podem ser protegidos; como ve
remos na prxima seo).
Voc s pode redefinir um mtodo virtual. Se o mtodo da classe base no for virtual, e voc
tentar redefini-lo, isso resultar em um erro de tempo de compilao. Este tema delicado; cabe
classe base decidir se seus mtodos podem ser redefinidos.
Se a classe derivada no declarar o mtodo utilizando a palavra-chave override, ela no redefi
nir o mtodo da classe de base. Em outras palavras, torna-se uma implementao de um mto
do completamente diferente que, por acaso, tem o mesmo nome. Como antes, isso acarretar um
aviso de ocultao em tempo de compilao, que voc pode silenciar utilizando a palavra-chave
new como descrito anteriormente.
Um mtodo override implicitamente virtual e pode ser redefinido em uma classe derivada pos
terior. Mas voc no pode declarar explicitamente que um mtodo override virtual utilizando
a palavra-chave virtual.

Mtodos virtuais e polimorfismo


Os m todos virtuais perm item cham ar diferentes verses do mesmo m todo, com base no
tipo de objeto determ inado dinam icam ente em tem po de execuo. Considere as classes do
exemplo a seguir que definem um a variao na hierarquia Mammal descrita anteriormente:
class Mammal

{
p u b l ic v i r t u a l s t r i n g GetTypeNameO

{
return " T h i s i s a mammal ;

}
}
class Horse : Mammal

{
p u b l ic o v e r rid e s t r i n g GetTypeNameO

{
re turn " T h i s i s a horse";

Captulo 12

Trabalhando com herana

273

}
cla ss Whale : Mammal

{
p u b lic o v e rrid e s t r i n g GetTypeName ( )

{
re turn " T h i s i s a whale";

}
}
cla ss Aardvark : Mammal

{
>
Note duas coisas: em primeiro lugar, a palavra-chave override usada pelo m todo GetType

Name nas classes Horse e Whale, e, em segundo lugar, o fato de que a classe Aardvark no
tem um m todo GetTypeName.
Agora, examine o bloco de cdigo a seguir:
Mammal myMammal;
Horse myHorse = new H o r s e ( . . . ) ;
Whale myWhale = new Whale( . . . ) ;
Aardvark myAardvark = new A a r d v a r k ( . . . ) ;
myMammal = myHorse;
Console.WriteLineCmyMammal .Ce tT ypeN am eO ); // Horse
myMammal = myWhale;
Console.WriteLine(myMammal .Ce tT ypeN am eO ); // Whale
myMammal = myAardvark;
Console.Wri teLineCmyMammal .CetTypeNameO) ; // Aardvark
Qual ser a sada das trs diferentes instrues Console. W riteLine! primeira vista, voc
poderia esperar que todas elas imprimissem "This is a m am m al" ("E ste um m am fero"),
porque cada instruo cham a o m todo GetTypeName na varivel myM amm al, que um

Mammal. Mas, no primeiro caso, voc pode ver que myM amm al na verdade uma refern
cia a um Horse (lembre-se de que voc no pode atribuir um Horse a uma varivel Mammal
porque a classe Horse herda da classe Mammal). Visto que o m todo GetTypeName defini
do com o virtual, o runtime determ ina que ele deve cham ar o m todo Horse.GetTypeName,
portanto, a instruo na verdade im prim e a m ensagem "This is a horse" ("E ste um cava
lo)". A mesma lgica se aplica segunda instruo Console. WriteLine, que em ite a m ensa
gem "This is a w h a le " ("Esta um a baleia"). A terceira instruo cham a Console. WriteLine
em um objeto Aardvark. M as a classe Aardvark no tem um m todo GetTypeName, ento
o m todo padro na classe Mam m al cham ado, retornando a string "This is a m am m al".
Esse fenm eno da mesma instruo invocando um m todo diferente, dependendo de seu
contexto, cham ado de polimorfismo, que literalm ente significa "m uitas form as".

274

Parte II

Entendendo a linguagem C#

Entendendo o acesso protected


As palavras-chave de acesso public e privaCe criam dois extremos de acessibilidade: os campos e
mtodos pblicos de uma classe so acessveis a todos, enquanto os campos e mtodos privados de
uma classe so acessveis apenas prpria classe.
Esses dois extremos so suficientes considerando-se as classes isoladamente. Mas como todos os
programadores experientes em orientao a objetos sabem, as classes isoladas no podem resolver
problemas complexos. A herana uma maneira muito poderosa de conectar as classes, e h clara
mente uma relao especial e prxima entre uma classe derivada e sua classe base. Frequentemente
til para a classe base permitir que as classes derivadas acessem alguns de seus membros, enquan
to oculta esses mesmos membros das classes que no fazem parte da hierarquia. Nessa situao,
voc pode utilizar a palavra-chave protected. para marcar os membros:
Se uma classe A deriva de outra classe B, ela pode acessar os membros de classe protegidos da
classe B. Em outras palavras, dentro da classe A derivada, um membro protegido da classe B
pblico.
Se uma classe A no deriva de outra classe B, ela no pode acessar membro algum protegido da
classe B. Em outras palavras, dentro da classe A, um membro protegido da classe B privado.
O C# d aos programadores completa liberdade para declarar mtodos e campos como protegidos.
Mas a maioria das diretrizes de programao orientada a objetos recomenda manter seus campos
estritamente privados. Os campos pblicos violam o encapsulamento porque todos os usurios da
classe tm acesso direto e irrestrito aos campos. Os campos protegidos mantm o encapsulamento
para os usurios de uma classe, para quem so inacessveis, mas esses ainda permitem que o encap
sulamento seja violado pelas classes que herdam da classe.

No exerccio a seguir, voc vai definir uma hierarquia simples de classes para modelar diferentes
tipos de veculos. Sero definidas uma classe base chamada Vehicle e classes derivadas chamadas
Airplane e Car. Voc vai definir mtodos comuns chamados StartEngine e StopEngne na classe Vehi
cle e adicionar alguns mtodos s duas classes derivadas que so especficas a essas classes. Voc
ento adicionar um mtodo virtual chamado Drive classe Vehicle e redefinir a implementao
padro desse mtodo nas duas classes derivadas.
Crie u m a hierarquia de classes
1. Inicie o Microsoft Visual Studio 2010 se ele ainda no estiver executando.
2. Abra o projeto Vehic/es, localizado na pasta \Microsoft PressWisual CSharp Step By Step\ Chapter
12\Vehicles na sua pasta Documentos.

Captulo 12

Trabalhando com herana

275

O projeto Vehicles contm o arquivo Program.cs, que define a classe Program com os mtodos
Main e DoWork que vimos nos exerccios anteriores.
3. No Solution Explorer, clique com o boto direito do mouse no projeto Vehicles, aponte para/trf
e ento clique em Class.
A caixa de dilogo Add New ItemVehicles aparece, permitindo adicionar um novo arquivo que
definir uma classe para o projeto.
4. Na caixa de dilogo Add New Item Vehicles, verifique se o template Class est destacado no
painel central, digite Vehicle.cs na caixa Name e clique em Add.
O arquivo Vehicle.cs criado e adicionado ao projeto e aparece na janela Code and Text Editor.
O arquivo contm a definio de uma classe vazia chamada Vehicle.
5. Adicione os mtodos StartEngine e StopEngine classe Vehicle como mostrado em negrito a
seguir:
c la s s V e h icle

{
p u b l ic void S ta r tE n g in e C s tr in g noiseToMakeWhenStarting)

{
C o n s o le . W r i t e L i n e (" S t a r t i n g engine: { 0 } " , noiseToMakeWhenStarting);

}
pu b lic void S to p E n g in e (s trin g noiseToMakeWhenStopping)

{
Console .W rite LineC 'S to pp ing engine: { 0 } " , noiseToMakeWhenStopping);

}
}
Todas as classes que derivam da classe Vehicle herdaro esses mtodos. Os valores para os
parmetros noiseToMakeWhenStarting e noiseToMakeWhenStopping sero diferentes para cada
tipo diferente de veculo, e isso o ajudar posteriormente a identificar qual veculo est sendo
iniciado e parado.
6. No menu Project, clique em Add Class.
A caixa de dilogo Add New ItemVehicles aparece mais uma vez.

7. Na caixa Name, digite Airplane.cs e ento clique em Add.


Um novo arquivo contendo uma classe chamada Airplane adicionado ao projeto e aparece na
janela Code and Text Editor.
8. Na janela Code and Text Editor, modifique a definio da classe Airplane de modo que ela herde
da classe Vehicle, como mostrado em negrito:
class Airp la n e : Veh icle

{
}

276

Parte II

Entendendo a linguagem C#

9. Adicione os mtodos TakeOfft Land classe Airplane, como mostrado em negrito:


cla ss A irp la n e : Vehicle

{
pu b lic void TakeOff( )

{
C o n s o le .W rite Lin e ("Ta king o f f " ) ;

/ / Decolan do

}
p u b lic v o id Lan d()

{
C o n s o le .W rite L in e (" L a n d in g " ); // A te rris s an do

}
10. No menu Project, clique emAdd Class.
A caixa de dilogo Add New ItemVehicles aparece mais uma vez.
11. Na caixa Name, digite Car.cs e ento clique em Add.
Um novo arquivo contendo uma classe chamada Car adicionado ao projeto e aparece na janela

Code and Text Editor.


12. Na janela Code and Text Editor, modifique a definio da classe Car de modo que ela derive da
classe Vehicle, como mostrado em negrito:
class Car : V e h i c l e

{
}
13. Adicione os mtodos Accelerate t Brake classe Car, como mostrado em negrito:
class Car : Vehicle

{
p u b lic void A c c e le ra te O

{
C o n s o l e . W r i t e L i n e ( " A c c e l e r a t i n g " ) ; / / A c e le r a n d o

}
p u b lic vo id B ra ke ()

{
C o n s o l e . W r i t e L i n e ( " B r a k i n g " ) ; // A cio n a n d o o f r e i o

}
14. Exiba o arquivo Vehicle.cs na janela Code and Text Editor.
15. Adicione a implementao padro do mtodo virtual Drive classe Vehicle, como mostrado em
negrito:
class Ve h icle

{
p u b lic v ir t u a l void D r i v e O

{
C o n s o l e . W r i t e L i n e ( " D e f a u l t im p le m e n t a ti o n o f t h e D r i v e m e t h o d " );

Captulo 12

Trabalhando com herana

277

16. Exiba o arquivo Program.cs na janela Code and Text Editor.

17. No mtodo DoWork, crie uma instncia da classe Airplane e teste os mtodos simulando uma
viagem rpida de avio, como a seguir:
s t a t i c void DoWorkO

{
C ons o le .W rite Lin e (" Jo u rn e y by a i r p l a n e : " ) ;
Airp la n e myPlane = new A i r p l a n e C ) ;
myPlane. S tartE ngi n e (" C o n ta c t" ) ;
m y P la n e .Ta k e O f f O ;
m y P la n e . D r i v e O ;
m yP la ne.Land O ;
myPlane. StopEngineC'Whi r r " ) ;

}
18. Adicione as seguintes instrues mostradas em negrito ao mtodo DoWork depois do cdigo
que voc acabou de escrever. Essas instrues criam uma instncia da classe Car e testa seus
mtodos.
s t a t i c void DoWorkO

{
C o n s o l e . W r i t e L i n e ( " \ n J o u r n e y by c a r : " ) ;
C a r myCar = new C a r ( ) ;
m y C a r.S ta rtE n g in e ("B rm b rm ");
my Car. A c c e l e r a t e O ;
m y C a r.D rive ();
m yC a r.B ra k e ();
m y C ar.Sto pEngine("P hut p h u t " ) ;

19. No menu Debug, clique em Start Without Debugging.


Na janela de console, verifique se o programa emite mensagens simulando as diferentes etapas
de uma viagem de avio e de carro, como mostrado na imagem a seguir:
G5B C:\Windows\system32\cmd.exe
J o u r n e y by a i r p l a n e :
S t a r t i n g e n g in e : C o n ta c t
T a k in g o f f
D e f a u lt im p le m e n ta tio n o f th e d r iv e m ethod
L an d in g
S to p p in g E n g in e : W h irr
J o u r n e y by c a r :
S t a r t i n g e n g in e : Brm brm
A c c e le r a tin g
D e f a u lt im p le m e n ta tio n o f t h e d r i v e m ethod
B r a k in g
S to p p in g E n g in e : Phu t phut
P r e s s any k e y t o c o n t in u e . .

Observe que os dois meios de transporte invocam a implementao padro do mtodo virtual

Drive porque nenhuma classe atualmente redefine esse mtodo.


20. Pressione Enter para fechar o aplicativo e retornar ao Visual Studio 2010.

278

Parte II

Entendendo a linguagem C#

21. Exiba a classe Airplane na janela Code and Text Editor. Redefina o mtodo Drive na classe Air
plane, desta maneira:
p u b l ic o v e r rid e void D r i v e O

{
Co ns o le.W riteLine("Flying");

22. Exiba a classe Car na janela Code and Text Editor. Redefina o mtodo Drive na classe Car da
seguinte maneira:
p u b l ic o v e r rid e void D r i v e O

{
Console . Wri te L i neC'Motori ng") ;

}
23. No menu Debug, clique em Start Without Debugging.
Na janela de console, observe que o objeto Airplane agora exibe a mensagem Flying quando o
aplicativo chama o mtodo Drive, e o objeto Car apresenta a mensagem Motoring.
24. Pressione Enter para fechar o aplicativo e retornar ao Visual Studio 2010.
25. Exiba o arquivo Program.cs na janela Code and Text Editor.
26. Adicione as instrues mostradas em negrito ao final do mtodo DoWork:
s t a t i c void DoWorkO

{
C o n s o le .W rite L in e ("\ n T e s tin g polym orphism ");
V e h i c l e v = myCar;
v .D riv e O I
v = myPlane;
v .D riv e O ;

}
Esse cdigo testa o polimorfismo do mtodo virtual Drive. O cdigo cria uma referncia ao ob
jeto Car utilizando uma varivel Vehicle (o que seguro, pois todos os objetos Car so Vehicle)
e ento chama o mtodo Drive empregando essa varivel Vehicle. As duas instrues finais
referenciam a varivel Vehicle ao objeto Airplane e chamam o que parece ser o mesmo mtodo
Drive mais uma vez.
27. No menu Debug, clique em Start Without Debugging.

Captulo 12

Trabalhando com herana

279

Na janela de console, verifique se as mesmas mensagens aparecem como anteriormente segui


das por este texto:
T e s tin g polymorphism
Motoring
F ly in g

O mtodo Drive virtual, portanto, o runtime (no o compilador) determina qual verso do m
todo Drive chamar ao invoc-lo por meio de uma varivel Vehicle com base no tipo real do objeto
referenciado por essa varivel. No primeiro caso, o objeto Vehicle referencia um Car, assim o
aplicativo chama o mtodo Car.Drive. No segundo caso, o objeto Vehicle referencia um Airplane,
portanto, o aplicativo chama o mtodo Airplane.Drive.
28. Pressione Enter para fechar o aplicativo e retornar ao Visual Studio 2010.

Entendendo m todos de extenso


A herana um recurso poderoso, pois pemite que voc estenda a funcionalidade de uma classe
criando uma nova classe que derive dela. Mas, s vezes, o uso da herana no o mecanismo mais
apropriado para adicionar novos comportamentos, especialmente se voc precisa estender rapida
mente um tipo sem afetar o cdigo existente.
Por exemplo, suponha que voc queira adicionar um novo recurso ao tipo in t- um mtodo chamado
Negate que retorna o valor negativo equivalente que um inteiro atualmente contm (eu sei que voc
poderia simplesmente utilizar o operador unrio de menos [-] para realizar a mesma tarefa, mas seja
paciente). Uma maneira de alcanar isso definir um novo tipo chamado Neglnt32 que herda de
System.Int32 (int um alias para System.Int32) e que adiciona o mtodo Negate-,
class Neglnt32 : System.Int32

// No tente is so!

{
pu b lic i n t Negate( )

{
}
}
A teoria que Neglnt32 herdar toda a funcionalidade associada ao tipo System.Int32 alm do m
todo Negate. H duas razes para voc no querer seguir essa abordagem:
Esse mtodo s ser aplicado ao tipo Neglnt32 e se voc quiser utiliz-lo com as variveis int
existentes no seu cdigo, voc ter de alterar a definio de cada varivel int para o tipo Ne-

glnt32.
O tipo System.Int32 na verdade uma estrutura, no uma classe, e voc no pode utilizar he
rana com estruturas.
Aqui os mtodos de extenso tornam-se muito teis.

280

Parte II

Entendendo a linguagem C#

Um mtodo de extenso permite estender um tipo existente (uma classe ou uma estrutura) com m
todos estticos adicionais. Esses mtodos estticos tornam-se imediatamente disponveis para seu
cdigo em qualquer instruo que referencie dados do tipo estendido.
Voc define um mtodo de extenso em uma classe esttica e especifica o tipo que o mtodo aplica
a ela como o primeiro parmetro para o mtodo, juntamente com a palavra-chave th is. A seguir, um
exemplo que mostra como voc pode implementar o mtodo de extenso Negate para o tipo int:
s t a t i c cla ss U t i l

{
p u b l ic s t a t i c i n t Neg ate(th is i n t i )

{
re turn - i ;

>
}
A sintaxe parece um pouco estranha, mas a palavra-chave this prefixando o parmetro para Negate
que o identifica como um mtodo de extenso, e o fato de que o parmetro que this prefixa um int
significa que voc est estendendo o tipo int.
Para utilizar o mtodo de extenso, coloque a classe Util no escopo. (Se necessrio, adicione uma
instruo using especificando o namespace qual a classe Util pertence.) Ento, voc poder sim
plesmente utilizar a notao
para referenciar o mtodo, desta maneira:
i n t x = 591;
C o n s o le .W rite L in e C x .N e g a te { 0 } " , x . N e g a t e O ) ;

Observe que voc no precisa referenciar a classe Util em nenhum lugar na instruo que chama o
mtodo Negate. O compilador C# detecta automaticamente todos os mtodos de extenso para um
dado tipo a partir de todas as classes estticas que esto em escopo. Voc tambm pode chamar o
mtodo Utils.Negate passando um int como o parmetro, utilizando a sintaxe comum que j vimos
anteriormente, embora esse uso torne bvio o propsito da definio do mtodo como um mtodo
de extenso:
i n t x = 591;
C onso le .W rite Lin e ("x.N e g a te { 0 } " , U t i l . N e g a t e ( x ) ) ;

No exerccio a seguir, voc adicionar um mtodo de extenso ao tipo int. Esse mtodo de extenso
permite converter o valor que uma varivel int contm a partir da base 10 em uma representao
desse valor em uma base numrica diferente.
Crie u m m to d o de extenso
1. No Visual Studio 2010, abra o projeto ExtensionMethod, localizado na pasta \Microsoft Press\
Visual CSharp Step by Step\Chapter 12\ExtensionMethod na sua pasta Documentos.

Captulo 12

Trabalhando com herana

281

2. Exiba o arquivo Util.cs na janela Code and TextEditor.


Esse arquivo contm uma classe esttica chamada Util em um namespace chamado Extensions.
Essa classe est vazia, com exceo dos comentrios //to do. Lembre-se de que voc deve definir
mtodos de extenso dentro de uma classe esttica.
3. Adicione um mtodo esttico pblico classe Util, chamado ConvertToBase. O mtodo deve
receber dois parmetros: um parmetro int nomeado i, prefixado com a palavra-chave this para
indicar que o mtodo um mtodo de extenso para o tipo int, e um outro parmetro int normal
nomeado baseToConvertTo. O mtodo converter o valor em i na base indicada por baseToConvertTo, devendo retornar um int contendo o valor convertido.
O mtodo ConvertToBase deve ser semelhante a este:
s t a t i c class U t i l

{
p u b lic s t a t i c i n t C onvertT oBase(th is i n t i ,

i n t baseToConvertTo)

{
}
}
4. Adicione uma instruo i f ao mtodo ConvertToBase que verifica se o valor do parmetro ba
seToConvertTo est entre 2 e 10. O algoritmo utilizado por este exerccio no funciona de uma
maneira confivel fora desse intervalo de valores. Lance uma ArgumentException com uma
mensagem adequada se o valor de baseToConvertTo estiver fora desse intervalo.
O mtodo ConvertToBase deve ser semelhante a este:
p u b lic s t a t i c i n t Con vertT oBase(th is i n t i , i n t baseToConvertTo)

{
i f (baseToConvertTo < 2 || baseToConvertTo > 10)
throw new ArgumentException("Value cannot be converted to base " +
baseTo ConvertTo.ToStringO );

}
5. Adicione as seguintes instrues mostradas em negrito ao mtodo ConvertToBase, aps a instru
o que lana a ArgumentException. Esse cdigo implementa um algoritmo bem conhecido que
converte um nmero de base 10 em uma base numrica diferente. (Voc viu uma verso desse
algoritmo para converter um nmero decimal em octal no Captulo 5, Utilizando atribuio
composta e instrues de iterao.)
p u b lic s t a t i c i n t ConvertToBaseCthis i n t i ,

{
i n t r e s u l t = 0;
i n t it e r a t i o n s = 0;
do

i n t baseToConvertTo)

282

Parte II

Entendendo a linguagem C#

{
i n t n e x t D i g i t = i % b a se T o C o n v e r tT o ;
i /= b a s e T o C o n v e r tT o ;
r e s u l t += n e x t D i g i t * ( i n t ) M a t h . P o w ( l O ,

ite ra tio n s);

ite ra tio n s+ + ;

}
w hile ( i

!= 0 ) ;

return re s u lt;

}
6. Exiba o arquivo Program.cs na janela Code and Text Editor.
7. Adicione a seguinte instruo using aps a instruo using System; na parte superior do arquivo:
using Extensions;

Essa instruo d escopo ao namespace contendo a classe Util. O mtodo ConvertToBase de


extenso no ser visvel no arquivo Program.cs se voc no realizar essa tarefa.
8. Adicione as seguintes instrues ao mtodo DoWork da classe Program
.
i n t x = 591;
f o r ( i n t i = 2; i < = 1 0 ; i+ + )

{
Consol e . W r i t e L i n e ( " { 0 } in base { 1 } i s { 2 } " , x, i , x . C o n v e r t T o B a s e ( i ) ) ;

}
Esse cdigo cria um int chamado x e o configura com o valor 591 (voc pode selecionar qual
quer valor inteiro que quiser). O cdigo ento utiliza um loop para imprimir o valor 591 em
todas as bases numricas entre 2 e 10. Observe que ConvertToBase aparece como um mtodo de
extenso no IntelliSense quando voc digita o ponto (.) aps x na instruo Console. WriteLine.
9. No menu Debug, clique em Start Without Debugging. Confirme se o programa exibe no console
as mensagens que mostram o valor 591 nas diferentes bases numricas, assim:
SB C:\Windows\system32\cmd.exe
5 9 1 in b a s e 2 i s
5 9 1 in b a s e 3 i s
5 9 1 in b a s e 4 i s
5 9 1 in b a s e 5 i s
5 9 1 in b a s e 6 i s
5 9 1 in b a s e 7 i s
5 9 1 in b a s e 8 i s
5 9 1 in b a s e 9 i s
5 9 1 in b a s e 1 0 i s
P r e s s an y k e y t o

1001001111
210220
21033
4331
2423
1503
1117
72 6
59 1
c o n t in u e .

10. Pressione Enter para fechar o programa.


Neste captulo, voc viu como utilizar a herana para definir uma hierarquia de classes e agora deve
entender como redefinir mtodos herdados e implementar mtodos virtuais. Tambm viu como adi
cionar um mtodo de extenso a um tipo existente.
Se voc quiser seguir para o prximo captulo agora:
Mantenha o Visual Studio 2010 em execuo e v para o Captulo 13.
Se quiser sair do Visual Studio 2010:
No menu File, clique em Exit. Se vir uma caixa df>diloo Save clique em Ves e salve o projeto

Captulo 12

Trabalhando com herana

283

Referncia rpida do Captulo 12


Para

Faa isto

Criar uma classe derivada a partir de uma classe base

Declare o novo nome da classe seguido por dois-pontos e


pelo nome da classe base. Por exemplo:
class Derived : Base
{
}

Chamar um construtor de classe base como parte do


construtor para uma classe que o herda

Fornea uma lista de parmetros do construtor antes do cor


po do construtor da classe derivada. Por exemplo:
class Derived : Base
{
p u b l ic D e r i v e d ( i n t x)

: Base(x)

{
}
}
Declarar um mtodo virtual

Use a palavra-chave virtual ao declarar o mtodo. Por exem


plo:
class Mammal
{
p u b l ic v i r t u a l void B re ath eO
{
}
}

Implementar um mtodo em uma classe derivada que


redefine um mtodo virtual herdado

Utilize a palavra-chave override ao declarar o mtodo na


classe derivada. Por exemplo:
class Whale : Mammal
{
p u b lic o v e rrid e void Bre ath eO
{
}
}

Definir um mtodo de extenso para um tipo

Adicione um mtodo pblico esttico a uma classe esttica.


0 primeiro parmetro deve ser do tipo estendido, precedido
pela palavra-chave this. Por exemplo:
s t a t i c cla ss U t i l

{
p u b lic s t a t i c i n t N e g ate(th is i n t i )

{
re turn - i ;

}
}

Captulo 13

Criando interfaces e definindo


classes abstratas
Neste captulo, voc vai aprender a:
D efinir um a in terface, e sp ecifica n d o as assinatu ras e os tip os d e retorn o de m todo s.
Im p le m e n tar u m a in terface, em u m a estru tu ra ou classe.
C ap tu rar d e ta lh e s de im p le m e n ta o co m u n s em u m a classe ab strata.
Fazer referncia a u m a classe p or m eio d e u m a interface.
Im p le m e n tar classes sealed q u e n o p o d em ser utilizadas para d erivar n ovas classes.

A herana de uma classe um mecanismo poderoso, mas o verdadeiro poder da herana vem da
herana de uma interface. Uma interface no contm qualquer cdigo ou dado; ela simplesmente es
pecifica os mtodos e as propriedades que uma classe que herda dela deve fornecer. Utilizando uma
interface voc pode separar completamente os nomes e as assinaturas dos mtodos de uma classe de
um lado, e, de outro, a implementao do mtodo.
Classes abstratas so bem semelhantes a interfaces, exceto por poderem conter cdigo e dados. Mas
possvel especificar que certos mtodos de uma classe abstrata sejam virtuais de tal modo que uma
classe que herde da classe abstrata disponha de sua prpria implementao desses mtodos. Voc
utiliza frequentemente classes abstratas com interfaces, e elas fornecem conjuntamente uma tcnica
fundamental que permite construir estruturas de programao extensveis, como voc descobrir
neste captulo.

Entendendo interfaces
Suponha que voc queira definir uma nova classe de coleo que permita a um aplicativo armazenar
objetos em uma sequncia que depende dos tipos dos objetos que a coleo contm. Por exemplo, se
a coleo armazena objetos alfanumricos como strings, ela deve ordenar os objetos de acordo com a
sequncia de intercalao (collation) do computador; se a coleo armazena objetos numricos como
inteiros, ela deve orden-los numericamente.
Ao definir a classe de coleo, voc no quer restringir os tipos de objetos que ela pode armazenar
(os objetos podem ser at mesmo tipos de classe ou estruturas) e, consequentemente, no sabe como
ordenar esses objetos. A pergunta , ao escrever a classe de coleo, como fornecer um mtodo nessa

286

Parte II

Entendendo a linguagem C#

classe que ordene os objetos cujos tipos voc no conhece? primeira vista, esse problema parece
semelhante ao problema ToStrng descrito no Captulo 12, Trabalhando com herana, que poderia
ser resolvido declarando um mtodo virtual que as subclasses da sua classe de coleo podem redefi
nir. Mas esse no o caso. Normalmente no h qualquer forma de relacionamento de herana entre
a classe de coleo e os objetos que armazena, portanto, um mtodo virtual no seria muito til.
Se pensar um pouco, o problema que a maneira como os objetos na coleo devem ser ordenados
depende do tipo dos prprios objetos, no da coleo. A soluo, ento, exigir que todos os objetos
forneam um mtodo, como o mtodo CompareTo aqui mostrado, que a coleo pode chamar, permi
tindo que a coleo compare esses objetos entre si.
i n t CompareTo(object o b j)

{
// re torn a 0 se essa i n s t n c ia f o r igual a obj
// retorna < 0 se essa i n s t n c ia f o r menor que obj
// retorna > 0 se essa i n s t n c ia f o r maior que obj

}
A classe de coleo pode utilizar esse mtodo para ordenar os objetos nela contidos.
Voc pode definir uma interface para objetos colecionveis que inclua o mtodo CompareTo e especifi
car que a classe de coleo s pode coletar as classes que implementam essa interface. Uma interface
, assim, semelhante a um contrato. Se uma classe implementar uma interface, esta garante que a
classe conter todos os mtodos especificados na interface. Esse mecanismo assegura que voc ser
capaz de chamar o mtodo CompareTo em todos os objetos da coleo e orden-los.
As interfaces permitem que voc realmente separe o o que do como, informando apenas quais
so o nome, o tipo de retorno e os parmetros do mtodo. A forma como o mtodo implementado
no uma preocupao da interface. A interface descreve a funcionalidade que uma classe deve
implementar, mas no como essa funcionalidade implementada.

Definindo uma interface


Para definir uma interface, voc utiliza a palavra-chave interface em vez da palavra-chave class ou
struct. Dentro da interface, voc declara os mtodos exatamente como em uma classe ou em uma es
trutura, exceto pelo fato de nunca especificar um modificador de acesso (public,private ou protected)
e substituir o corpo do mtodo por um ponto e vrgula. Eis um exemplo:
i n t e r f a c e IComparable

{
i n t CompareToCobject o b j ) ;

Captulo 13

Dica

Criando interfaces e definindo classes abstratas

287

A documentao do Microsoft .NET Framework recomenda que voc comece o nome das

suas interfaces com a letra I maiscula. Essa conveno o ltimo vestgio da notao hngara no
C#. Casualmente, o namespace

System define a interface IComparable como mostrado anterior

mente.

Implementando uma interface


Para implementar uma interface, voc declara uma classe ou estrutura que herda da interface e im
plementa todos os mtodos especificados por ela. Por exemplo, suponha que voc esteja definindo a
hierarquia Mammal descrita no Captulo 12 e precise especificar que mamferos terrestres fornecem
um mtodo chamado NumberOfLegs que retorna como um int o nmero de patas que um mamfero
tem (mamferos marinhos no implementam essa interface). Voc poderia definir a interface dos
mamferos terrestres ILandBound que contm esse mtodo assim:
i n te rf a c e ILandBound

{
i n t NumberOfLegsO;

}
Voc pode ento implementar essa interface na classe Horse. Voc herda da interface e fornece uma
implementao de cada mtodo definido pela interface.
class Horse : ILandBound

{
p u blic i n t NumberOfLegsO

{
return 4;

}
}
Ao implementar uma interface, voc deve garantir que cada mtodo corresponda exatamente ao
mtodo da interface correspondente, de acordo com as regras a seguir:
O nome e o tipo de retorno dos mtodos devem se corresponder exatamente.
Qualquer parmetro (incluindo os modificadores de palavra-chave r e ft out) devem se corres
ponder exatamente.
O nome do mtodo se inicia com o nome da interface. Isso conhecido como implementao
explcita de interface e um bom hbito a cultivar.
Todos os mtodos que implementam uma interface devem ser publicamente acessveis. Mas
se voc estiver utilizando a implementao explcita de interface, o mtodo no deve ter um
qualificador de acesso.
Se houver alguma diferena entre a definio da interface e sua implementao declarada, a classe
no compilar.

288

Parte II

Entendendo a linguagem C#

Uma classe pode estender outra classe e implementar uma interface ao mesmo tempo. Nesse caso, o
C# no indica a classe base e a interface utilizando palavras-chave especficas, como o Java faz. Em
vez disso, o C# emprega uma notao posicionai. A classe base nomeada primeiramente, seguida
por uma vrgula, seguida pela interface. O seguinte exemplo define Horse como uma classe que um
Mammal mas que tambm implementa a interface ILandBound:
i n te r f a c e ILandBound

class Mammal

class Horse : Mammal , ILandBound

Referenciando uma classe por meio de sua interface


Da mesma maneira que voc pode referenciar um objeto utilizando uma varivel definida como uma
classe mais elevada na hierarquia, voc pode referenciar um objeto utilizando uma varivel definida
como uma interface que sua classe implementa. Considerando o exemplo anterior, voc pode referen
ciar um objeto Horse utilizando uma varivel ILandBound, como mostrado a seguir:
Horse myHorse = new H o r s e ( . . . ) ;
ILandBound iMyHorse = myHorse; // v li d o

Isso funciona porque todos os cavalos so mamferos terrestres (/and bound), embora o contrrio
no seja verdadeiro, no sendo possvel atribuir um objeto ILandBound a uma varivel Horse sem
antes fazer um casting nela, para verificar se realmente ela faz referncia a um objeto Horse e no a
alguma outra classe que implementa a interface ILandBound.
A tcnica de referenciar um objeto por meio de uma interface til porque permite definir mtodos
que podem receber diferentes tipos como parmetros, contanto que os tipos implementem uma in
terface especificada. Por exemplo, o mtodo FindLandSpeed mostrado abaixo pode receber qualquer
parmetro que implemente a interface ILandBound:
i n t FindLandSpeed(ILandBound landBoundMammal)

{
}
Observe que, ao referenciar um objeto por meio de uma interface, voc s pode invocar os mtodos
que so visveis pela interface.

Captulo 13

Criando interfaces e definindo classes abstratas

289

Trabalhando com mltiplas interfaces


Uma classe pode ter no mximo uma classe base, mas pode implementar um nmero ilimitado de
interfaces, devendo ainda implementar todos os mtodos que herda de todas as suas interfaces.
Se uma interface, estrutura ou classe herda de mais de uma interface, voc escreve as interfaces
em uma lista separada por vrgulas. Se uma classe tambm tem uma classe base, as interfaces so
listadas aps a classe base. Por exemplo, suponha que voc defina uma outra interface chamada
IGrazable que contm o mtodo ChewGrass para todos os animais de pasto. Voc pode definir a
classe Horse assim:
class Horse : Mammal, ILandBound, IG ra zable

{
}

Implementando explicitamente uma interface


Os exemplos apresentados at agora mostraram classes que implementam implicitamente uma inter
face. Se voc examinar novamente a interface ILandBound e a classe Horse (mostrada a seguir), em
bora a classe Horse implemente a interface ILandBound, perceber que no h nada na implementa
o do mtodo NumberOfLegs na classe Horse que indique que ela faz parte da interface ILandBound.
i n te rf a c e ILandBound

{
i n t NumberOfLegs( ) ;

}
class Horse : ILandBound

{
p u b lic i n t NumberOfLegsO

{
re turn 4;

}
}
Isso no seria problemtico em um cenrio simples, mas vamos supor que a classe Horse implemen
tasse vrias interfaces. Nada existe a impedir que diversas interfaces especifiquem um mtodo com
o mesmo nome, embora possa ter semnticas distintas. Por exemplo, vamos supor que voc quisesse
implementar um sistema de transportes baseado em carroas puxadas a cavalos. Uma jornada longa
poderia ser dividida em vrios estgios ou pernas. Para rastrear por quantas pernas cada cavalo
puxou a carroa, voc poderia definir a seguinte interface:
i n te rf a c e I lo u r n e y

{
i n t NumberOfLegsO;

290

Parte II

Entendendo a linguagem C#

Se voc implementar essa interface na classe Horse, enfrentar um problema interessante:


class Horse : ILandBound, IJourney

{
p u b l ic i n t NumberOfLegsO

{
return 4;

}
}
Este um cdigo vlido, mas o cavalo tem quatro patas ou ele puxou a carroa por quatro pernas
do percurso? Pela perspectiva do C#, a resposta as duas opes! Por padro, o C# no distingue
qual interface o mtodo est implementando, de modo que o mesmo mtodo implementa as duas
interfaces.
Para solucionar esse problema e discernir qual mtodo faz parte de qual implementao de interface,
voc pode implementar as interfaces explicitamente. Para isso, especifique a qual interface um mto
do pertence, quando voc a implementar, como a seguir:
class Horse : ILandBound, I lo u r n e y

{
i n t ILandBound.NumberOfLegsO

{
return 4;

}
i n t Ilourney.NumberOfLeg sO

{
return 3;

}
}
Agora, possvel discernir que o cavalo tem quatro patas e puxou a carroa por trs pernas da jor
nada.
Alm de prefixar o nome do mtodo com o nome da interface, existe outra diferena sutil nessa
sintaxe: os mtodos so marcados como public. No possvel especificar a proteo para os m
todos que fazem parte de uma implementao explcita de interface. Isso leva a outro fenmeno
interessante. Se voc criar a varivel Horse no cdigo, no poder chamar qualquer dos dois mto
dos NumberOfLegs, porque no so visveis. No que tange classe Horse, qual mtodo o seguinte
cdigo chamaria - o da interface ILandBound ou o da interface IJourney!
Horse horse = new H o rs e O ;
i n t legs = h o rse . NumberOfLegsO ;

Captulo 13

Criando interfaces e definindo classes abstratas

291

Como possvel acessar esses mtodos? A resposta fazer referncia ao objeto Horse pela interface
adequada, como a seguir:
Horse horse = new H o rs e O ;
IJo u rne y journeyHorse = horse;
i n t le g s ln lo u rn e y = journeyHorse.NumberOfLegsO ;
ILandBound landBoundHorse = horse;
i n t legsOnHorse = landBoundHorse.NumberOfLegsO ;

recomendvel que voc implemente interfaces explicitamente, sempre que possvel.

Restries das interfaces


importante lembrar que uma interface nunca contm qualquer implementao. As restries a
seguir so consequncias naturais disso:
Voc no tem permisso para definir campos em uma interface, nem mesmo campos estticos.
Um campo um detalhe de implementao de uma classe ou estrutura.
Voc no tem permisso para definir construtores em uma interface. Um construtor tambm
considerado um detalhe de implementao de uma classe ou estrutura.
Voc no tem permisso para definir um destrutor em uma interface. Um destrutor contm as
instrues utilizadas para destruir uma instncia de objeto. (Os destrutores esto descritos no
Captulo 14, Utilizando a coleta de lixo e o gerenciamento de recursos.)
Voc no pode especificar um modificador de acesso para qualquer mtodo. Todos os mtodos
de uma interface so implicitamente pblicos.
Voc no pode aninhar tipo algum (como enumeraes, estruturas, classes ou interfaces) dentro
de uma interface.
Uma interface no pode ser herdada de uma estrutura nem de uma classe, embora uma inter
face possa herdar de outra interface. As estruturas e classes contm implementaes; se uma
interface tivesse permisso para herdar de qualquer uma das duas, ela estaria herdando alguma
implementao.

Definindo e utilizando interfaces


Nos exerccios a seguir, voc definir e implementar interfaces que fazem parte de um simples paco
te de desenho grfico. Voc definir duas interfaces chamadas IDraw e IColor, e tambm definir as
classes que as implementam. Cada classe determinar uma forma que pode ser desenhada sobre um
canvas, em um formulrio do Windows Presentation Foundation (WPF). (Um canvas um controle
WPF que permite desenhar linhas, texto e formas.)

292

Parte II

Entendendo a linguagem C#

A interface IDraw define os seguintes mtodos:


S etL ocation Esse mtodos permite especificar a posio como coordenadas X e Y da forma
sobre o canvas.
D raw

Esse mtodo desenha a forma sobre o canvas, no local especificado pelo mtodo SetLo

cation.
A interface IColor define o seguinte mtodo:
S etC olor Esse mtodo permite especificar a cor da forma. Quando desenhada sobre o canvas,
a forma ser exibida com essa cor.
Defina as interfaces IDraw e IColor:
1. Inicialize o Microsoft Visual Studio 2010, se ele ainda no estiver em execuo.
2. Abra o projeto Drawing, localizado na pasta \Microsoft PressWisual CSharp Step By Step\Chapter 13\Drawing de sua pasta Documentos.
O projeto Drawing um aplicativo WPF, e contm um formulrio WPF chamado DrawingPad.
Esse formulrio dispe de um controle canvas, chamado drawingCanvas. Voc utilizar esse
formulrio e o canvas para testar seu cdigo.
3. No menu Project, clique em Add New Item.
exibida a caixa de dilogo Add New Item - Drawing.
4. No painel esquerdo da caixa de dilogo Add New Item - Drawing, clique em Visual C#. Se voc
estiver utilizando o Visual Studio 2010 Professional ou o Visual Studio 2010 Standard, clique
em Code. (O Visual C# 2010 Express tem menos templates e no os divide em grupos, como
acontece no Visual Studio.) No painel central, clique no template Interface. Na caixa de texto
Name, digite IDraw.cs e clique em Add.
O Visual Studio cria o arquivo IDraw.cs e o adiciona a seu projeto. O arquivo IDraw.cs aparece
na janela Code and Text Editor, e parecido com o seguinte:
using
using
using
using

System;
System. C o l1e c t i o n s . G e n e r ic ;
System.Linq;
System.Te x t ;

namespace Drawing

{
i n t e r f a c e IDraw

Captulo 13

Criando interfaces e definindo classes abstratas

293

5. No arquivo IDraw.cs, adicione a seguinte instruo using lista localizada no incio do arquivo:
using System.Windows. C o n t r o ls ;

Voc far uma referncia classe Canvas nessa interface. A classe Canvas est localizada no
namespace System. Windows.Controls.
6. Adicione os mtodos mostrados aqui em negrito interface IDraw-.
i n te r f a c e IDraw

v o i d S e t L o c a t i o n ( i n t xC o o rd ,

in t y C o o rd );

v o i d Dr aw (Ca nvas c a n v a s ) ;

}
7. No menu Project, clique em Add New Item novamente.
8. No painel central da caixa de dilogo Add New Item - Drawing, clique no template Interface. Na
caixa de texto Name, digite IColor.cs e clique zmAdd.
O Visual Studio gera o arquivo IColor.cs e o adiciona a seu projeto. O arquivo IColor.cs aparece
na janela Code and Text Editor.
9. No arquivo IColor.cs, adicione a seguinte instruo using lista localizada no incio do arquivo:
using System.Windows.Medi a;

Voc far uma referncia classe Color nessa interface, localizada no namespace System. Win-

dows.Media.
10. Adicione o seguinte mtodo mostrado em negrito definio da interface IColor.
in te rf a c e IC o lo r

{
void S e tC o lo rfC o lo r c o l o r ) ;

}
Voc acabou de definir as interfaces IDraw e IColor. A prxima etapa criar algumas classes que as
implementam. No exerccio a seguir, voc criar duas novas classes de formas, chamadas Square e
Circle. Essas classes implementaro as duas interfaces.
Crie as classes Square e Circle e im p le m e n te as interfaces
1. No menu Project, clique em Add Class.

2. Na caixa de dilogo Add New Item - Drawing, verifique se o template Class est selecionado no
painel central, digite Square.cs na caixa de texto Name e clique em Add.
O Visual Studio gera o arquivo Square.cs e o exibe na janela Code and Text Editor.

294

Parte II

Entendendo a linguagem C#

3. Adicione as seguintes instrues using lista localizada no incio do arquivo Square.cs:


using
using
using
using

System.Windows;
System.Windows.Medi a;
System.Windows.Shapes;
System.Windows.Controls;

4. Modifique a definio da classe Square de modo que ela implemente as interfaces IDraw e ICo
lor, como mostrado aqui em negrito:
class Square : ID r a w , I C o l o r

{
>
5. Adicione as seguintes variveis privadas, apresentadas em negrito, classe Square. Essas va
riveis armazenaro a posio e o tamanho do objeto Square sobre o canvas. A classe Rectangle
uma classe WPF localizada no namespace System. Windows.Shapes. Voc utilizar essa classe
para desenhar o quadrado:
class Square : IDraw, I C o lo r

{
p r iv a t e i n t sideLength;
p r i v a t e i n t l o c X = 0 , l o c Y = 0;
p r i v a t e R e c ta n g le r e c t = n u l l ;

}
6. Adicione o construtor, mostrado em negrito, classe Square. Esse construtor inicializa o campo
sideLength e especifica o comprimento de cada lado do quadrado.
class Square : IDraw, I C o lo r

{
p u b lic S q u a re (in t sideLength)

{
t h i s . sideLength = sideLen gth;

}
}
7. Na definio da classe Square, clique com o boto direito do mouse na interface IDraw. exibi
do um menu de atalho. Nele, aponte para Implement Interface e clique em Implement Interface
Explicitly, como ilustra a imagem a seguir:

Captulo 13

'

l*1

Criando interfaces e definindo classes abstratas

Debug

295

J| showFloatVal'

Esse recurso instrui o Visual Studio a gerar implementaes padro dos mtodos na interface

IDraw. Se preferir, voc tambm pode adicionar manualmente os mtodos classe Square. O
exemplo a seguir mostra o cdigo gerado pelo Visual Studio:
void ID ra w . S e t L o c a t i o n ( i n t xCoord, i n t yCoord)

{
throw new Notlm plementedExcep tionO;

}
void IDraw.DrawCCanvas canvas)

{
throw new Notlm plementedExcep tionO;

}
Cada um desses mtodos lana atualmente uma exceo NotlmplementedException. Espera-se
que voc substitua o corpo desses mtodos pelo seu cdigo.
8. No mtodo SetLocation, substitua o cdigo existente pelas instrues mostradas em negrito.
Esse cdigo armazena os valores passados pelos parmetros nos campos locX e locY, no objeto

Square.
void ID ra w . S e t L o c a t i o n ( i n t xCoord, i n t yCoord)

{
this.locX = xCoord;
this.locY = yCoord;
}

296

Parte II

Entendendo a linguagem C#

9. Substitua o cdigo existente no mtodo Draw pelas instrues mostradas aqui em negrito:
vo id ID ra w .Draw(Canvas canvas)

{
if

(th is .re c t

!= n u l l )

{
ca n va s .C h ild re n .R e m o v e (th is .re c t);

}
else

{
t h i s . r e c t = new R e c t a n g l e Q ;

this.rect.Height = this.sideLength;
this.rect.Width = this.sideLength;
canvas.SetTop(this.rect, this.locY);
canvas.SetLeft(this.rect, this.locX);
canvas.Children.Add(rect);

>
Esse mtodo processa o objeto Square, ao desenhar uma forma Rectangle no canvas. (Um qua
drado to somente um retngulo de quatro lados com o mesmo tamanho.) Se o Rectanglej
foi desenhado (possivelmente em outro local e com outra cor), ele ser removido do canvas. A
altura e largura de Rectangle so definidas pelo valor do campo sideLength. A posio do Rec
tangle no canvas definida por meio dos mtodos estticos, SetTop e SetLeft, da classe Canvas
e, em seguida, o Rectangle adicionado ao canvas. (Isso causa a sua exibio.)
10. Adicione o mtodo SetColor da interface IColor classe Square, como mostrado a seguir:
void IC o lo r.S e tC o lo rC C o lo r c o lo r)

{
if

(r e c t != n u ll)

{
SolidC olorBru sh brush = new S o lid C o lo rB ru s h (c o lo r);
r e c t . F i l l = brush;

>
}
Esse mtodo verifica se o objeto Square foi realmente exibido. (0 campo rect ser null (nulo) se
ele ainda no tiver sido processado.) O cdigo define a propriedade Fill do campo rect com a cor
especificada, atravs do objeto SolidColorBrush. (Os detalhes do objeto SolidBrushClass esto
alm do escopo desta discusso.)
11. No menu Project, clique em Add Class. Na caixa de dilogo Add New Item - Drawing, digite
Circle.cs na caixa de texto Name e clique em Add.
O Visual Studio gera o arquivo Circle.cs e o exibe na janela Code and Text Editor.

Captulo 13

Criando interfaces e definindo classes abstratas

297

12. Adicione as seguintes instrues using lista localizada no incio do arquivo Circle.cs:
using
using
using
using

System.Windows;
System.Windows.Medi a;
System.Windows.Shapes;
System.Windows.Controls;

13. Modifique a definio da classe Circle de modo que ela implemente as interfaces IDraw e IColor,
como mostrado aqui em negrito:
class C i r c l e : IDraw, I C o l o r

{
}
14. Adicione as seguintes variveis privadas, mostradas em negrito, classe Circle. Essas variveis
armazenaro a posio e o tamanho do objeto Circle sobre o canvas. A classe Ellipse outra
classe WPF que voc utilizar para desenhar o crculo.
class C i r c l e : IDraw, I C o lo r

{
p r i v a t e i n t ra d iu s ;
p r i v a t e i n t locX = 0, loc Y = 0;
private E llip s e c i r c l e = n u ll;

}
15. Adicione o construtor, mostrado em negrito, classe Circle. Esse construtor inicializa o campo
radius (raio).
cla ss C i r c l e : IDraw, I C o lo r

{
p u b l ic C i r c l e ( i n t r a d iu s )

{
t h i s . r a d i u s = rad iu s;

}
16. Adicione o mtodo SetLocation, mostrado a seguir, classe Circle. Esse mtodo implementa
parte da interface IDraw, e o cdigo exatamente o mesmo da classe Square.
void ID ra w . S e t L o c a t i o n ( i n t xCoord, i n t yCoord)

{
t h i s . l o c X = xCoord;
t h i s . l o c Y = yCoord;

298

Parte II

Entendendo a linguagem C#

17. Adicione o mtodo Draw, mostrado a seguir, classe Circle. Esse mtodo tambm faz parte da
interface IDraw.
void IDraw.Draw(Canvas canvas)

{
i f (th is .c irc le

1= n u l l )

{
canvas.Children.Remove(thi s . c i r c l e ) ;

}
else

{
t h i s . c i r c l e = new E l l i p s e O ;

}
t h i s . c i r c l e . Height = t h i s . r a d i u s ;
th is .c irc le .W id th = th is .ra d iu s ;
canvas. S e tT o p (th i s . ci r c l e , t h i s .1 o c Y ) ;
canvas. S e t L e f t ( t h i s . c i r c l e , t h i s . l o c X ) ;
canvas. Chi 1d r e n . Ad d(ci r c l e ) ;

}
Esse mtodo semelhante ao mtodo Draw na classe Square, exceto pelo fato de que ele proces
sa o objeto Circle ao desenhar uma forma Ellipse sobre o canvas. (Um crculo uma elipse em
que a largura e a altura so idnticas.)
18. Adicione o mtodo SetColor classe Circle. Esse mtodo faz parte da interface IColor. Como an
teriormente, esse mtodo parecido com o da classe Square.
void I C o l o r . S e t C o l o r ( C o l o r c o l o r )

{
i f (c irc le

! - null)

Soli dColorB rush brush = new S o l i d C o l o r B r u s h ( c o l o r ) ;


c i r c l e . F i l l = brush;

}
}
Voc concluiu as classes Square e Circle e j pode utilizar o formulrio WPF para test-las.
Teste as classes Square e Circle:
1. Exiba o arquivo DrawingPad.xaml na janela Design View.
2. Clique na rea sombreada existente no meio do formulrio WPF.
A rea sombreada do formulrio o objeto Canvas, e esta ao define o foco neste objeto.
3. Na janela Properties, clique no boto Events. (Esse boto possui um cone parecido com um
relmpago.)
4. Na lista de eventos, localize o evento MouseLeftButtonDown e clique duas vezes nesse evento.

Captulo 13

Criando interfaces e definindo classes abstratas

299

0 Visual Studio gera um mtodo chamado drawingCanvasJ/louseLeftButtonDown para a classe


DrawingPadWindow, que implementa o formulrio WPF e o exibe na janela Code and Text Edi
tor. Esse um manipulador de eventos executado quando o usurio clica no boto esquerdo do
mouse sobre o canvas. (Voc conhecer mais detalhes sobre os manipuladoras de eventos no
Captulo 17, Interrompendo o fluxo do programa e tratando eventos.)
5. Adicione o cdigo, mostrado em negrito, ao mtodo drawingCanvas_MouseLeftButtonDown-.
p r i v a t e void drawingCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

{
Point mouseLocation = e . G e t P o s i t i o n ( t h is .d r a w in g C a n v a s );
Square mySquare = new S q u a re (lO O );
i f (mySquare i s IDraw)

{
IDraw drawSquare = mySquare;
draw Squ are .S etL ocation((in t)m ou s eLoc a tio n .X , (in t)m o u s e L o c a tio n . Y ) ;
drawSquare. Draw(drawi ngCanvas);

}
}
O parmetro de MouseButtonEventArgs, e, para esse mtodo fornece informaes teis sobre
a posio do mouse. Mais especificamente, o mtodo GetPosition retorna uma estrutura Point
que contm as coordenadas X e Y do mouse. O cdigo que voc adicionou gera um novo objeto
Square. Em seguida, ele verifica se esse objeto implementa a interface IDraw (o que uma pr
tica recomendada) e gera uma referncia ao objeto, pela interface IDraw. Convm lembrar que,
quando voc implementa explicitamente uma interface, os mtodos definidos por essa interface
s estaro disponveis ao criar uma referncia a essa interface. (Os mtodos SetLocation tD raw
so privados para a classe Square e esto disponveis apenas pela interface IDraw.) Em seguida,
o cdigo define a localizao do Square com a posio do mouse. Observe que as coordenadas
X e Y na estrutura Point so valores double, de modo que esse cdigo os converte em ints. Em
seguida, o cdigo chama o mtodo Draw para exibir o objeto Square.
6. Adicione o seguinte cdigo, mostrado em negrito, ao final do mtodo drawingCanvas_MouseLeft-

ButtonDown-,
p ri v a te void drawingCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

i f (mySquare i s I C o lo r )

{
IC o lo r colorSquare = mySquare;
co lo rS q u a re . S e tC o lo r(C o lo rs . B Iu e V io le t );

}
}
Esse cdigo testa a classe Square para verificar se ela implementa a interface IColor; em caso
afirmativo, ele gera uma referncia classe Square por meio dessa interface e chama o mtodo
SetColor para definir a cor do objeto Square com Colors.BlueViolet. (A enumerao Colors for
necida como parte do .NET Framework.)

300

Parte II

Entendendo a linguagem C#

Im p o r t a n t e

Voc deve chamar Draw antes de chamar SetColor. Isso deve ser feito dessa

maneira porque o mtodo SetColor s define a cor do objeto Square se ele j tiver sido dese
nhado. Se voc chamar SetColor antes de Draw, a cor no ser definida e o objeto Square no
ser exibido.

7. Retorne ao arquivo DrawingPad.xaml na janela Design View e clique no objeto Canvas no meio
do formulrio. Na lista de eventos na janela Properties, clique duas vezes no eventoMouseRight-

ButtonDown.
0 Visual Studio gera outro mtodo, chamado drawingCanvas_MouseRightButtonDown, execu
tando-o quando o usurio clica com o boto direito do mouse sobre o canvas.
8. Adicione o cdigo mostrado em negrito a seguir ao mtodo drawingCanvas_MouseRightButtonDown. A lgica nesse cdigo semelhante ao do mtodo que manipula o boto esquerdo do
mouse, exceto pelo fato de que ele exibe um objeto Circle em HotPink.
p ri v a te void drawingCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)

{
Point mouseLocation = e . G e t P o s i t i o n ( t h i s . dr awingCanvas);
C i r c l e m yCircle = new C i r c l e ( l O O ) ;
i f (m y C ir c le i s IDraw)

{
IDraw d r aw C ir cle = m yC irc le ;
d r a w C ir c le .S e tL o c a tio n (( i n t) m o u s e L o c a ti o n .X , (in t )m o u s e L o c a t i o n . Y );
drawCi r c l e . Draw(drawi ng Canvas);

}
i f (m y C ir c le i s I C o l o r )

{
I C o l o r c o l o r C i r c l e = m yC irc le ;
co lo rC i r c l e . S e t C o l o r ( C o l o r s . HotPi n k ) ;

}
}
9. No menu Debug, clique em Start Without Debugging para construir e executar o aplicativo.
10. Quando a janela DrawingPad for exibida, clique com o boto esquerdo do mouse em qualquer
lugar nessa janela. Deve ser exibido um quadrado violeta.
11. Clique com o boto direito do mouse em qualquer lugar na janela. Deve ser exibido um crculo
rosa-shocking. Voc pode clicar os botes direito e esquerdo do mouse quantas vezes desejar, e
cada clique desenhar um quadrado ou um crculo na posio do mouse, como mostra a ima
gem a seguir:

Captulo 13

Criando interfaces e definindo classes abstratas

301

12. Feche a janela e retorne ao Visual Studio.

Classes abstratas
Voc pode implementar as interfaces ILandBound e IGrazable, discutidas na sego anterior, em vrias
classes diferentes, dependendo de quantos tipos de mamferos voc deseja modelar em seu aplicativo
C#. Em situaes dessa natureza, muito comum que as partes das classes derivadas compartilhem
implementaes em comum. Por exemplo, a duplicao nas duas classes seguintes bvia:
class Horse : Mammal, ILandBound, IG ra zable

{
void IGrazable.ChewGrass O

{
C onsole .W ri te LineC C hewin g g r a s s " ) ;
// cdigo para pastar

};

class Sheep : Mammal, ILandBound, IG ra zable

{
vo id IGrazable.ChewGrass O

{
Console .W ri te LineCChewin g g r a s s " ) ;
// o mesmo cdigo de horse para pastar

>;

302

Parte II

Entendendo a linguagem C#

A duplicao no cdigo um sinal de aviso. Se possvel, voc deve refatorar o cdigo para evitar essa
duplicao e reduzir custos de manuteno. Para refatorar, coloque a implementao comum em
uma nova classe criada especificamente para essa finalidade. Na realidade, voc pode inserir uma
nova classe na hierarquia de classes. Por exemplo:
class CrazingMammal : Mammal, IG ra zable

{
void IGrazable.ChewGrass O

{
C o n s o le .W ri te L i n e (Chewing g r a s s " ) ;
// cdigo comum para pastar

cla ss Horse : GrazingMammal, ILandBound

cla ss Sheep : GrazingMammal, ILandBound

Essa uma boa soluo, mas h algo que ainda no est muito certo: voc pode criar instncias
da classe GrazingMammal (e tambm da classe Mammal). Isso realmente no faz sentido. A classe
GrazingMammal existe para fornecer uma implementao padro comum e seu nico objetivo
ser herdada. Essa classe uma abstrao da funcionalidade comum em vez de uma entidade por si
prpria.
Para declarar que a criao de instncias de uma classe no permitida, voc deve explicitar que a
classe abstrata, utilizando a palavra-chave abstract. Por exemplo:
ab s tra ct class GrazingMammal : Mammal, IG ra zable

{
}
Se tentar instanciar um objeto GrazingMammal, o cdigo no ir compilar:
GrazingMammal myGrazingMammal = new GrazingMammal( . . . ) ;

// i n v li d o

Mtodos abstratos
Uma classe abstrata pode conter mtodos abstratos. Um mtodo abstrato semelhante em princpio
a um mtodo virtual (discutimos mtodos virtuais no Captulo 12), exceto por ele no conter um

Capitulo 13

Criando interfaces e definindo classes abstratas

303

corpo de mtodo. Uma classe derivada precisa redefinir esse mtodo. O exemplo a seguir define o
mtodo DigestGrass na classe GrazingMammal como um mtodo abstrato; mamferos de pasto po
deriam utilizar o mesmo cdigo para pastar, mas eles devem fornecer uma implementao prpria
do mtodo DigestGrass. Um mtodo abstrato til se no fizer sentido fornecer uma implementao
padro na classe abstrata e se voc quiser assegurar que uma classe que herda fornea uma imple
mentao prpria desse mtodo.
abs tra ct cla ss GrazingMammal : Mammal, IG razable

{
abs tract void D i g e s t G r a s s O ;

Classes seladas
Utilizar herana nem sempre fcil e exige prudncia. Se criar uma interface ou uma classe abstrata,
voc estar intencionalmente escrevendo algo que ser herdado no futuro. O problema que prever
o futuro difcil. Com prtica e experincia, voc pode desenvolver habilidades para produzir uma
hierarquia fcil de usar e flexvel em termos de interfaces, classes abstratas e classes, mas isso exige
esforo e tambm necessrio um entendimento slido do problema que est modelando. Ou seja,
a menos que voc projete conscientemente uma classe com a inteno de utiliz-la como uma classe
base, muito pouco provvel que ela funcione to bem quanto uma classe base. O C# permite que
voc utilize a palavra-chave sealed para impedir que uma classe seja utilizada como uma classe base
se quiser. Por exemplo:
sealed cla ss Horse : GrazingMammal, ILandBound

{
}
Se alguma classe tentar utilizar Horse como sua classe base, um erro de tempo de compilao ser
gerado. Observe que uma classe selada no pode declarar mtodo virtual algum e que uma classe
abstrata no pode ser selada.

Mtodos selados
Voc tambm pode utilizar a palavra-chave sealed para declarar que um mtodo individual em uma
classe no selada est selado. Isso significa que uma classe derivada no poder sobrescrever pos

304

Parte II

Entendendo a linguagem C#

teriormente o mtodo selado. Voc pode selar apenas um mtodo override e declarar o mtodo como
sealed override, ou seja, no pode selar um mtodo que esteja implementando diretamente um mto
do em uma interface. (No possvel sobrescrever um mtodo herdado diretamente de uma interface,
s de uma classe.) Considere as palavras-chave interface, virtual, override e sealed, como descrito a
seguir:
Uma interface introduz o nome de um mtodo.
Um mtodo virtual a primeira implementao de um mtodo.
Um mtodo override outra implementao de um mtodo.
Um mtodo sealed a ltima implementao de um mtodo.

Implementando e utilizando uma classe abstrata


Os exerccios a seguir utilizam uma classe abstrata para racionalizar uma parte do cdigo que voc
desenvolveu no exerccio anterior. As classes Square e Circle contm uma alta proporo de cdigo
duplicado. Compensa fatorar esse cdigo em uma classe abstrata, chamada DrawingShape, porque
esta ao facilitar a manuteno das classes Square e Circle mais adiante.
Crie a classe abstrata DrawingShape
1. Retorne ao projeto Drawing no Visual Studio.

2. No menu Project, clique em Add Class.


exibida a caixa de dilogo Add New Item - Drawing.
3. Na caixa de texto Name, digite DrawingShape.es e clique em Add.
O Visual Studio gera o arquivo e o exibe na janela Code and Text Editor.
4. No arquivo DrawingShape.es, adicione as seguintes instrues using lista localizada no incio:
using
using
using
using

System.Windows;
System.Windows.Medi a;
System.Windows. Shapes;
System.Windows.Controls;

5. O objetivo dessa classe conter o cdigo comum s classes Circle e Square. Um programa no
poder instanciar diretamente um objeto DrawingShape. Modifique a definio da classe Dra
wingShape e declare-a como abstract, como mostrado aqui em negrito:

Capitulo 13

Criando interfaces e definindo classes abstratas

305

abstract c la s s DrawingShape

{
}
6. Adicione as variveis privadas, mostradas em negrito, classe DrawingShape-.
a b s tra ct c la s s DrawingShape

{
protected i n t size;
protected i n t locX = 0, locY = 0;
protected Shape shape = n u l l ;

}
As classes Square e Circle utilizam os campos locX e locY para especificar a localizao do objeto
sobre canvas, para que voc possa mover esses campos para a classe abstrata. De modo seme
lhante, as classes Square e Circle usavam um campo para indicar o tamanho do objeto quando
ele era processado; embora ele tenha outro nome em cada classe (sideLength e radius), seman
ticamente o campo executava a mesma tarefa nas duas classes. O nome size uma abstrao
eficiente do objetivo desse campo.
Internamente, a classe Square usa um objeto Rectangle para se desenhar sobre o canvas, e
a classe Circle utiliza um objeto Ellip se. Essas duas classes fazem parte de uma hierarquia
baseada na classe abstrata Shape do .NET Framework. A classe DrawingShape emprega um
campo Shape para representar esses dois tipos.
7. Adicione o seguinte construtor classe DrawingShape:
p u b lic DrawingShape(int s iz e )

{
t h is .s iz e = s iz e ;

}
Esse cdigo inicializa o campo size do objeto DrawingShape.
8. Adicione os mtodos SetLocation e SetColor classe DrawingShape, como mostrado em negrito.
Esses mtodos fornecem as implementaes herdadas por todas as classes derivadas da classe
DrawingShape. Observe que eles no esto marcados como virtu al, e no se espera que uma
classe derivada os substitua. Alm disso, a classe DrawingShape no est declarada como se
implementasse as interfaces ID raw ou IColor (implementao de interfaces um recurso das
classes Square e Circle e no dessa classe abstrata), de modo que esses mtodos so apenas
declarados como public.
a b s tra ct c la s s DrawingShape

{
public void SetLocation(int xCoord, i n t yCoord)

{
t h is.lo cX = xCoord;
t h is .lo c Y = yCoord;
}

306

Parte II

Entendendo a linguagem C#

public void SetColorCColor colo r)

{
i f (shape != n u ll)

{
SolidColorBrush brush = new SolidColo rBrush(colo r);
shape.Fill = brush;

}
}
}
9. Adicione o mtodo Draw classe DrawingShape. Diferentemente dos mtodos anteriores, esse
mtodo declarado como virtual, e espera-se que as classes derivadas o sobrescrevam para es
tender a funcionalidade. O cdigo contido nesse mtodo verifica se o campo shape no nulo,
e depois o desenha no canvas. As classes que herdam esse mtodo devem fornecer um cdigo
prprio para instanciar o objeto shape. (Lembre-se de que a classe Square cria um objeto Rectan
gle, e a classe Circle gera um objeto Ellip se.)
a b s tra c t c la s s DrawingShape

public v irtu a l void Draw(Canvas canvas)


i f (this.shape == n u ll)

{
throw new ApplicationException(Shape is n u l l ) ;

}
this.shape.Height = t h i s . s i z e ;
this.shape.Width = t h i s . s i z e ;
Canvas.SetTop(this.shape, t h i s . l o c Y ) ;
Canvas.SetLeft(this.shape, t h i s . l o c X ) ;
canvas.Children.Add(shape);

}
}
Voc acabou de finalizar a classe abstrata DrawingShape. A prxima etapa mudar as classes Square
e Circle para que elas herdem dessa classe, e remover o cdigo duplicado das classes Square e Circle.

M odifique as classes

Square e Circle para herdar da classe DrawingShape

1. Exiba o cdigo da classe Square na janela Code and Text Editor. Modifique a definio da classe
Square para que ela herde da classe DrawingShape, alm de implementar as interfaces ID raw e
IColor.
c la s s Square : DrawingShape, IDraw, IC o lo r

Observe que voc deve especificar a classe da qual a classe Square herda antes de quaisquer
interfaces.
2. Na classe Square, remova as definies dos campos sideLength, rect, locX e locY.
3. Substitua o construtor existente pelo seguinte cdigo, que chama o construtor da classe base.
Perceba que o corpo desse construtor est vazio porque o construtor da classe base se encarrega
de toda a inicializao necessria.

Captulo 13

Criando interfaces e definindo classes abstratas

307

c la s s Square : DrawingShape, IDraw, IC o lo r

{
public Square(int sideLength) : base(sideLength)

{
}

4. Remova os mtodos SetLocation e SetColor da classe Square. A classe Draw ingShapej fornece
a implementao desses mtodos.
5. Modifique a definio do mtodo Draw. Declare-o como public override, e remova a referncia
interface ID raw . Mais uma vez, a classe DrawingShape j disponibiliza a funcionalidade
bsica para esse mtodo, mas voc a estender com um cdigo especfico, necessrio classe
Square.
public override void Draw(Canvas canvas)

6. Substitua o corpo do mtodo Draw pelo cdigo mostrado em negrito. Essas instrues instanciam
o campo shape herdado da classe DrawingShape, como uma nova instncia da classe Rectangle,
se ela ainda no foi instanciada, e depois chamam o mtodo Draw na classe DrawingShape.
p u b lic o ve rrid e void Draw(Canvas canvas)

{
i f (th is .s h a p e != n u ll)

{
can vas.C h ild re n . Remove(thi s . sh a p e);

}
el se

{
th is.s h a p e = new R e cta n g le O ;

}
b ase.D raw (can vas);

}
7. Repita as etapas 2 a 6 para a classe Circle, exceto pelo fato de que o construtor deve ser cha
mando de Circle com um parmetro chamado radius, e no mtodo Draw voc deve instanciar
o campo shape como um novo objeto Ellipse. O cdigo completo para a classe Circle deve ficar
parecido com o seguinte:
c la s s C ir c le : DrawingShape, IDraw, IC o lo r

{
p u b lic C ir c l e ( i n t ra d iu s) : b ase (ra d iu s )

{
}
p u b lic o ve rrid e void Draw(Canvas canvas)

{
i f (th is .s h a p e != n u ll)

{
can vas.Chi 1dren . Remove(thi s . sh ap e);

308

Parte II

Entendendo a linguagem C#

el se

{
th is.s h a p e = new E l l i p s e O ;

}
base.D raw (can vas);

}
}
8. No menu Debug, clique em Start Without Debugging. Quando a janela Drawing Pad for exibida,
verifique se os objetos Square aparecem quando voc clica com o boto esquerdo do mouse na
janela, e os objetos Circle so exibidos quando voc clica com o boto direito do mouse na janela.
9. Feche a janela Drawing Pad e volte ao Visual Studio.
Neste captulo, vimos como definir e implementar interfaces e classes abstratas. A tabela a seguir
resume as diversas combinaes de palavras-chave vlidas (sim ), invlidas (no) e obrigatrias (exi
gido), ao definir mtodos para interfaces e classes.

Palavra-chave

Interface

Classe abstrata

Classe

Classe selada

Estrutura

abstract

no

sim

no

no

no

new

sim1

sim

sim

sim

sim

override

no

sim

sim

sim

sim

private

no

sim

sim

sim

sim

protected

no

sim

sim

sim

no2

public

no

sim

sim

sim

sim

sealed

no

sim

sim

exigido

no

virtual

no

sim

sim

no

no

'Uma interface pode estender outra interface e introduzir um novo mtodo com a mesma assinatura.
2Uma estrutura implicitamente selada e no pode ser derivada.

Se voc quiser seguir para o prximo captulo agora:


Mantenha o Visual Studio 2010 em execuo e v para o Captulo 14.
Se quiser sair do Visual Studio 2010:
No menu File, clique em Exit. Se vir uma caixa de dilogo Save, clique em Yes e salve o projeto.

Capitulo 13

Criando interfaces e definindo classes abstratas

309

Referncia rpida do Captulo 13


Para

Faa isto

Declarar uma interface

Utilize a palavra-chave interface. Por exemplo:


in terface IDemo
{
strin g NameC);
string D e scrip tio n O ;

}
Implementar uma interface

Declare uma classe utilizando a mesma sintaxe da heran


a de classes e ento implemente todas as funes-membro da interface. Por exemplo:
class Test : IDemo

{
public string IDemo.NameO

{
}
public string IDemo.DescriptionO
{
}
}
Criar uma classe abstrata que s possa ser utilizada como
uma classe base, contendo mtodos abstratos

Declare a classe utilizando a palavra-chave abstract.


Para cada mtodo abstrato, declare o mtodo com a
palavra-chave abstract e sem um corpo de mtodo. Por
exemplo:
abstract class CrazingMammal
{
abstract void DigestCrassC);
}

Criar uma classe selada que no possa ser utilizada como


uma classe base

Declare a classe utilizando a palavra-chave sealed. Por


exemplo:
sealed c la s s Horse

Captulo 14

Utilizando a coleta de lixo e o


gerenciamento de recursos
Neste captulo, voc vai aprender a:
Gerenciar os recursos do sistema utilizando a coleta de lixo.
Escrever cdigo que executa quando um objeto finalizado usando um destrutor.
Liberar um recurso em um determ inado m om ento e de modo seguro quanto a excees
escrevendo uma instruo

try/finally.

Liberar um recurso em um determ inado m om ento e de modo seguro quanto a excees


escrevendo uma instruo

using.

Voc viu nos captulos anteriores como criar variveis e objetos, e agora j deve entender como a me
mria alocada quando variveis e objetos so criados (no caso de voc ter esquecido, os tipos-valor
so criados na pilha, e os tipos-referncia so memria alocada a partir do heap). Os computadores
no tm quantidades infinitas de memria, portanto, ela deve ser recuperada quando uma varivel
ou objeto no precisar mais dela. Os tipos-valor so destrudos e sua memria reivindicada quando
eles saem de escopo. Essa a parte fcil. Mas e os tipos-referncia? Voc cria um objeto utilizando
a palavra-chave new, mas como e quando um objeto destrudo? Esse o assunto deste captulo.

O tempo de vida de um objeto


Em primeiro lugar, vamos recapitular o que acontece quando voc cria um objeto.
Voc cria um objeto utilizando o operador new. O exemplo a seguir cria uma nova instncia da classe
Square, que voc conheceu no Captulo 13, Criando interfaces e definido classes abstratas .
Square mySquare = new Sq ua reO ; // Square um tip o - re fe r n c ia

Do seu ponto de vista, a operao new atmica, mas, por baixo dos panos, a criao do objeto na
verdade um processo de duas fases:
1. A operao new aloca uma parte da memria bruta a partir do heap. Voc no tem controle
algum sobre essa fase da criao de um objeto.
2. A operao new converte a parte da memria bruta em um objeto; ela tem de inicializar o objeto.
Voc pode controlar essa fase utilizando um construtor.

312

Parte II

Entendendo a linguagem C#

Depois de criar um objeto, voc pode acessar seus membros utilizando o operador (.). Por exemplo, a
classe Square inclui um mtodo chamado Draw que voc pode executar:
m ySquare.DrawQ;

Voc pode fazer outras variveis de referncia referenciarem o mesmo objeto:


Square referenceToMySquare = mySquare;

Quantas referncias a um objeto voc pode criar? Quantas voc quiser! Isso tem um impacto sobre
o tempo de vida de um objeto. O runtime tem de manter o controle de todas essas referncias. Se a
varivel Square desaparecer (saindo do escopo), outras variveis (como referenceToMaySquare) ainda
podero existir. O tempo de vida de um objeto no pode ser vinculado a uma determinada varivel
de referncia. Um objeto pode ser destrudo e sua memria s pode ser reivindicada quando todas as
referncias a ele desaparecerem.
Como a criao de um objeto, a destruio do objeto um processo de duas fases, as quais espelham
exatamente as duas fases de criao:
1. O runtime precisa organizar as coisas. Voc pode controlar isso escrevendo um destrutor.
2. O runtime precisa retornar a memria que anteriormente pertencia ao objeto de volta ao heap;
a memria em que o objeto residia precisa ser desalocada. Voc no tem controle algum sobre
essa fase.
O processo de destruio de um objeto e devoluo da memria para o heap conhecido como coleta
de lixo.

Escrevendo destrutores
Voc pode utilizar um destrutor para executar qualquer limpeza necessria quando um objeto vai
para a coleta de lixo. Um destrutor um mtodo especial, parecido com um construtor, exceto pelo
fato de o runtime o chamar depois de a ltima referncia a um objeto desaparecer. A sintaxe para es-

Captulo 14

Utilizando a coleta de lixo e o gerenciamento de recursos

313

crever um destrutor um til (~ ) seguido pelo nome da classe. Como exemplo, eis uma classe simples
que conta o nmero de instncias existentes incrementando uma varivel esttica no construtor e
decrementando a mesma varivel esttica no destrutor:
c la s s T a lly

{
public T a l l y O

{
th i s . i nstanceCount++;

}
- T a lly O

{
th is .in sta n ce C o u n t ;

}
p u b lic s t a t i c in t InstanceC ountO

{
return th is.in sta n ce C o u n t;

}
p riv a te s t a t i c in t instanceCount = 0;

}
H algumas restries importantes que dizem respeito aos destrutores:
Os destrutores s se aplicam a tipos-referncia. Voc no pode declarar um destrutor em um
tipo-valor, como um struct.
s tru c t T a lly

{
-T a lly O {

} // erro de tempo de compilao

}
Voc no pode especificar um modificador de acesso (como public) para um destrutor. Voc nun
ca chama o destrutor no seu prprio cdigo - uma parte do runtime, chamada coletor de lixo,
faz isso para voc.
p u b lic - T a lly O

{ . . . } // erro de tempo de compilao

Um destrutor no pode aceitar quaisquer parmetros. Novamente, isso ocorre porque voc nun
ca chama o destrutor por conta prpria.
- T a lly ( in t parameter) { . . .

} // erro de tempo de compilao

Internamente, o compilador C# converte automaticamente um destrutor em uma redefinio do


mtodo Object.Finalize. O compilador converte este destrutor:
c la s s T a lly

{
- T a lly O

{ // Seu cdigo e n tra aqui }

314

Parte II

Entendendo a linguagem C#

nisto:
c la s s T a lly

{
protected override void F in a liz e O

{
t r y { // Seu cdigo en tra aqui }
f i n a l l y { base. F in a liz e O ; }

}
}
O mtodo Finalize gerado pelo compilador contm o corpo do destrutor dentro de um bloco try,
seguido por um blocofin a lly que chama o mtodo Finalize da classe base (as palavras-chave try e
Jin a lly foram descritas no Captulo 6, Gerenciando erros e excees"). Isso garante que um destrutor
sempre chamar o destrutor da sua classe base, mesmo que ocorra uma exceo durante seu cdigo
de destrutor.
importante perceber que apenas o compilador pode fazer essa converso. Voc no pode escrever
seu prprio mtodo para substituir Finalize, nem pode chamar Finalize por conta prpria.

Por que utilizar o coletor de lixo?


Voc agora deve entender que nunca ser possvel destruir um objeto utilizando cdigo C#. No h
uma sintaxe que faa isso. O tempo de execuo se encarregar disso para voc e existem boas ra
zes para os projetistas do C# terem decidido impedi-lo de fazer isso. Se fosse sua responsabilidade
destruir os objetos, mais cedo ou mais tarde, uma das seguintes situaes poderia ocorrer:
Voc poderia esquecer de destruir o objeto. Isso significa que o destrutor do objeto (se houver)
no seria executado, a limpeza no ocorreria e a memria no seria desalocada de volta para o
heap. Voc poderia esgotar a memria.
Voc poderia destruir um objeto ativo. Lembre que os objetos so acessados por referncia. Se
uma classe mantivesse uma referncia a um objeto destrudo, essa seria uma referncia osci
lante. A referncia oscilante terminaria referenciando uma memria no utilizada ou talvez um
objeto completamente diferente na mesma parte da memria. De uma maneira ou de outra, o re
sultado do uso de uma referncia varivel seria indefinido, ou, pior, seria um risco de segurana.
Voc poderia tentar e destruir o mesmo objeto mais de uma vez. Isso poderia ou no ser desas
troso, dependendo do cdigo do destrutor.
Esses problemas so inaceitveis em uma linguagem como o C#, que coloca a resistncia e a segu
rana no alto de sua lista de objetivos de projeto. Em vez disso, o coletor de lixo responsvel por
destruir os objetos para voc, dando-lhe as seguintes garantias:
Todo objeto ser destrudo, e seus destrutores sero executado. Quando um programa terminar,
todos os objetos existentes sero destrudos.

Captulo 14

Utilizando a coleta de lixo e o gerenciamento de recursos

315

Cada objeto ser destrudo apenas uma vez.


Cada objeto ser destrudo somente quando ele se tornar inacessvel - isto , quando no hou
ver quaisquer referncias ao objeto no processo de execuo de seu aplicativo.
Essas garantias so muito teis e liberam o programador das enfadonhas tarefas de limpeza, que
so passveis de erro. Elas permitem que voc se concentre na lgica do programa e seja mais pro
dutivo.
Ouando ocorre a coleta de lixo? Essa pode parecer uma pergunta estranha. Afinal de contas, a
coleta de lixo ocorre quando um objeto no mais necessrio. isso mesmo, mas no necessa
riamente de imediato. A coleta de lixo pode ser um processo caro, portanto, o runtime s coleta
o lixo quando h necessidade (quando ele verifica que a memria disponvel est diminuindo) e
ento coleta o mximo possvel. melhor executar algumas limpezas grandes do que executar
muitas pequenas!

Outra caracterstica do coletor de lixo que voc no sabe a ordem em que os objetos sero destru
dos. A questo final a discutir talvez a mais importante: os destrutores no so executados at
que os objetos sofram coleta de lixo. Se voc escrever um destrutor, sabe que ele ser executado, mas
voc simplesmente no sabe quando. Consequentemente, voc nunca deve escrever um cdigo que
dependa dos destrutores em execuo em uma sequncia especfica ou em um ponto especfico em
seu aplicativo.

Como funciona o coletor de lixo?


O coletor de lixo executa em sua prpria thread e s pode executar em certas horas - normalmente
quando o aplicativo chega ao final de um mtodo. Enquanto ele executa, outras threads em execuo
no seu aplicativo so temporariamente suspensas. Isso acontece porque o coletor de lixo poderia
precisar mover os objetos e atualizar as referncias de objeto, e ele no pode faz-lo caso o objeto
esteja em uso.

316

Parte II

Entendendo a linguagem C#

Os passos executados so:


1. Ele constri um mapa de todos os objetos acessveis. Ele faz isso seguindo repetidamente os
campos de referncia dentro dos objetos. Esse mapa construdo cuidadosamente certifican
do-se de que as referncias circulares no causam uma recurso infinita. Qualquer objeto que
no esteja nesse mapa considerado inacessvel.
2. Verifica se algum dos objetos inacessveis tem um destrutor que precisa ser executado (um pro
cesso chamadofinalizao). Todo objeto inacessvel que requeira uma finalizao colocado em
uma fila especial chamadafilafreach ab le (pronuncia-se efe-rtchbl).
3. Ele desaloca os objetos inacessveis restantes (aqueles que no requerem finalizao) movendo
os objetos acessveis para a parte inferior do heap, desfragmentando assim o heap e liberando
a memria na parte superior do heap. Quando o coletor de lixo move um objeto acessvel, ele
tambm atualiza todas as referncias ao objeto.
4. Nesse ponto, ele permite que outras threads se reiniciem.
5. Finaliza os objetos inacessveis que requerem finalizao (agora na filafreachable) em sua pr
pria thread.

Recomendaes
Escrever classes que contenham destrutores aumenta a complexidade do seu cdigo e do proces
so de coleta de lixo e faz seu programa executar mais lentamente. Se o programa no contiver
destrutor algum, o coletor de lixo no precisar posicionar objetos inacessveis na filafreachable
e finaliz-los. Evidentemente, no fazer coisa alguma mais rpido do que fazer. Portanto, tente
evitar o uso de destrutores, exceto quando voc realmente precisar deles. Por exemplo, considere a
possibilidade de empregar uma instruo using (consulte a seo A instruo using" mais adiante
neste captulo).
Voc precisa ter bastante cuidado ao escrever um destrutor. Em particular, voc deve estar ciente de
que, se seu destrutor chamar outros objetos, possvel que o coletor de lixoj tenha chamado os
destrutores desses outros objetos. Lembre-se de que a ordem da finalizao no garantida. Portan
to, certifique-se de que destrutores no dependam um do outro nem se sobreponham (evite que dois
destrutores tentem liberar o mesmo recurso, por exemplo).

Gerenciamento de recursos
s vezes desaconselhvel liberar um recurso em um destrutor; alguns recursos so simplesmente
muito valiosos para que permaneam ociosos por um perodo de tempo arbitrrio at que o coletor de
lixo realmente os libere. Os recursos escassos precisam ser liberados, e isso precisa ser feito o mais
cedo possvel. Nessas situaes, sua nica opo voc mesmo liberar o recurso, criando um mtodo
de descarte. Um mtodo de descarte descarta um recurso. Se uma classe tem um mtodo de descarte,
voc pode cham-lo e controlar quando o recurso ser liberado.

Captulo 14

Utilizando a coleta de lixo e o gerenciamento de recursos

317

Mtodos de descarte
Um exemplo de classe que implementa um mtodo de descarte a TextR.ead.er do namespace Sys
tem. IO. Essa classe fornece um mecanismo para ler caracteres em um fluxo sequencial de entrada.
A classe TextReader contm um mtodo virtual chamado Close, que fecha o fluxo. A classe StreamReader (que l os caracteres de um fluxo, como um arquivo aberto) e a classe StringReader (que
l os caracteres de uma string) derivam da classe TextReader, e ambas redefinem o mtodo Close.
Eis um exemplo que l as linhas de texto de um arquivo utilizando a classe Stream Reader e ento
as exibe na tela:
TextReader reader = new Stream R ead er(filenam e);
s trin g lin e ;
w h ile ( ( l i n e = re a d e r.R e a d L in e O ) != n u ll)

{
C o n s o le .W r it e L in e (lin e );

}
re a d e r.C lo s e t);

O mtodo ReadLine l a prxima linha de texto do fluxo e a armazena em uma string. O mtodo ReadLine retorna null se no restar coisa alguma no fluxo. importante chamar Close quando voc tiver
terminado com reader para liberar o handle de arquivo e recursos associados. Mas h um problema
com esse exemplo: no seguro quanto a excees. Se a chamada para ReadLine ou Write-Line gerar
uma exceo, a chamada para Close no acontecer; ser pulada. Se isso acontecer com muita fre
quncia, voc ficar sem handles de arquivos e no ser capaz de abrir mais arquivo algum.

Descarte seguro quanto a excees


Uma maneira de garantir que um mtodo de descarte (como Close) seja sempre chamado, indepen
dentemente de haver ou no uma exceo, chamar o mtodo de descarte dentro de um blocojinal/y.
Veja o exemplo anterior codificado utilizando essa tcnica:
TextReader reader = new Stream R ead er(filen am e);
try

{
s trin g lin e ;
w h ile ( ( l i n e = re a d e r.R e a d L in e O ) != n u ll)

{
C o n s o le .W r it e L in e (lin e );

>
}
f i n a l 1y

{
re a d e r.C lo s e O ;

318

Parte II

Entendendo a linguagem C#

Utilizar um blocofin a lly como esse funciona, mas tem vrias desvantagens que o tornam uma solu
o longe da ideal:
O cdigo torna-se rapidamente difcil de manejar se voc remover mais de um recurso (voc
acaba tendo blocos try efin a lly aninhados).
Em alguns casos, talvez voc precise modificar o cdigo (por exemplo, voc poderia precisar
reordenar a declarao da referncia de recurso, lembrar-se de inicializar a referncia como null
e de verificar se a referncia no nu ll no blocofin a lly ).
Ele falha ao criar uma abstrao da soluo. Isso significa que a soluo difcil de entender, e
voc precisar repetir o cdigo onde essa funcionalidade for necessria.
A referncia ao recurso permanece no escopo aps o blocofin a lly . Isso significa que voc pode
acidentalmente tentar utilizar o recurso aps ele ter sido liberado.
A instruo using projetada para solucionar todos esses problemas.

A instruo using
A instruo using fornece um mecanismo limpo para controlar os tempos de vida dos recursos. Voc
pode criar um objeto e esse ser destrudo quando o bloco da instruo using terminar.

Importante

No confunda a instruo

using mostrada nesta seo com a diretiva using que

coloca um namespace em escopo. Infelizmente essa mesma palavra-chave tem dois significados
diferentes.

A sintaxe para uma instruo using :


using ( tipo varivel = in icia liza o )

{
StatementBlock

}
Eis a melhor maneira de garantir que seu cdigo sempre chamar Close em um TextReader:
using (TextReader reader = new Stream R eader(filenam e))

{
s trin g lin e ;
w h ile ( ( l i n e = re a d e r.R e a d L in e ())

{
C o n s o le .W r it e L in e (lin e );

}
}

!= n u ll)

Captulo 14 Utilizando a coleta de lixo e o gerenciamento de recursos

319

A instruo using precisamente equivalente seguinte transformao:


{
TextReader reader = new Stream R ead er(filen am e);
try

{
s trin g lin e ;
w h ile ( ( l i n e = re a d e r.R e a d L in e ())

!= n u ll)

{
C o n s o le .W r it e L in e (lin e );

}
}
f in a lly

{
i f (read er != n u ll)

{
( (ID i sposabl e) reader) . Di sposeO ;

}
}
}
A varivel que voc declara em uma instruo using deve ser do tipo que implementa a interface
IDisposable.

Nota

A instruo using introduz um bloco prprio para propsitos de definio de escopo. Esse

arranjo significa que a varivel declarada em uma instruo using sai automaticamente de escopo
no final da instruo embutida e no h como voc acidentalmente tentar acessar um recurso re
movido.

A interface IDisposable reside no namespace System e contm apenas um mtodo, chamado Dispose:
namespace System

{
in te rfa c e ID isposab le

{
void Di s p o s e O ;

}
}
Isso acontece de tal maneira que a classe Stream Reader implementa a interface IDisposable, e seu
mtodo Dispose chama Close para fechar o fluxo. Voc pode empregar uma instruo using como
uma maneira adequada, segura e robusta quanto a excees para garantir que um recurso seja sem
pre liberado. Isso resolve todos os problemas que existiam na soluo manual trylfin ally. Voc agora
tem uma soluo que:

320

Parte II

Entendendo a linguagem C#

facilmente escalonvel se precisar descartar mltiplos recursos.


No altera a lgica do cdigo do programa.
Elimina o problema e evita a repetio.
robusta. Voc no pode utilizar a varivel que foi declarada dentro da instruo using (nesse
caso, reader) aps essa ter terminado porque ela no est mais no escopo - voc obter um erro
de tempo de compilao.

Chamando o mtodo Dispose a partir de um destrutor


Ao escrever uma classe, voc deve escrever um destrutor ou implementar a interface /Disposable?
Uma chamada para um destrutor acontecer, mas voc s no sabe quando. Por outro lado, voc
sabe exatamente quando uma chamada para o mtodo Dispose acontece, mas s no pode ter certe
za de que ela realmente acontecer, porque ela precisa que o programador se lembre de escrever uma
instruo using. Mas possvel garantir que o mtodo Dispose sempre execute chamando a partir
de um destrutor. Isso funciona como um backup til. Voc poder esquecer de chamar o mtodo Dis
pose, mas pelo menos pode ter certeza de que ele ser chamado, mesmo que seja somente quando o
programa terminar. Veja um exemplo de como fazer isso:
c la s s Example : ID isposable

{
p riv a te Resource scarce;
// recurso escasso para g eren ciar e d e sca rtar
p riv a te bool disposed = f a ls e ; // s in a liz a d o r para in d ic a r se o recurso
// fo i descartado
-ExampleO

{
Di s p o s e O ;

}
p u b lic v ir t u a l void D ispose( )

{
i f ( ! t h i s . d isposed)

{
try {
// lib e r a recursos escassos aqui

}
fin a lly {
t h i s . disposed = tru e ;
C C .S u p p re s s F in a liz e (th is );

}
}
}

Captulo 14

Utilizando a coleta de lixo e o gerenciamento de recursos

321

public void SomeBehaviorO // mtodo de exemplo

{
checklfD i sp o sed O ;

}
p riv a te void ch ecklfD i sposedO

{
i f (th is .d is p o s e d )

{
throw new O bjectDisposedExceptionCExam ple: o b je ct has been disposed o f " ) ;

}
}
}
Observe os seguintes recursos da classe Exam ple:
A classe implementa a interface IDisposable.
0 destrutor chama Dispose.
0 mtodo Dispose pblico e pode ser chamado a qualquer momento.
O mtodo Dispose pode ser chamado de modo seguro muitas vezes. A varivel disposed indica
se o mtodo j foi executado antes. O recurso escasso liberado somente na primeira vez que o
mtodo executa.
O mtodo Dispose chama o mtodo esttico GC.SuppressFinalize. Esse mtodo faz o coletor de
lixo parar de chamar o destrutor no seu objeto, porque o objeto agora foi finalizado.
Todos os mtodos comuns da classe (como SomeBehavior) verificam se o objeto j foi descartado.
Se afirmativo, eles geram uma exceo.

Implementando descarte seguro quanto a excees


No prximo exerccio, voc reescrever uma pequena parte do cdigo para torn-lo seguro quanto a
excees. O cdigo abre um arquivo de texto, l seu contedo linha por linha, escreve essas linhas
em uma caixa de texto em um formulrio na tela e ento fecha o arquivo de texto. Entretanto, se
surgir uma exceo enquanto o arquivo lido ou enquanto as linhas so escritas na caixa de texto,
a chamada para fechar o arquivo de texto ser pulada. Voc reescrever o cdigo para utilizar uma
instruo using, garantindo assim que o cdigo seja seguro quanto a excees.

Escreva um a instruo

using

1. Inicie o Microsoft Visual Studio 2010 se ele ainda no estiver executando.


2. Abra o projeto UsingStatem ent, localizado na pasta \Microsoft PressWisual CSharp Step By
Step\Chapter 14\UsingStatement na sua pasta Documentos.

322

Parte II

Entendendo a linguagem C#

3. No menu Debug, clique em S ta rt W ithout Debugging. Um formulrio Windows Presentation


Foundation (WPF) aparece.
4. No formulrio, clique em Open File.
5. Na caixa de dilogo Open, navegue para a pasta \Microsoft PressWisual CSharp Step by Step\
Chapter 14\UsingStatement\UsingStatement na sua pasta Documentos e selecione o arquivofonte Windowl.xaml.es.
Esse o arquivo-fonte do aplicativo.
6. Clique em Open.
0 contedo do arquivo exibido no formulrio, como mostrado aqui:

Current File

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.W indows.Data;
using System.Windows.Documents;
using System.W in d o w sln put;
using System,Windows.Media;
using System,Windows.Media.Imaging;
using System .Windows.Navigation;
using System.W indows.Shapes;
using System.10;
using Microsoft.W in32;
namespace Using Statement

{
/// <sum m ary>

7. Feche o formulrio para retornar ao Visual Studio 2010.


8. Abra o arquivo MainWindow.xaml.es na janela Code and Text Editor e ento localize o mtodo
openFileDialogFileOk.
0 mtodo semelhante a este:
p riv a te void o p e n F ile D ia lo g F ile O k (o b je c t sender,
System.ComponentModel. CancelEventArgs e)

{
s trin g f u l l Pathname = openFileD ialog.FileN am e;
F i le ln f o sre = new F ile In f o (f u llP a t h n a m e );
fileN am e.Text = src.Name;
s o u r c e .C le a r ();
TextReader reader = new Stream R eader(fullPathnam e);
s trin g lin e ;
w h ile ( ( l i n e = re a d e r. R e a d L in e ()) != n u ll)

{
source.Text += lin e + "\ n ";

}
re a d e r .C lo s e ();

Captulo 14

Utilizando a coleta de lixo e o gerenciamento de recursos

323

As variveisfileN am e, openFileDialog e source so trs campos privados da classe M ain Window.


Esse cdigo utiliza um objeto TextReader chamado reader para abrir o arquivo especificado pelo
usurio. (Os detalhes de como selecionar o arquivo esto descritos no Captulo 23, Obtendo a
entrada do usurio .) A instruo while contm a principal funcionalidade desse mtodo; ela
itera por meio do arquivo, em uma linha de cada vez, ao utilizar o mtodo ReadLine do objeto
reader, e exibe cada linha, anexando-a propriedade Text do campo de texto Source, no for
mulrio. Quando o mtodo ReadLine retorna nulo, no existem mais dados no arquivo, o loop
while termina e o mtodo Close do objeto reader fecha o arquivo.
O problema com esse cdigo que no h garantia de que a chamada a reader.Close ser execu
tada. Se ocorrer uma exceo aps a abertura do arquivo, o mtodo terminar com uma exce
o, mas o arquivo permanecer aberto at o prprio aplicativo terminar.
9. Modifique o mtodo openFileDialogFileOk e insira o cdigo que processa o arquivo dentro de
uma instruo using (incluindo as chaves de abertura e de fechamento), como mostrado em
negrito. Remova a instruo que fecha o objeto TextReader.
p riv a te void o p e n F ile D ia lo g F ile O k (o b je c t sender,
System.ComponentModel. CancelEventArgs e)

{
s trin g fullPathnam e = open FileD ialo g.FileN am e;
F ile ln f o src = new F ile In f o (f u llP a t h n a m e );
f i 1eName.Text = src.Name;
s o u r c e .C le a rO ;

using (TextReader reader = new StreamReader(fullPathname))

{
s trin g lin e ;
w h ile ( ( l i n e = rea d e r.R e a d L in e O )

1= n u ll)

{
source.Text += lin e + "\ n ";

}
}
}
Voc no precisa mais chamar reader.Close porque ele ser chamado automaticamente pelo m
todo Dispose da classe Stream Reader quando a instruo using for completada. Isso se aplica
tanto se a instruo using concluir naturalmente como se concluir devido a uma exceo.
10. No menu Debug, clique em Start W ithout Debugging.
11. Verifique se o aplicativo ainda funciona como anteriormente e ento feche o formulrio.
Neste captulo, vimos como o coletor de lixo funciona e como o .NET Framework o utiliza para des
cartar objetos e resgatar memria. Voc aprendeu a escrever um destrutor para limpar os recursos
utilizados por um objeto quando a memria reciclada pelo coletor de lixo. Voc tambm viu como
possvel utilizar a instruo using para implementar um descarte de recursos seguro quanto a
excees.
Se voc quiser seguir para o prximo captulo agora:
Mantenha o Visual Studio 2010 em execuo e v para o Captulo 15.
Se quiser sair do Visual Studio 2010:
No menu File, clique em Exit. Se vir uma caixa de dilogo Save, clique em Yes e salve o projeto.

324

Parte II

Entendendo a linguagem C#

Referncia rpida do Captulo 14


Para

Faa isto

Escrever um destrutor

Escreva um mtodo cujo nome seja igual ao nome da classe e seja


iniciado com um til (). 0 mtodo no deve ter um modificador
de acesso (como public) e no pode ter parmetro algum nem
retornar um valor. Por exemplo:
class Example
{
~Example()
{
}
}

Chamar um destrutor

Voc no pode chamar um destrutor. Somente o coletor de lixo


pode cham-lo.

Forar uma coleta de lixo (no recomendvel)

Chame System. GC.Collect.

Liberar um recurso em um determinado momento (mas com o risco de vazamentos de memria


se uma exceo interromper a execuo)

Escreva um mtodo de descarte (um mtodo que descarte um


recurso) e chame o explicitamente a partir do programa. Por
exemplo:
class TextReader

{
public v irtu a l void Close()

{
}
}
class Example

{
void Use()

{
TextReader reader = . . . ;
// u t iliz a r reader
re a d e r.C lo se ();

}
}

Microsoft Visual C # 2010 Passo a Passo

Parte III

Criando componentes
Captulo 15: Im plem entando propriedades para acessar cam pos.............327
Captulo 16: Utilizando in d e x a d o re s ............................................................347
Captulo 17: Interrom pendo o fluxo do programa e tratando eventos. . . 361
Captulo 18: Apresentando g e n ric o s ..........................................................385
Captulo 19: Enum erando c o le e s ..............................................................413
Captulo 20: Consultando dados na memria utilizando
expresses de c o n s u lt a ............................................................427
Captulo 21: Sobrecarga de operadores....................................................... 451

Captulo 15

Implementando propriedades
para acessar campos
Neste captulo, voc vai aprender a:
Encapsular campos lgicos utilizando propriedades.
Controlar o acesso de leitura s propriedades declarando mtodos de acesso gef.
Controlar o acesso de gravao s propriedades declarando mtodos de acesso sei.
Criar interfaces que declaram propriedades.
Implementar interfaces que contm propriedades utilizando estruturas e classes.
Gerar propriedades automaticamente com base em definies de campo.
Utilizar propriedades para inicializar objetos.

As duas primeiras partes deste livro apresentaram a sintaxe bsica da linguagem C# e mostraram
como utilizar o C# para criar novos tipos empregando estruturas, enumeraes e classes. Voc viu
tambm como o runtime gerncia a memria utilizada pelas variveis e pelos objetos quando um
programa executado e agora deve entender o ciclo de vida dos objetos do C#. Os captulos da Parte
III, "Criando componentes", so baseados nessas informaes mostrando a voc como utilizar o C#
para criar componentes reutilizveis - classes funcionais que voc pode reutilizar em muitos aplicativos diferentes.
Este captulo examina como definir e utilizar as propriedades para encapsular campos e dados em
uma classe. Os captulos anteriores enfatizaram que voc deve tornar privados os campos em uma
classe e fornecer mtodos para armazenar e recuperar valores. Essa abordagem oferece acesso seguro e controlado a campos e permite encapsular a lgica e as regras adicionais em relao a valores
que so permitidos. Mas a sintaxe para acessar um campo dessa maneira no natural. Quando
quer ler ou escrever uma varivel, voc normalmente utiliza uma instruo de atribuio; portanto,
chamar um mtodo para conseguir o mesmo efeito em um campo (que , afinal de contas, apenas
uma varivel) parece algo grosseiro. As propriedades so projetadas para resolver esse problema.

328

Parte III

Criando componentes

Implementando encapsulamento com mtodos


Em primeiro lugar, vamos recapitular a motivao original para utilizar os mtodos a fim de ocultar
os campos.
Considere a seguinte estrutura que representa uma posio na tela de um computador como um par
de coordenadas x e j. Suponha que o intervalo de valores vlidos para a coordenada x resida entre 0
e 1280 e o intervalo de valores vlidos para a coordenada^ resida entre 0 e 1024:
s t r u c t Screen P o sitio n

{
p u b lic in t X;
p u b lic in t Y;
p u b lic S c re e n P o s itio n (in t x, in t y)

{
t h is .X = rangeCheckedX(x);
t h is .Y = rangeCheckedY(y);

}
p riv a te s t a t i c in t rangeCheckedX(int x)

{
if

(x < 0 || x > 1280)

{
throw new Argum entOutOfRangeException("X");

}
return x;

}
p riv a te s t a t i c in t rangeCheckedYfint y)

{
i f (y < 0 || y > 1024)

{
throw new Argum entOutOfRangeException("Y");

}
return y;

}
}
Um problema com essa estrutura que ela no segue a regra principal do encapsulamento - isto
, ela no mantm seus dados privados. Geralmente, os dados pblicos so uma m ideia porque a
classe no pode controlar os valores que um aplicativo especifica. Por exemplo, o construtor Screen
Position verifica a faixa de seus parmetros para ter certeza de que estejam em um intervalo espe
cificado, mas nenhuma verificao desse tipo pode ser feita no acesso bruto" aos campos pblicos.
Mais cedo ou mais tarde (provavelmente mais cedo), um erro ou um mau entendimento por parte de
um desenvolvedor que utiliza essa classe em um aplicativo poder (azerX ou Y sair desse intervalo:
Scre en P o sitio n o rig in = new S c re e n P o s itio n (0 , 0 );
in t xpos = o r ig in .X ;
o r ig in .Y = -100; // oops

Captulo 15

Implementando propriedades para acessar campos

329

A maneira de resolver esse problema criar campos privados e adicionar um mtodo de acesso e um
mtodo modificador para ler e escrever respectivamente o valor de cada campo privado. Os mtodos
modificadores podem ento verificar o intervalo dos novos valores de campo. Por exemplo, o cdigo
a seguir contm um mtodo de acesso (GetX) e um modificador (SetX) para o campo X . Observe como
SetX verifica seu valor de parmetro:
s tru c t Screen Po sitio n

{
p u b lic in t GetXO

{
return t h is .x ;

}
p u b lic void S e t X (in t newX)

{
t h is .x = rangeCheckedX(newX);

}
p riv a te s t a t i c in t rangeCheckedX(int x) { . . .
p riv a te s t a t i c in t rangeCheckedY(int y ) { . . .
p riv a te in t x, y;

}
}

}
O cdigo agora impe com xito as restries de intervalo de valores, o que bom. Mas h um preo
a ser pago por essa valiosa garantia - ScreenPosition no tem mais uma sintaxe natural do tipo cam
po; em vez disso, ele utiliza a complicada sintaxe baseada em mtodo. O exemplo a seguir aumenta
o valor d eX por 10. Para fazer isso, ele precisa ler o valor deX utilizando o mtodo de acesso GetX e
ento escrever o valor d eX utilizando o mtodo modificador SetX.
in t xpos = o rig in .G e tX O ;
origin.SetXCxpos + 10);

Compare esse cdigo com o cdigo equivalente caso o campoX fosse pblico:
o rig in .X += 10;

No h dvida de que, neste caso, utilizar campos pblicos sintaticamente mais claro, conciso e
fcil. Infelizmente, o uso de campos pblicos quebra o encapsulamento. As propriedades permitem
combinar o melhor dos dois mundos (campos e mtodos) para manter o encapsulamento, permitindo
a sintaxe do tipo campo.

O que so propriedades?
Uma propriedade um cruzamento entre um campo e um mtodo - ela parece um campo, mas atua
como um mtodo. Voc acessa uma propriedade utilizando exatamente a mesma sintaxe empregada
para acessar um campo. O compilador, porm, converte automaticamente essa sintaxe do tipo campo
em chamadas a mtodos de acesso.

330

Parte III

Criando componentes

A sintaxe de uma declarao de propriedade se parece com esta:


AccessModifier

Type PropertyName

{
get

{
// cdigo de le i t u r a de propriedade

}
set

{
// cdigo de gravao de propriedade

}
}
Uma propriedade pode conter dois blocos de cdigo, comeando com as palavras-chave get e set. O
bloco get contm instrues que so executadas quando a propriedade lida, e o bloco set engloba
instrues que so executadas quando a propriedade gravada. O tipo de propriedade especifica o
tipo de dados lidos e gravados pelos mtodos de acesso get e set.
O prximo exemplo de cdigo mostra a estrutura ScreenPosition reescrita utilizando propriedades. Ao
ler esse cdigo, observe o seguinte:
x e y minsculos so campos private.
X e Y maisculos so propriedades public.
m A todos os mtodos de acesso set so passados os dados a serem escritos, utilizando um par

metro oculto e predefinido chamado value.

Dica

Os campos e as propriedades seguem a conveno de nomes public/private padro do Mi

crosoft Visual C#. Os campos e as propriedades pblicos devem iniciar com uma letra maiscula,
mas os campos e as propriedades privados devem comear com uma letra minscula.

s tru c t Screen P o sitio n

{
p riv a te in t x, y;
p u b lic S c re e n P o s itio n (in t X, in t Y)

{
t h is .x = rangeCheckedX(X);
t h i s . y = rangeCheckedY(Y);

}
p u b lic in t X

{
get { return t h is .x ; }
se t { t h is .x = rangeCheckedX(value); }

Captulo 15

Implementando propriedades para acessar campos

331

p u b lic in t Y

{
get { return t h i s . y ; }
se t { t h i s . y = rangeCheckedY(value); }

}
p riv a te s t a t ic in t rangeCheckedX(int x) { . . .
p riv a te s t a t ic in t rangeCheckedY(int y ) { . . .

}
}

}
Nesse exemplo, um campo privado implementa diretamente cada propriedade, mas isso somente
uma das maneiras de implementar uma propriedade. Tudo o que necessrio que um mtodo de
acesso get retorne um valor do tipo especificado. Esse valor poderia ser calculado de forma fcil e
dinmica em vez de simplesmente ser recuperado dos dados armazenados, nesse caso no h neces
sidade de um campo fsico.

Utilizando propriedades
Ao utilizar uma propriedade em uma expresso, voc pode empreg-la em um contexto de leitura
(quando voc estiver lendo seu valor) ou em um contexto de gravao (quando estiver modificando
seu valor). O exemplo a seguir mostra como ler os valores das propriedadesX e Fde uma estrutura
ScreenPosition:
Screen Po sitio n o rig in = new S c re e n P o s itio n (0 , 0 );
in t xpos = o rig in .X ;
// chama o r ig in .X .g e t
in t ypos = o rig in .Y ;
// chama o r ig in .Y .g e t

Observe que voc acessa as propriedades e os campos usando a mesma sintaxe. Quando voc utiliza
uma propriedade em um contexto de leitura, o compilador automaticamente traduz seu cdigo do
tipo campo em uma chamada ao mtodo de acesso get dessa propriedade. Da mesma maneira, se
voc utilizar uma propriedade em um contexto de gravao, o compilador automaticamente traduz
seu cdigo do tipo campo em uma chamada para o mtodo de acesso set dessa propriedade:
o rig in .X = 4 0; // chama o r ig in .X .s e t , com o v a lo r configurado como 40
o r ig in .Y = 100; // chama o r i g i n . Y . s e t, com o v a lo r configurado como 100

Os valores atribudos so passados para os mtodos de acesso set utilizando a varivel value, con
forme descrito na seo anterior. O runtime faz isso automaticamente.
Alm disso, possvel usar uma propriedade em um contexto de leitura/gravao. Nesse caso, tanto
o mtodo de acesso get quanto o mtodo de acesso set so empregados. Por exemplo, o compilador
traduz automaticamente as instrues em chamadas aos mtodos de acesso^ e set como a seguin
te:
o rig in .X += 10;

332

Parte

III

Dica

Criando componentes

Voc pode declarar propriedades static assim como campos e mtodos static. As proprieda

des estticas so acessadas por meio do nome da classe ou estrutura em vez de uma instncia da
classe ou estrutura.

Propriedades somente-leitura
Voc pode declarar uma propriedade que contenha apenas um mtodo de acesso get. Nesse caso,
voc somente pode utilizar a propriedade em um contexto de leitura. Por exemplo, veja a propriedade
X da estrutura ScreenPosition declarada como uma propriedade somente-leitura:
s tru c t Screen P o sitio n

{
p u b lic in t X

{
get { return t h is .x ; }

}
}
A propriedadeX no contm um mtodo de acesso set; portanto, qualquer tentativa de utilizar em
um contexto de gravao falhar. Por exemplo:
o r ig in .X = 140; // erro de tempo de compilao

Propriedades somente-gravao
Da mesma forma, voc pode declarar uma propriedade que contm apenas um mtodo de acesso set.
Nesse caso, voc somente pode utilizar a propriedade no contexto de gravao. Por exemplo, veja a
propriedadeX da estrutura ScreenPosition declarada como uma propriedade de gravao:
s tru c t Screen P o sitio n

{
p u b lic in t X

{
s e t { t h is .x = rangeCheckedX(value); }

A propriedade^ no contm um mtodo de acesso get-, qualquer tentativa de utilizar X em um con


texto de leitura falhar. Por exemplo:
C o n s o le .W rite L in e C o rig in .X );
o r ig in .X 200;
o r ig in .X += 10;

// e rro de tempo de compilao


// compila OK
// erro de tempo de compilao

Captulo 15

Implementando propriedades para acessar campos

333

Acessibilidade de propriedades
Voc pode especificar a acessibilidade de uma propriedade (public, private ou protected) quando voc
a declara. Mas tambm possvel dentro da declarao de propriedade redefinir a acessibilidade da
propriedade para os mtodos de acesso get e set. Por exemplo, a verso da estrutura ScreenPosition
mostrada aqui define o mtodo de acesso set das propriedades X e V como private. (Os mtodos de
acesso get so public, porque as propriedades so public.)
s tru c t Screen Po sitio n

{
p u b lic in t X

{
get { return t h is .x ; }
p riv a te set { t h is .x = rangeCheckedX(value); }

>
p u b lic in t Y

{
get { return t h i s . y ; }
p riv a te set { t h i s . y = rangeCheckedY(value); }

}
p riv a te in t x, y;

>
Voc deve observar algumas regras ao definir mtodos de acesso com acessibilidades diferentes
entre si:
possvel alterar a acessibilidade de apenas um dos mtodos de acesso quando definidos. No
faria muito sentido definir uma propriedade como public apenas para alterar a acessibilidade de
ambos os mtodos de acesso para private\
O modificador no deve especificar uma acessibilidade que seja menos restritiva do que a da
propriedade. Por exemplo, se a propriedade for declarada como private, voc no poder espe
cificar o mtodo de acesso de leitura como public (em vez disso, voc tornaria a propriedade
public e o mtodo de acesso de gravao private).

334

Parte III

Criando componentes

Nomes de propriedades e campos: um alerta


Embora seja uma prtica comumente aceita dar s propriedades e aos campos privados o
mesmo nome, diferindo apenas pela letra inicial maiscula, voc deve estar ciente de uma

Employee. 0
EmployeelD fornece acesso pblico a esse

desvantagem. Examine o cdigo a seguir que implementa uma classe chamada


campo employeelD privado, mas a propriedade
campo.
c la s s Employee

{
p riv a te in t employeelD;
p u b lic in t EmployeelD;

{
get { return t h i s . EmployeelD; }
se t { t h i s . EmployeelD = va lu e ; }

}
}
Esse cdigo compilar perfeitamente bem, mas resulta em um programa que lana uma

StackOverflowException sempre que a propriedade EmployeelD acessada. Isso ocorre por


que os mtodos de acesso get e set referenciam a propriedade (com a letra maiscula E) em
vez do campo privado (com a letra minscula e), o que causa um loop recursivo infinito que
acaba fazendo o processo esgotar a memria disponvel. Esse tipo de erro muito difcil de
descobrir!

Entendendo as restries de uma propriedade


As propriedades se parecem com e atuam como campos. Mas elas no so campos verdadeiros, e
determinadas restries se aplicam a elas:
Voc pode atribuir um valor por meio de uma propriedade de uma estrutura ou classe somente
depois que a estrutura ou a classe foi inicializada. O exemplo de cdigo a seguir no vlido
porque a varivel location no foi inicializada (utilizando new):
S creen P o sitio n lo c a tio n ;
lo c a tio n .X = 40; // erro de tempo de compilao, lo c a tio n no fo i a trib u id a

Voc no pode utilizar uma propriedade como um argumento ref ou out para um mtodo (embo
ra seja possvel usar um campo gravvel como um argumento ref ou out). Isso faz sentido por-

Captulo 15

Implementando propriedades para acessar campos

335

que a propriedade na realidade no aponta para uma posio da memria, mas, em vez disso,
para um mtodo de acesso. Por exemplo:
MyMethod(ref lo c a t io n .X ); // e rro de tempo de compilao

Uma propriedade pode conter no mximo um mtodo de acesso get e um mtodo de acesso set.
Uma propriedade no pode conter outros mtodos, campos ou outras propriedades.
Os mtodos de acesso get e set no podem receber parmetro algum. Os dados que so atribu
dos so passados automaticamente para o mtodo de acesso set, utilizando a varivel value.
Voc no pode declarar propriedades const. Por exemplo:
const in t X { get

set { . . . } } // erro de tempo de compilao

Utilizando as propriedades adequadamente


As propriedades so um recurso poderoso, e quando utilizadas do modo correto, podem
contribuir para facilitar o entendimento e a manuteno do cdigo. Mas elas no substituem
um cuidadoso projeto orientado a objetos que focaliza o comportamento dos objetos em vez
de suas propriedades. O acesso aos campos privados por meio de mtodos ou propriedades
comuns no torna, por si s, seu cdigo bem projetado. Por exemplo, uma conta de banco
armazena um saldo. Voc poderia ser tentado a criar uma propriedade

Balance em uma clas

se BankAccount, como esta:


c la s s BankAccount

{
p u b lic money Balance

{
get { . . .
set { . . .

}
}

}
p riv a te money balance;

}
Esse um projeto medocre, pois falha ao representar a funcionalidade necessria para sacar
e depositar dinheiro em uma conta (se conhecer um banco em que voc pode mudar o saldo
da sua conta diretamente sem efetuar depsito, me avisei). Ao programar, tente expressar
o problema em questo na prpria soluo, e no em um labirinto de sintaxe de baixo nvel:
c la s s BankAccount

{
p u b lic money Balance { get { . . . } }
// Saldo
p u b lic void Deposit(money amount) { . . . } // Depsitos
p u b lic bool Withdraw(money amount) { . . . } // Sacar
p riv a te money balance;

336

Parte III

Criando componentes

Declarando propriedades de interface


J discutimos interfaces no Captulo 13, Criando interfaces e definindo classes abstratas". As in
terfaces podem definir tanto propriedades quanto mtodos. Para fazer isso, voc declara a pala
vra-chave get ou set, ou ambas, mas substitui o corpo do mtodo de acesso get ou set por um ponto
e vrgula. Por exemplo:
in te r fa c e IS c re e n P o s itio n

{
in t X { g et; s e t; }
in t Y { g et; s e t; }

}
Qualquer classe ou estrutura que implemente essa interface deve implementar as propriedades X e Y
com os mtodos de acesso get e set. Por exemplo:
s tru c t Screen P o sitio n : IS c re e n P o s itio n

{
p u b lic in t X

{
get { . . .
set { . . .

}
}

}
p u b lic in t Y

{
get { . . .
set { . . .

}
}

Se voc implementar as propriedades de interface em uma classe, poder declarar as implementaes


da propriedade como virtu al, o que permite s classes derivadas redefinirem as implementaes. Por
exemplo:
c la s s Scre en P o sitio n : IS c re e n P o s itio n

{
p u b lic v i rtu a l i nt X

{
get { . . .
se t { . . .

}
}

}
publi c v i rtu a l i nt Y

{
get { . . .
set { . . .

}
}

Captulo 15

Implementando propriedades para acessar campos

337

Voc tambm pode optar por implementar uma propriedade utilizando a sintaxe explcita de imple
mentao de interface abordada no Captulo 13. Uma implementao explcita de uma propriedade
no pblica e no virtual (e no pode ser redefinida). Por exemplo:
s tru c t Screen Po sitio n : IS c re e n P o s itio n

{
in t IS c re e n P o s itio n .X

{
get { . . . }
se t { . . . }

}
in t IS c re e n P o s itio n .Y

{
get { . . .
se t { . . .

}
}

}
p riv a te in t x, y;

Utilizando propriedades em um aplicativo Windows


Ao configurar valores para propriedades de objetos, como controles TextBox, Windows e Buttons,
utilizando a janela Properties no Microsoft Visual Studio 2010, voc na verdade est gerando um
cdigo que configura os valores dessas propriedades em tempo de execuo. Alguns componentes
tm um grande nmero de propriedades, embora algumas sejam mais utilizadas do que outras. Voc
pode escrever seu prprio cdigo para modificar boa parte dessas propriedades em tempo de execu
o empregando a mesma sintaxe que voc viu por todo este captulo.
No prximo exerccio, voc utilizar algumas propriedades predefinidas dos controles TextBox e da
classe Window para criar um aplicativo simples que exibir continuamente o tamanho da sua janela
principal, mesmo quando ela for redimensionada.

Utilize propriedades
1. Inicie o Visual Studio 2010 se ele ainda no estiver em execuo.
2. Abra o projeto WindowProperties, localizado na pasta \Microsoft PressWisual CSharp Step By
Step\Chapter 15\WindowProperties na sua pasta Documentos.
3. No menu Debug, clique em Start W ithout Debugging.
O projeto compila e executa. Um formulrio Windows Presentation Foundation (WPF) aparece,
exibindo duas caixas de texto vazias rotuladas Width e Height.

338

Parte III

Criando componentes

No programa, os controles da caixa de texto so nomeados width e height. Atualmente, eles


esto vazios. Voc adicionar um cdigo ao aplicativo que exibe o tamanho atual da janela, e
que atualiza os valores nessas caixas de texto se a janela for redimensionada.
4. Feche o formulrio e retorne ao ambiente de programao do Visual Studio 2010.
5. Exiba o arquivo MainWindow.xaml.es na janela Code and Text Editor e localize o mtodo sizeChanged.
Esse mtodo chamado pelo construtor de M ainWindow. Voc o utilizar para exibir o tama
nho atual do formulrio nas caixas de texto width e height. Tambm vai usar as propriedades
ActualW idth e ActualH eight da classe Window, que retornam a largura e a altura atuais do
formulrio como valores double.
6. Adicione duas instrues ao mtodo sizeChanged para exibir o tamanho do formulrio. A pri
meira instruo deve ler o valor da propriedade ActualW idth do formulrio, convert-lo em uma
string e atribuir esse valor propriedade Text da caixa de texto width. A segunda instruo deve
ler o valor da propriedade ActualHeight do formulrio, convert-lo em uma string e atribuir esse
valor propriedade Text da caixa de texto height.
O mtodo sizeChanged deve ficar assim:
p riv a te void sizeChangedO

{
w idth.T ext = th is .A c tu a lW id th .T o S trin g O ;
h eig h t.T ex t = th is .A c tu a lH e ig h t.T o S trin g O ;

}
7. Localize o mtodo M ain WindowsizeChanged.
Esse mtodo executa sempre que o tamanho da janela muda quando o aplicativo est em exe
cuo. Observe que esse mtodo chama o mtodo sizeChanged para exibir o novo tamanho da
janela nas caixas de texto.
8. No menu Debug, clique em Start W ithout Debugging para compilar e executar o projeto.
O formulrio exibe as duas caixas de texto contendo os valores 305 e 155. Essas so as dimen
ses padro do formulrio, especificadas quando o formulrio foi projetado.
9. Redimensione o formulrio. Observe que o texto nas caixas de texto muda a fim de refletir o
novo tamanho.
10. Feche o formulrio e retorne ao ambiente de programao do Visual Studio 2010.

Gerando propriedades automticas


Este captulo mencionou anteriormente que o principal propsito das propriedades ocultar a im
plementao dos campos. Isso bom se suas propriedades realizarem algum trabalho til, mas se os

Captulo 15

Implementando propriedades para acessar campos

339

mtodos de acesso
e set simplesmente envolvem operaes que apenas leem ou atribuem um va
lor a um campo, voc poderia questionar o valor dessa abordagem. H pelo menos duas boas razes
para voc definir propriedades em vez de expor dados como campos pblicos:

Compatibilidade com aplicativos

Os campos e as propriedades se expem utilizando dife


rentes metadados nos assemblies. Se desenvolver uma classe e decidir utilizar os campos pbli
cos, qualquer aplicativo que usar essa classe ir referenciar esses itens como campos. Embora
seja possvel empregar a mesma sintaxe C# para ler e gravar um campo que voc utiliza ao ler
e gravar uma propriedade, o cdigo compilado bem diferente - o compilador C# s oculta as
diferenas de voc. Se voc mais tarde decidir que precisa transformar esses campos em pro
priedades (talvez os requisitos do negcio tenham mudado e voc precise realizar uma lgica
adicional ao atribuir valores), os aplicativos existentes no sero capazes de utilizar a verso
atualizada da classe sem uma recompilao. Isso complicado se voc instalou o aplicativo em
um grande nmero de desktops dos usurios por toda uma organizao. H maneiras de con
tornar isso, mas, em geral, melhor evitar essa situao desde o incio.

Compatibilidade com interfaces

Se estiver implementando uma interface e ela definir um


item como uma propriedade, voc dever escrever uma propriedade que corresponda especifi
cao na interface, mesmo se a propriedade apenas ler e gravar os dados em um campo privado.
Voc no pode implementar uma propriedade simplesmente expondo um campo pblico com o
mesmo nome.

Os projetistas da linguagem C# perceberam que os programadores so pessoas atarefadas que no


devem desperdiar tempo escrevendo mais cdigo do que o necessrio. Para esse fim, o compilador
C# pode gerar o cdigo para propriedades automaticamente, desta maneira:
cla s s C ir c le

{
p u b lic in t radius { get; s e t; }

}
Nesse exemplo, a classe Circle contm uma propriedade chamada Radius. Alm do tipo, voc no
especificou como essa propriedade funciona - os mtodos de acesso get e set esto vazios. O com
pilador C# converte essa definio em um campo privado e em uma implementao padro que se
parece a esta:
c la s s C ir c le

{
p riv a te in t _ra d iu s;
p u b lic in t Radius{
get

{
return t h is ._ r a d iu s ;

340

Parte III

Criando componentes

set

{
t h is ._ r a d iu s = va lu e;

}
}

Portanto, com o mnimo de esforo, voc pode implementar uma propriedade simples utilizando um
cdigo automaticamente gerado; se precisar incluir uma lgica adicional posteriormente, voc pode
r fazer isso sem quebrar aplicativos existentes. Voc deve observar, porm, que necessrio espe
cificar um mtodo de acesso get e um mtodo de acesso set com uma propriedade automaticamente
gerada - uma propriedade automtica no pode ser somente-leitura ou somente-gravao.

Inicializando objetos com propriedades


No Captulo 7, Criando e gerenciando classes e objetos , voc aprendeu a definir construtores para
inicializar um objeto. Um objeto pode ter mltiplos construtores e voc pode definir construtores com
parmetros variados para inicializar diferentes elementos em um objeto. Por exemplo, voc poderia
definir uma classe que modela um tringulo desta maneira:
p u b lic c la s s T ria n g le

{
p riv a te in t sid elLe n g th;
p riv a te in t side2Length;
p riv a te in t side3Length;
// co n struto r padro - v a lo re s padro para todos os lados
p u b lic T ria n g le O

{
th is .s id e lL e n g th = th is.s id e 2 L e n g th = th is.sid e 3 L e n g th = 10;

}
// e s p e c ific a o comprimento para sid elL en g th , va lo re s padro para os outros
p u b lic T r ia n g le (in t le n g th l)

{
t h i s . sidelLen gth = le n g th l;
th is.sid e 2 L e n g th = th is.s id e 3 L e n g th = 10;

Captulo 15

Implementando propriedades para acessar campos

341

}
// e s p e c ific a o comprimento para sidelLength e side2Length,
// v a lo r padro para side3Length
p u b lic T r ia n g le (in t le n g th l, in t length2)

{
th is .s id e lL e n g th = le n g th l;
th is.sid e 2 Le n g th = length2;
th is.sid e 3 Le n g th = 10;

}
// e s p e c ific a o comprimento para todos os lados
p u b lic T r ia n g le (in t le n g th l, in t length2, in t length3)

{
t h i s . sid elLen gth = le n g th l;
th is.sid e 2 L e n g th = length2;
th is.sid e 3 L e n g th = length3;

}
}
Dependendo de quantos campos uma classe contm e das vrias combinaes que voc quer permitir
para inicializar os campos, talvez voc precise escrever vrios construtores. Tambm h possveis
problemas se muitos dos campos tiverem o mesmo tipo: talvez voc no seja capaz de escrever um
construtor nico para todas as combinaes dos campos. Por exemplo, na classe Triangle anterior,
voc no poderia adicionar facilmente um construtor que s inicializa os campos sidelLength e sideJLength porque ele no teria uma assinatura nica; receberia dois parmetros int, e o construtor
que inicializa sidelLength e side2Length j tem essa assinatura. Uma possvel soluo definir um
construtor que aceite parmetros opcionais, e especificar valores para os parmetros como argu
mentos nomeados, quando voc criar um objeto Triangle. Entretanto, uma soluo mais eficiente e
transparente inicializar os campos privados com seus valores padro e definir propriedades, como
demonstrado a seguir:
p u b lic c la s s T ria n g le

{
p riv a te in t sid elLength = 10;
p riv a te in t side2Length = 10;
p riv a te in t side3Length = 10;
p u b lic in t SidelLength

{
se t { th is .s id e lL e n g th = va lu e ; }

}
p u b lic in t Side2Length

{
set { th is.sid e 2 L en g th = va lu e ; }

342

III

Parte

Criando componentes

p u b lic in t SideBLength

{
se t { th is.s id e 3 L e n g th = va lu e ; }

}
}
Ao criar uma instncia de uma classe, voc pode inicializ-la especificando valores para qualquer
propriedade pblica que tenha mtodos de acesso set. Isso significa que possvel criar objetos
Trangle e inicializar qualquer combinao dos trs lados, desta maneira:
T ria n g le
T ria n g le
T ria n g le
T ria n g le

tr il
t r i2
t r i3
tr i4

=
=
=
=

new
new
new
new

T ria n g le
T ria n g le
T ria n g le
T ria n g le

{
{
{
{

Side3Length = 15 } ;
SidelLen gth = 15, Side3Length = 20 } ;
Side2Length = 12, Side3Length = 17 } ;
SidelLength = 9, Side2Length = 12,
Side3Length = 15 } ;

Essa sintaxe conhecida como inicializador de objeto. Ouando voc chama um inicializador de ob
jeto utilizando essa sintaxe, o compilador C# gera o cdigo que chama o construtor padro e ento
chama o mtodo de acesso set de cada propriedade identificada para inicializ-la com o valor espe
cificado. Voc tambm pode especificar inicializadores de objeto em combinao com construtores
no padro. Por exemplo, se a classe Trangle tambm fornecer um construtor que recebeu um nico
parmetro string descrevendo o tipo de tringulo, voc poder chamar esse construtor e inicializar as
outras propriedades desta maneira:
T ria n g le t r i5 = new T ria n g 1 e ("E q u ila te ra l t r ia n g le " ) { SidelLength = 3,
Side2Length = 3,
Side3Length = 3 } ;

O mais importante a lembrar que o construtor executa primeiro, e as propriedades so configuradas


subsequentemente. Entender essa sequncia importante se o construtor configurar os campos em
um objeto com valores especficos e se as propriedades que voc especifica mudarem esses valores.
Voc tambm pode utilizar inicializadores de objeto com propriedades automticas, como veremos
no prximo exerccio. Neste exerccio, voc definir uma classe para modelar polgonos regulares,
que contm propriedades automticas para fornecer acesso a informaes sobre o nmero de lados
que o polgono contm e o comprimento desses lados.

Defina p ropriedades a u to m ticas e utilize inicializadores de objeto


1. No Visual Studio 2010, abra o projeto Autom aticProperties, localizado na pasta \Microsoft Press\
Visual CSharp Step by Step\Chapter 15\AutomaticProperties na sua pasta Documentos.
O projeto Autom aticProperties contm o arquivo de Program.cs que define a classe Program com
os mtodos M ain e DoWork que vimos nos exerccios anteriores.
2. No Solution Explorer, clique com o boto direito do mouse no projeto Autom aticProperties,
aponte para Add e ento clique em Class. Na caixa de dilogo Add New Item -Autom aticProper
ties, na caixa de texto Name, digite Polygon.cs e ento clique em Add.

Captulo 15

Implementando propriedades para acessar campos

343

0 arquivo Polygon.cs, contendo a classe Polygon, criado e adicionado ao projeto e aparece na


janela Code and Text Editor.
3. Adicione as propriedades automticas NumSides e SideLength, mostradas em negrito, classe
Polygon-.
class Polygon

{
public in t NumSides { get; set; }
public double SideLength { get; set; }

}
4. Adicione o seguinte construtor padro classe Polygon-.
class Polygon

public Polygon( )

{
t h i s . NumSides = 4;
t h i s . SideLength = 10.0;

}
Neste exerccio, o polgono padro um quadrado com lados de comprimento de 10 unidades.
5. Exiba o arquivo Program.cs na janela Code and Text Editor.
6. Adicione as instrues mostradas em negrito ao mtodo DoWork-.
s t a t i c void DoWorkO

{
Polygon square = new Polygon O ;
Polygon tria ngle = new Polygon { NumSides = 3 } ;
Polygon pentagon = new Polygon { SideLength = 15.5, NumSides = 5 } ;

}
Essas instrues criam objetos Polygon. A varivel square inicializada utilizando o construtor
padro. As variveis triangle epentagon tambm so inicializadas atravs do construtor padro
e esse cdigo muda o valor das propriedades expostas pela classe Polygon. No caso da varivel
triangle, a propriedade NumSides configurada como J , mas a propriedade SideLength perma
nece no valor padro 10.0. Para a varivel pentagon, o cdigo altera os valores das proprieda
des SideLength e NumSides.
7. Adicione o seguinte cdigo ao final do mtodo DoWork-,
s t a t i c void DoWorkO

Console.WriteLine("Square: number of sides is { 0 } , length of each side is { 1 } " ,


square. NumSi des, square. Si deLength);

344

Parte III

Criando componentes

Console.WriteLine("Triangle: number of sides is { 0 } , length of each side is { 1 } " ,


triangle.NumSides, tria ngle .S ide Le ngth );
Console.WriteLine("Pentagon: number of sides is { 0 } , length of each side is { 1 } " ,
pentagon. IMumSi des, pentagon. Si deLength) ;

}
Essas instrues exibem os valores das propriedades NumSides e SideLength para cada objeto
Polygon.
8. No menu Debug, clique em Start W ithout Debugging.
Verifique se o programa constri e executa, gravando a mensagem mostrada no console:

E H C:\Windows^system32' cmd.exe

Square: number of sides is 4, length of each side is 10


Triangle: number of sides is 3, length of each side is 10
Pentagon: number of sides is 5, length of each side is 15.5
Press any key to continue . . .

9. Pressione a tecla Enter para finalizar o programa e retornar ao Visual Studio 2010.
Neste captulo, vimos como criar e utilizar propriedades para fornecer acesso controlado aos dados
em um objeto. Examinamos tambm a criao de propriedades automticas e o uso de propriedades
ao inicializar objetos.
Se voc quiser seguir para o prximo captulo agora:
Mantenha o Visual Studio 2010 em execuo e v para o Captulo 16.
Se quiser sair do Visual Studio 2010:
No menu File, clique em Exit. Se vir uma caixa de dilogo Save, clique em Yes e salve o projeto.

Captulo 15

Implementando propriedades para acessar campos

345

Referncia rpida do Captulo 15


Declarar uma propriedade de leitura/gravao
para uma estrutura ou classe

Declare o tipo da propriedade, seu nome, um mtodo de acesso get


e um mtodo de acesso set. Por exemplo:

struct ScreenPosition

public int X
{
get { ... }
set { ... }

Declarar uma propriedade somente-leitura


para uma estrutura ou classe

Declare uma propriedade com apenas um mtodo de acesso get.


Por exemplo:
struct ScreenPosition

public int X
{
get { ... }

Declarar uma propriedade somente-gravao


para uma estrutura ou classe

Declare uma propriedade com apenas um mtodo de acesso set. Por


exemplo:

struct ScreenPosition

public int X
set { . . }

Declarar uma propriedade em uma interface

Declare uma propriedade com apenas uma palavra-chave get ou sef,


ou ambas. Por exemplo:

interface IScreenPosition

int X { get; set; } // nenhum corpo


int Y { get; set; } // nenhum corpo
(continua)

346

Parte III

Criando componentes

Para

Faa isto

Implementar uma propriedade de interface em


uma estrutura ou em uma classe

Na classe ou estrutura que implementa a interface, declare a propriedade e implemente os mtodos de acesso. Por exemplo:
s tru c t Screen P o sitio n : IS c re e n P o s itio n

{
p u b lic 'i n t X
{
get { ... }
set { . . . }

}
p u b lic
in t Y
{
get { . . .
set { . . .

}
}

}
}
Criar uma propriedade automtica

Na classe ou estrutura que contm a propriedade, defina a proprie


dade com mtodos de acesso get e set vazios. Por exemplo:
c la s s Polygon

{
p u b lic in t NumSides { get; s e t; }

}
Usar propriedades para inicializar um objeto

Ao construir o objeto, especifique as propriedades e seus valores


como uma lista dentro de chaves. Por exemplo:
T ria n g le t r i 3 =
new T ria n g le { Side2Length = 12, Side3Length = 17 } ;

Captulo 16

Utilizando indexadores
Neste captulo, voc vai aprender a:
Encapsular o acesso a um objeto com lgica de arrays utilizando indexadores.
Controlar o acesso de leitura a indexadores declarando mtodos de acesso

get.

Controlar o acesso de gravao a indexadores declarando os mtodos de acesso

set.

Criar interfaces que declaram indexadores.


Implementar indexadores em estruturas e classes que herdam de interfaces.

0 captulo anterior descreveu como implementar e usar as propriedades como um meio de fornecer
acesso controlado aos campos em uma classe. As propriedades so teis para espelhar campos que
contenham um valor nico. Mas os indexadores so inestimveis se voc quiser fornecer acesso aos
itens que contenham mltiplos valores utilizando uma sintaxe natural e familiar.

O que um indexador?
Considere um indexador um array inteligente quase como voc considera uma propriedade um cam
po inteligente. Enquanto uma propriedade encapsula um nico valor em uma classe, um indexador
encapsula um conjunto de valores. A sintaxe que voc utiliza para um indexador a mesma utiliza
da para um array.
A melhor maneira de entender indexadores trabalhar com um exemplo. Primeiro, examinaremos
um problema e veremos uma soluo que no utiliza os indexadores. Em seguida, trabalharemos no
mesmo problema e examinaremos uma soluo melhor que utiliza os indexadores. O problema diz
respeito aos inteiros, ou, mais precisamente, ao tipo int.

Um exemplo que no utiliza indexadores


Normalmente voc utiliza um tipo in t para armazenar um valor inteiro. Internamente, um in t arma
zena o valor como uma sequncia de 32 bits, em que cada bit pode ser 0 ou 1. Na maioria das vezes,
voc no se preocupa com essa representao binria interna; simplesmente utiliza um tipo in t como
um continer que armazena um valor inteiro. s vezes, porm, os programadores empregam o tipo
in t para outros propsitos: alguns programas usam um in t como um conjunto de flags binrios e
manipulam os bits individuais dentro de um int. Se voc for um hacker em C, antigo como eu, o que
vem a seguir deve ser bastante familiar!

348

Parte III

Criando componentes

O C# dispe de um conjunto de operadores para acessar e manipular os bits individuais em um int,


a saber:
Operador NOT (~ ) um operador unrio que executa um complemento de bit a bit. Por
exemplo, se pegar o valor de 8 bits 11001100 (204 em decimal) e aplicar o operador ~ a ele, o
resultado ser O O llO O ll [51 em decimal) - todos os ls no valor original tornam-se Os, e todos
os Os tornam-se ls.
Operador de deslocamento para a esquerda (< < ) um operador binrio que realiza
um deslocamento para a esquerda. A expresso 204 < < 2 retorna o valor 48 (em binrio, o
valor decimal 204 11001100 e, deslocando-o para a esquerda em duas casas, o resultado
OOllOOOO ou 48 em decimal). Os bits mais esquerda so descartados, e zeros so introduzi
dos direita. H um operador de deslocamento para a direita > > correspondente.
Operador OR ( |) um operador binrio que realiza uma operao OR bit a bit, retornando
um valor que contm um 1 em cada posio em que um dos operandos tem um 1. Por exemplo,
a expresso204 \24 tem o valor 220 (.204 11001100,24 00011000 e 220 11011100).
Operador AND (&) Executa uma operao AND bit a bit. AND semelhante ao operador OR
bit a bit, exceto por ele retornar um valor contendo um 1 em cada posio onde os dois ope
randos tm um 1. Portanto, 2 0 4 & 2 0 S (204 11001100,24 00011000 e 8 OOOOIOOO).
Operador XOR ( ~ ) Executa uma operao OR exclusiva de bit a bit, retornando um nmero
1 em cada bit onde h um nmero 1 em um ou outro operando, mas no em ambos (dois ls
resultam um 0 - essa a parte exclusiva do operador). Portanto204 * 24 .212 (11001100
~ 00011000 11010100).
Voc pode utilizar esses operadores em conjunto para calcular os valores dos bits individuais em
um int. Por exemplo, a seguinte expresso emprega os operadores de deslocamento para a esquerda
(< <) e o operador AND (<$) em nvel de bits para determinar se o sexto bit do in t chamado bits est
definido com 0 ou 1:
( b it s & (1

5 )) != 0

Vamos supor que a varivel bits contm o valor decimal 42. Em binrio, esse valor 00101010. O
valor decimal 1 00000001 em binrio, de modo que a expresso 1 < < 5 tem o valor 00100000. Em
notao binria, a expresso bits & (1 << 5) 00101010 & 00100000, e o valor dessa expresso
o binrio 00100000, que no zero. Se a varivel bits contiver o valor 65, ou 01000001 em binrio,
o valor da expresso 01000001 & 00100000, que d o resultado binrio de 00000000, ou zero.

Captulo 16

Utilizando indexadores

349

Esse um exemplo relativamente complexo, mas comum, quando comparado com a seguinte ex
presso, que utiliza o operador de atribuio composta <= para definir o bit na posio 4 como O:
b its &= ~ ( i

4)

De modo semelhante, para definir o bit na posio 4 como 1, voc pode utilizar um operador OR ( |)
em nvel de bits. A seguinte expresso complexa se baseia no operador de atribuio composta | =:
b its |= (1

4)

O problema em relao a esses exemplos o fato de que, embora funcionem, eles so extremamente
difceis de serem entendidos. So complicados e a soluo de baixo nvel: ela deixa de criar uma
abstrao do problema que ela soluciona.

O mesmo exemplo utilizando indexadores


Vamos deixar de lado a soluo fraca por um momento e parar para lembrar qual o problema.
Gostaramos de utilizar um in t no como um in t (inteiro), mas como um array de bits. Portanto, a
melhor maneira de resolver esse problema utilizar um in t como se ele fosse um array de bits! Em
outras palavras, o que queremos ser capazes de escrever para acessar o bit no ndice 6 da varivel
bits algo assim:
b its [6]

E para definir o bit no ndice 4 como true, gostaramos de escrever:


b its [4] = tru e

Infelizmente, voc no pode utilizar a notao de colchetes em um in t - ela s funciona em um array


ou em um tipo que se comporta como tal. Portanto, a soluo para o problema criar um novo tipo
que funcione como e seja utilizado como um array de variveis bool, mas que seja implementado por
meio de um int. Voc pode alcanar essa faanha definindo um indexador. Vamos chamar esse novo
tipo de In tBits. O In tB its conter um valor in t (inicializado no seu construtor), mas a ideia que
utilizaremos In tBits como um array de variveis bool.

350

Parte III

Criando componentes

Dica

O tipo

IntBits menor e mais leve, ento faz sentido cri-lo como uma estrutura em vez de

como uma classe.

s tru c t In t B i t s

{
p u b lic I n t B i t s ( i n t in i t i a lB it V a lu e )

{
b its = i n i t i a lB it V a lu e ;

}
// indexador a se r e s c r it o aqui
p riv a te in t b it s ;

}
Para definir o indexador, voc utiliza uma notao que um cruzamento entre uma propriedade e
um array. Introduza o indexador com a palavra-chave this, especifique o tipo do valor retornado
pelo indexador e o tipo do valor a ser utilizado como o ndice para o indexador, entre colchetes. O
indexador para a estrutura In tB its emprega um inteiro como tipo do argumento index e retorna um
booleano, como a seguir:
s t r u c t In t B i t s

{
p u b lic bool th is [ i n t index ]

{
get

{
retu rn ( b it s & (1

in d e x )) 1= 0;

}
set

{
i f (v a lu e ) // a t iv a o b it se value fo r verd ad eiro ; caso c o n tr rio , desativa-o
b its |= (1 in d e x );
e l se
b its &= ~(1 ind ex );

}
}

Observe as seguintes consideraes:


Um indexador no um mtodo - no h parnteses contendo um parmetro, mas h colchetes
que especificam um ndice. Esse ndice utilizado para especificar que elemento est sendo
acessado.
Todos os indexadores utilizam a palavra-chave this. Uma classe ou uma estrutura podem defi
nir no mximo um indexador, e seu nome sempre this.
Os indexadores contm mtodos de acesso get e set exatamente como as propriedades. Nesse
exemplo, os mtodos de acesso get e set contm complexas expresses bit a bit j discutidas.
O ndice especificado na declarao do indexador preenchido com o valor do ndice especifica
do quando o indexador chamado. Os mtodos de acesso get e set podem ler esse argumento
para determinar que elemento ser acessado.

Captulo 16

Utilizando indexadores

351

Nota Voc deve executar uma verificao de intervalo no valor do ndice para evitar que excees
inesperadas ocorram no cdigo do indexador.

Depois de declarar o indexador, voc pode utilizar uma varivel do tipo IntBits em vez de um int e
aplicar a notao de colchetes, como mostrado no prximo exemplo:
int adapted = 126;
// 126 tem a representao binria 1111110
IntBits bits = new IntBits(adapted);
bool peek = bits[6]; // recupera o valor bool do ndice 6; deve ser true (1)
bits[0] = true;
// configura o bit do ndice O como true (1)
bits[3] = false;
// configura o bit do ndice 3 como false (0)
// o valor int adaptado agora 1110111, ou 119 em decimal

Essa sintaxe certamente muito mais fcil de entender, pois captura direta e sucintamente a essn
cia do problema.

Entendendo os mtodos de acesso do indexador


Quando voc. l um indexador, o compilador traduz automaticamente seu cdigo com lgica de ar
rays em uma chamada para o mtodo de acessoget desse indexador. Considere o seguinte exemplobool peek = bits[6];

Essa instruo convertida em uma chamada ao mtodo de acesso get para bits, e o argumento
ndex definido como 6.
Da mesma maneira, se voc escrever para um indexador, o compilador traduz automaticamente seu
cdigo com lgica de array para uma chamada ao mtodo de acesso set desse indexador, configurando o argumento ndex como o valor especificado entre colchetes. Por exemplo:
bits[4] = true;

Essa instruo convertida em uma chamada ao mtodo de acesso set para bits onde ndex 4.
Assim como com propriedades normais, os dados que voc grava no indexador (nesse caso, true}
tornam-se disponveis dentro do mtodo de acesso set utilizando a palavra-chave value. O tipo de
value o mesmo do prprio indexador (nesse caso, bool}.
Tambm possvel utilizar um indexador em um contexto de leitura/gravao combinado. Nesse
caso, os mtodos de acesso get e set so utilizados. Examine a prxima instruo, que utiliza o operador XOR ( ~ ) para inverter o valor do bit 6 na varivel bits:
bits[6] A= true;

Esse cdigo automaticamente traduzido para:


bits[6] = bits[6] A true;

Esse cdigo funciona porque o indexador declara ambos os mtodos de acesso,get e set

Nota Voc pode declarar um indexador que contm apenas um mtodo de acesso get (um
indexador somente-leitura) ou apenas um mtodo de acesso set (um mtodo de acesso somente
gravao).

352

Parte l

Criando componentes

Comparando indexadores e arrays


Quando voc utiliza um indexador, a sintaxe deliberadamente muito parecida com a de um array.
Mas existem algumas diferenas importantes entre os indexadores e os arrays:
Os indexadores podem utilizar subscritos no numricos, como uma string, conforme mostrado
no seguinte exemplo. Os arrays s podem utilizar subscritos inteiros:
public int this [ string name ] { . . . } // OK

Dica Muitas classes de coleo, como Hashtable, que implementam uma pesquisa associativa baseada em pares chave/valor, implementam os indexadores como uma alternativa conveniente ao uso do mtodo Add para adicionar um novo valor e como uma alternativa a iterar
pela propriedade Values a fim de localizar um valor no seu cdigo. Por exemplo, em vez disso:
Hashtable ages = new HashtableQ;
ages.Add("John", 42);
voc pode utilizar isso:
Hashtable ages = new HashtableQ;
ages["3ohn"] = 42;

Os indexadores podem ser sobrecarregados (assim como os mtodos), enquanto os arrays no


podem:
public Name
this [ PhoneNumber number ]{...}
public PhoneNumber this [ Name name ]{...}

Os indexadores no podem ser utilizados como parmetros refou out, enquanto os elementos
do array podem:
IntBits bits;
// bits contm um indexador
Method(ref bits[l]); // erro de tempo de compilao

Propriedades, arrays e indexadores


Uma propriedade pode retornar um array, mas lembre-se de que os arrays so tipos-referncia, portanto, expor um array como uma propriedade permite sobrescrever acidentalmente
muitos dados. Examine a estrutura a seguir que expe uma propriedade array chamada Data
(dados):

struct Wrapper

private int[] data;


public int[] Data

Captulo 16

Utilizando indexadores

353

get { retu rn t h is .d a t a ; }
set { t h is .d a ta = va lu e ; }

}
}
Agora considere o cdigo a seguir que utiliza essa propriedade:
Wrapper wrap = new W rapperO ;
i n t [ ] myData = wrap.Data;
myData[0]++;
myData[1]++;

Isso parece bastante incuo. Entretanto, como os arrays so tipos-referncia, a varivel

myData referencia o mesmo objeto da varivel data privada na estrutura Wrapper. Todas as
alteraes que voc fizer nos elementos em myData sero feitas no array data; a instruo
myData[0]++ tem exatamente o mesmo efeito de data[0]+ + . Se essa no for a inteno,
voc deve utilizar o mtodo Clone nos mtodos de acesso get e sef da propriedade Data
para retornar uma cpia do array de dados ou criar uma cpia do valor que est sendo con
figurado, como mostrado (o mtodo

Clone retorna um objeto, ao qual voc deve aplicar um

casting para um array de inteiros).


s tru c t Wrapper

{
p riv a te i n t [ ] data;
p u b lic i n t [ ] Data

{
get { return th is .d a ta .C lo n e O as i n t [ ] ; }
se t { t h is .d a ta = v a lu e .C lo n e () as i n t [ ] ; }

}
}
Entretanto, essa abordagem pode tornar-se bem complicada e cara em termos de uso de
memria. Os indexadores fornecem uma soluo natural para esse problema - no exponha
o array inteiro como uma propriedade; simplesmente disponibilize seus elementos indivi
duais por meio de um indexador:
s tru c t Wrapper

{
p riv a te i n t [ ] data;
p u b lic in t th is [ i n t i ]

{
get { return t h i s . d a t a [ i ] ; }
set { t h i s . d a t a [ i ] = value; }

}
}
O cdigo a seguir utiliza o indexador de maneira semelhante propriedade mostrada ante
riormente:
Wrapper wrap = new W rapperO ;
i n t [ ] myData = new i n t [2 ];
myData[0] = w rap [0 ];

354

Parte III

Criando componentes

myData[l] = wrap[l];
myData[0]++;
myData[l]++;

MyData no tem qualquer efeito sobre o array


Wrapper. Se quiser realmente modificar os dados no objeto Wrapper, voc

Desta vez, incrementar os valores no array


original no objeto

deve escrever instrues como esta:


wrap[0]++;

muito mais claro e seguro!

Indexadores em interfaces
Voc pode declarar indexadores em uma interface. Para fazer isso, especifique a palavra-chave get,
a palavra-chave set, ou ambas, mas substitua o corpo do mtodo get ou set por um ponto e vrgula.
Toda classe ou estrutura que implementa a interface deve implementar os mtodos de acesso indexa
dores declarados na interface. Por exemplo:
interface IRawInt

{
bool this [ int index ] { get; set; }

}
struct Rawlnt : IRawInt

{
public bool this [ int index ]

{
get { ... }
set { ... }

Se voc implementar o indexador da interface em uma classe, poder declarar as implementaes do


indexador como virtuais. Isso permite que outras classes derivadas redefinam os mtodos de acesso
get e set. Por exemplo:
cl ass Rawlnt : IRawInt

{
p u bl i c v i r t u a l bool t h i s [ i n t index ]

{
get {
set {

}
}

...
...

}
}

Captulo 16

Utilizando indexadores

355

Voc tambm pode escolher implementar um indexador utilizando a sintaxe de implementao explcita abordada no Captulo 12, "Trabalhando com herana". Uma implementao explcita de um
indexador no pblica e no virtual (e, portanto, no pode ser redefinida). Por exemplo:
struct Rawlnt : IRawInt

bool IRawInt. thi s [ int ndex ]

get {
set {

Utilizando indexadores em um aplicativo Windows


No exerccio a seguir, voc examinar um aplicativo de catlogo telefnico simples e completar sua
implementao. Voc escrever dois indexadores na classe PhoneBook: um que aceita um parmetro
Name e retorna um PhoneNumber, e outro que aceita um parmetro PhoneNumber e retorna um
Name (as estruturas Name e PhoneNumber j foram escritas). Voc tambm precisar chamar esses
indexadores nos locais correios do programa.
Conhea o aplicativo
1. Inicie o Microsoft Visual Studio 2010 se ele ainda no estiver executando.
2. Abra o projeto Indexers, localizado na pasta \Microsoft PressWisual CSharp Step By Step\Chapterl\Indexers na sua pasta Documentos.
Esse um aplicativo Windows Presentation Foundation (WPF) que permite ao usurio procurar
o nmero de telefone e tambm localizar o nome de um contato que corresponde a um dado
nmero de telefone.
3. No menu Debug, dique em Start Without Debugging.
O projeto compila e executa. Aparece um formulrio, exibindo duas caixas de texto vazias rotuladas Name e PhoneNumber. O formulrio tambm contm trs botes: um para adicionar um
par nome/nmero de telefone a uma lista de nomes e nmeros de telefone armazenados pelo
aplicativo; um para localizar um nmero de telefone quando um nome dado; e um para localizar um nome quando dado um nmero de telefone. Atualmente, esses botes no fazem coisa
alguma. Sua tarefa concluir o aplicativo para que esses botes funcionem.
4. Feche o formulrio e retorne ao Visual Studio 2010.
5. Exiba o arquivo Name.cs na janela Code and Text Editor. Examine a estrutura Afame?. Seu propsito atuar como um armazenador de nomes.

356

Parte l

Criando componentes

O nome fornecido como uma string para o construtor. O nome pode ser recuperado utilizando
a propriedade de string somente-leitura chamada Text (os mtodos Equals e GetHashCode so
utilizados para comparar Names durante a pesquisa em um array de valores Name - voc pode
ignor-los por enquanto).
6. Exiba o arquivo PhoneNumber.es na janela Code and Text Editor e examine a estrutura PhoneNumber. Ela semelhante estrutura Name.
7. Exiba o arquivo PhoneBook.es na janela Code and Text Editor e examine a classe PhoneBook.
Essa classe contm dois arrays privados: um array de valores Name chamado names, e um
array de valores PhoneNumber chamado phoneNumbers. A classe PhoneBook tambm contm
um mtodo Add que adiciona um nmero de telefone e um nome agenda telefnica (phone
book). Esse mtodo chamado quando o usurio dica no boto Add no formulrio. O mtodo
enlargelfFull chamado por Add para verificar se os arrays esto cheios quando o usurio adiciona outra entrada. Esse mtodo cria dois novos arrays maiores, copia o contedo dos arrays
existentes para eles e ento descarta os arrays antigos.
Escreva os indexadores
1. No arquivo PhoneBook.es, adicione um indexador pblico somente-leitura classe PhoneBook,
como mostrado em negrito no cdigo a seguir. O indexador deve retornar Name e obter um item
PhoneNumber como seu ndice. Deixe o corpo do mtodo de acesso get em branco.
O indexador deve ser semelhante a este:
sealed class PhoneBook
public Name this [PhoneNumber number]
{

get

2. Implemente o mtodo de acesso get como mostrado em negrito no cdigo seguinte. A finalidade
do mtodo de acesso localizar o nome que corresponde ao nmero de telefone especificado.
Para fazer isso, chame o mtodo esttico IndexOf a classe Array. O mtodo IndexOf executa
uma pesquisa em um array, retornando o ndice do primeiro item no array correspondente ao
valor especificado. O primeiro argumento para IndexOf o array a ser pesquisado (phoneNumbers}. O segundo argumento paralndexOf o item que voc est pesquisando, fndexOfKtoma o
ndice inteiro do elemento se ele o encontrar; caso contrrio, fndexOfKtoma -l. Se o indexador
encontrar o nmero de telefone, ele deve retorn-lo; caso contrrio, ele deve retornar um valor
Name vazio (Observe que Name uma estrutura e, como tal, o construtor padro define seu
campo name privado com null.}

Captulo 16

Utilizando indexadores

357

sealed c la s s PhoneBook

{
p u b lic Name th is [PhoneNumber number]

{
get

{
int i = Array.IndexOf(this.phoneNumbers, number);
if (i != -1)

{
return this.names[i];

}
else

{
return new N a m e O ;

>
}
>

3. Adicione um segundo indexador pblico somente-leitura classe PhoneBook que retorna um


PhoneNumber e aceita um nico parmetro Name. Implemente esse indexador da mesma ma
neira que o primeiro (observe mais uma vez que PhoneNumber uma estrutura e, portanto,
sempre tem um construtor padro).
0 segundo indexador deve ser semelhante a este:
sealed c la s s PhoneBook

{
public PhoneNumber th is [Name name]

{
get

{
i n t i = Array.IndexOfCthis.names, name);
i f ( l != -1 )
return this.phoneNumbers[i];

}
else

{
return new PhoneNumber( ) ;

}
}
}

358

Parte III

Criando componentes

Observe tambm que esses indexadores sobrecarregados podem coexistir porque eles retornam
tipos diferentes, ou seja, suas assinaturas so diferentes. Se as estruturas Name t PhoneNumber
fossem substitudas por strings simples (que elas encapsulam), as sobrecargas teriam a mesma
assinatura, e a classe no compilaria.
4. No menu Build, clique em Build Solution. Corrija todos os erros de sintaxe e ento recompile, se
necessrio.

C ham e os indexadores
1. Exiba o arquivo MainWindow.xaml.es na janela Code and Text Editor e ento localize o mtodo
JindPhoneC/ick.
Esse mtodo chamado quando o primeiro boto Search by Name pressionado. Esse mtodo
estar vazio. Adicione o cdigo mostrado em negrito no exemplo a seguir para realizar estas
tarefas:
1.1. Ler o valor da propriedade Text a partir da caixa de texto name no formulrio. Isso uma
string contendo o nome de contato que o usurio digitou.
1.2. Se a string no estiver vazia, procure o nmero de telefone correspondente a esse nome no
PhoneBook, utilizando o indexador (observe que a classe }Aa.\nWindow contm um campo
PhoneBook privado chamado phoneBook). Construa um objeto Name a partir da string e
passe-o como o parmetro para o indexador PhoneBook.
1.3. Grave a propriedade Text da estrutura PhoneNumber retornada pelo indexador na caixa de
texto phoneNumber no formulrio.
0 mtodofindPhoneClick deve ser semelhante a este:
p riv a te void findPhoneC 1ick(object sender, RoutedEventArgs e)

{
s tring text = name.Text;
i f (!S trin g .Is N u llO rE m p ty (te x t))

{
Name personsName = new Name(text);
PhoneNumber personsPhoneNumber = this.phoneBook[personsName];
phoneNumber.Text = personsPhoneNumber.Text;

Dica

N ote o u so d o m to d o e sttico

IsNullOrEmpty de String para d e te rm in a r se um a string est

vazia ou co n t m um v a lo r n u lo . Esse o m to d o p re fe rid o para testa r se um a strin g co n t m um


valor. Ele reto rna

true se a string tiver um v a lo r n o n ulo, e false, ca so co n tr rio .

2. Localize o mtodoJindNam eClick no arquivo MainWindow.xaml.es. Ele est abaixo do mtodo


JindPhoneClick.
0 mtodoJindNam e_Click chamado quando o boto Search by Phone clicado. Esse mtodo
est atualmente vazio, assim voc precisa implement-lo desta maneira (o cdigo mostrado
em negrito no exemplo a seguir).
2.1. Leia o valor da propriedade Text a partir da caixa de texto phoneNumber no formulrio.
Isso uma string contendo o nmero de telefone que o usurio digitou.
2.2. Se a string no estiver vazia, procure o nome correspondente a esse nmero de telefone no
PhoneBook, utilizando o indexador.

Captulo 16

Utilizando indexadores

359

2.3. Grave a propriedade Text da estrutura Name retornada pelo indexador na caixa de texto
name no formulrio.
0 mtodo completo deve ser parecido com este:
p riv a te void find N am eC lick(object sender, RoutedEventArgs e)

{
string text = phoneNumber.Text;
i f (!String.IsNu110rE mpty(text))

{
PhoneNumber personsPhoneNumber = new PhoneNumber(text);
Name personsName = this.phoneBook[personsPhoneNumber];
name.Text = personsName.Text;

}
}
3. No menu Build, clique em Build Solution. Corrija qualquer erro que ocorra.

Execute o aplicativo
1. No menu Debug, clique em Start W ithout Debugging.
2. Digite seu nome e o nmero de telefone nas caixas de texto e ento clique emAdd.
Quando voc clica no boto Add, o mtodo Add armazena as informaes no catlogo de telefo
ne e limpa as caixas de texto para que elas estejam prontas para executar uma pesquisa.
3. Repita o passo 2 vrias vezes com alguns nomes e nmeros de telefone diferentes de modo que
a agenda telefnica contenha uma seleo de entradas. O aplicativo no realiza verificao de
nomes e nmeros de telefone digitados e voc pode inserir o mesmo nome e nmero de telefone
mais de uma vez. Para evitar confuses, certifique-se de fornecer nomes e nmeros de telefone
diferentes.
4. Digite um nome que voc utilizou no passo 2 na caixa de texto Name e ento clique em Search
byName.
O nmero de telefone que voc adicionou para esse contato no Passo 2 recuperado do catlogo
de telefone e exibido na caixa de texto PhoneNumber.
5. Digite um nmero de telefone para um diferente contato na caixa de texto Phone Number e en
to clique em Search by Phone.
O nome do contato recuperado do catlogo telefnico e exibido na caixa de texto Name.
6. Digite um nome que voc no inseriu no catlogo telefnico na caixa de texto Name e ento
clique em Search by Name.
Desta vez, a caixa de texto Phone Number est vazia, indicando que o nome no pde ser en
contrado no catlogo telefnico.
7. Feche o formulrio e retorne ao Visual Studio 2010.
Neste captulo, vimos como utilizar indexadores para fornecer acesso do tipo array aos dados em
uma classe. Voc aprendeu a criar indexadores que podem aceitar um ndice e retornar o respectivo
valor, ao utilizar uma lgica definida pelo mtodo de acessoget, e aprendeu a utilizar o mtodo de
acesso set com um ndice para preencher um valor em um indexador.
Se voc quiser seguir para o prximo captulo agora:
Mantenha o Visual Studio 2010 em execuo e v para o Captulo 17.
Se quiser sair do Visual Studio 2010:
No menu File, clique em Exit. Se vir uma caixa de dilogo Save, clique em Yes e salve o projeto.

360

Parte III

Criando componentes

Referncia rpida do Captulo 16


Para
Criar um indexador para uma classe ou estrutura

Faa isto
Declare o tipo do indexador, seguido pela palavra-chave

this e ento os argum entos do indexador entre colchetes.


O corpo do indexador pode conter um mtodo de acesso

get e/ou set. Por exemplo:


s tru c t Rawlnt

{
p u b lic bool t h is [ in t index ]

{
get { . . .
set { . . .

}
}

Definir um indexador em uma interface

Defina um indexador com as palavras-chave


Por exemplo:

get e/ou set.

in t e r fa c e IR aw In t

{
bool th is [ i n t index ] { get;

se t; }

}
Im plem entar um indexador de uma interface em uma
classe ou estrutura

Na classe ou estrutura que implem enta a interface, defina


o indexador e im plem ente os m todos de acesso. Por
exemplo:
s t r u c t Rawlnt : IRaw Int

{
p u b lic bool th is [ in t index

{
get { . . . }
set { . . . }

Im plem entar um indexador definido por uma interface


utilizando a im plem entao de interface explcita em uma
classe ou estrutura

Na classe ou estrutura que im plem enta a interface, especifique a interface, mas no especifique a acessibilidade
do indexador. Por exemplo:
s tru c t Rawlnt : IR aw Int

{
bool IR a w In t .th is [ in t index

{
get { . . .
se t { . . .

}
}

}
}

Captulo 17

Interrompendo o fluxo do
programa e tratando eventos
Neste captulo, voc vai aprender a:
Declarar um tipo delegate para criar uma abstrao de uma assinatura de mtodo.
Criar uma instncia de um delegate para referenciar um m todo especfico.
Chamar um m todo por meio de um delegate.
Definir uma expresso lambda para especificar o cdigo para um delegate.
Declarar um cam po de evento.
M anipular um evento utilizando um delegate.
Disparar um evento.

Grande parte do cdigo escrito nos vrios exerccios deste livro pressupe que as instrues so
executadas sequencialmente. Embora esse seja o cenrio comum, voc vai perceber que algumas
vezes necessrio interromper o fluxo atual da execuo e executar outra tarefa mais importante.
Ouando a tarefa concluda, o programa pode continuar a partir de onde foi interrompido. 0 exem
plo clssico desse estilo de programa o formulrio Windows Presentation Foundation (WPF). Um
formulrio WPF exibe controles, como botes e caixas de texto. Ao clicar em um boto ou digitar em
uma caixa de texto, voc espera que o formulrio responda imediatamente, mas o aplicativo tem
de parar temporariamente o que est fazendo e manipular a sua entrada. Esse estilo de operao
se aplica no apenas s interfaces grficas com o usurio, mas a qualquer aplicao em que uma
operao precisa ser executada com urgncia - desligar o reator de uma usina nuclear se estiver
superaquecendo, por exemplo.
Para manipular esse tipo de aplicao, o tempo de execuo tem de fornecer duas coisas: um meio
de indicar que algo urgente est acontecendo e uma maneira de especificar um cdigo que deve ser
executado quando isso acontecer. Essa a finalidade de eventos e delegates.
Comearemos examinando os delegates.

Declarando e utilizando delegates


Um delegate um ponteiro para um mtodo. Voc pode chamar um mtodo por meio de um dele
gate ao especificar o nome desse delegate. Ao invocar um delegate, na verdade o runtime executa
o mtodo que o delegate referencia. Voc pode alterar dinamicamente o mtodo que um delegate
referencia de modo que o cdigo que chama um delegate possa realmente executar um mtodo di
ferente a cada vez. A melhor maneira de entender os delegates v-los em ao, portanto, vamos
acompanhar um exemplo.

362

Parte III

Criando componentes

O cenrio da fbrica automatizada


Suponha que voc esteja escrevendo sistemas de controle para uma fbrica automatizada. Ela con
tm um grande nmero de diferentes mquinas, cada uma delas executando tarefas distintas na pro
duo de artigos manufaturados - moldagem e dobradura de chapas de metal, soldagem das chapas,
pintura das chapas, etc. As mquinas foram construdas e instaladas por fornecedores especialistas,
e so controladas por computador e cada fornecedor disponibilizou um conjunto de APls para con
trolar sua mquina. Sua tarefa integrar os diferentes sistemas utilizados pelas mquinas em um
nico programa de controle. Um aspecto no qual voc decidiu se concentrar o de fornecer um meio
de desligar todas as mquinas, rapidamente, se necessrio!

Cada mquina tem seu processo controlado por computador (e API) para ser desligada de modo se
guro. Estes esto resumidos assim:
S topFold ingO ;
Fini shWeldingO ;
Pa in tO ffO ;

// Mquina de dobra e modelagem


// Mquina de solda
// Mquina de pintura

Implementando a fbrica sem utilizar delegates


Uma abordagem simples para implementar a funcionalidade de desligamento no programa de con
trole mostrada a seguir:
class Controller

{
// Campos que representam as diferentes mquinas
private FoldingMachine folder;
private WeldingMachine welder;
private PaintingMachine painter;
public void ShutDownO

{
f o ld e r.S to p F o ld in g O ;
welder.FinishWelding();
pai nter.Pai ntOff( ) ;
}
}

Captulo 17

Interrompendo o fluxo do programa e tratando eventos

363

Embora esse enfoque funcione, ele no muito extensvel ou flexvel. Se a fbrica comprar uma
nova mquina, voc precisar modificar esse cdigo; a classe Controller e um cdigo para gerenciar
as mquinas esto fortemente acoplados.

Implementando a fbrica utilizando um delegate


Embora os nomes de cada mtodo sejam diferentes, todos tm a mesma forma : no recebem par
metro algum e no retornam um valor (veremos o que acontece se esse no for o caso mais adiante,
portanto, tenha um pouco de pacincia comigo!). O formato geral de cada mtodo :
void nomeDoMtodo( ) ;

Aqui um delegate til, pois um delegate que corresponda a essa forma pode ser utilizado para
referenciar qualquer um dos mtodos de desligamento de mquina. Voc declara um delegate assim:
delegate void StopM achineryD elegateO ;

Note as seguintes questes:


Utilize a palavra-chave delegate ao declarar um delegate.
Um delegate define a forma dos mtodos que ele pode referenciar. Voc especifica o tipo de
retorno (void, neste exemplo), um nome para o delegate (stopMachineryDelegate) e todos os
parmetros (no h parmetro algum neste caso).
Aps ter definido o delegate, voc pode criar uma instncia e faz-la referenciar um mtodo corres
pondente utilizando o operador de atribuio composto + = . Voc pode fazer isso no construtor da
classe Controller como a seguir:
c la s s C o n tro lle r

{
delegate void StopM achineryD elegateO ;
p riv a te stopMachineryDelegate stopMachinery; // uma in s t n c ia do delegate
p u b lic C o n tro U e rO

{
t h i s . stopMachinery += fo ld e r.S to p F o ld in g ;

Demora um pouco para se acostumar com essa sintaxe. Voc adiciona o mtodo ao delegate, porm,
no est realmente chamando-o. O operador + sobrecarregado a fim de ter esse novo significado
quando utilizado com delegates (voc aprender mais sobre sobrecarga de operadores no Captulo
21, Sobrecarga de operadores). Observe que voc simplesmente especifica o nome do mtodo e no
inclui parnteses nem parmetros.
seguro utilizar o operador += em um delegate no inicializado. Ele ser inicializado automa
ticamente. Como alternativa, voc tambm pode utilizar a palavra-chave new para inicializar um
delegate explicitamente com um mtodo especfico, como este:
t h is .stopMachinery = new stopMachineryDe1egate(folder.StopFolding);

364

Parte III

Criando componentes

Voc pode chamar o mtodo invocando o delegate, cor~o a seguir:


p u b lic void ShutDownO

{
th i s . stopMachi n eryO ;

}
Utilize a mesma sintaxe para chamar um delegate que voc utiliza para fazer uma chamada de mto
do. Se o mtodo que o delegate referencia receber qualquer parmetro, voc deve especific-lo nesse
momento, entre parnteses.

A principal vantagem de utilizar um delegate que ele pode referenciar mais de um mtodo; basta
utilizar o operador + = para adicionar os mtodos ao delegate, como mostrado a seguir:
p u b lic C o n tro lle r O

{
t h i s . st op Mac h i n e r y += f o l d e r . S t o p F o l d i n g ;
t h i s . s t op Mac h i n e r y += w e l d e r . F i n i s h We l d i ng;
t h i s . s t o p Mac h i ner y += p a i n t e r . P a i n t O f f ;

}
Ativar this.stopMachineryO no mtodo Shutdown da classe Controller automaticamente faz um m
todo de cada vez ser chamado. O mtodo Shutdown no precisa saber quantas mquinas existem ou
quais so os nomes dos mtodos.
Voc pode remover um mtodo de um delegate utilizando o operador de atribuio composto -=:
t h i s . stopMachinery -= f o ld e r .StopFolding;

O esquema atual adiciona os mtodos de mquina ao delegate no construtor Controller. Para tornar
a classe Controller totalmente independente das vrias mquinas, voc precisa transformar stopMachineryDelegate em um tipo pblico e fornecer um meio de permitir que classes fora de Controller
adicionem mtodos ao delegate. Voc tem diversas opes:
Tornar a varivel do delegate, stopMachinery, pblica:
p u b lic stopMachineryDelegate stopMachinery;

Manter a varivel do delegate stopMachinery privada, mas fornecer uma propriedade de leitura
e gravao para proporcionar-lhe acesso:
p u b lic delegate void stopM ach ineryD eleg ateO ;
p u b lic stopMachineryDelegate StopMachinery

{
get

Captulo 17

Interrompendo o fluxo do programa e tratando eventos

365

return this.stopMachinery;

set

this.stopMachinery = value;

Fornecer um encapsulamento completo implementando os mtodos Add e Remove separados. O


mtodo Add recebe um mtodo como um parmetro e o adiciona ao delegate, enquanto o mtodo Remove remove o mtodo especificado do delegate (observe que voc especifica um mtodo
como um parmetro utilizando um tipo delegate):
public void Add(stopMachineryDelegate stopMethod)
{
this.stopMachinery += stopMethod;
}
public void Remove(stopMachineryDelegate stopMethod)
{
this.stopMachinery -= stopMethod;
}

Se voc for um purista em orientao a objetos, provavelmente optar pela abordagem Add/Remove.
Mas as outras alternativas so mais utilizadas, razo pela qual elas so apresentadas aqui.
Qualquer que seja a tcnica escolhida, voc deve remover o cdigo que adiciona os mtodos de mquina ao delegate a partir do construtor Controller. Voc pode ento instanciar um Controller e objetos
representando as outras mquinas como a seguir (este exemplo utiliza a abordagem Add/Remove):
Controller control = new ControllerQ;
FoldingMachine folder = new FoldingMachineQ;
WeldingMachine welder = new WeldingMachineO;
PaintingMachine painter = new PaintingMachineQ;
control.Add(folder.StopFolding);
control .Add(welder.FinishWelding);
control.Add(painter.PaintOff);
control .ShutDownQ;

Utilizando delegates
No exerccio a seguir, voc concluir um aplicativo que implementa um relgio internacional.
O aplicativo contm um formulrio WPF que exibe a hora local, assim como a hora atual em
Londres, Nova York e Tquio. Cada exibio controlada por um objeto relgio. Cada relgio
implementado de modo diferente, para simular o cenrio anterior de controle de um conjunto de
mquinas independentes que funcionam em uma fbrica. Entretanto, cada relgio conta com um

366

Parte III

Criando componentes

par de mtodos que permitem iniciar e parar o relgio. Quando voc inicia um relgio, a hora em
sua exibio atualizada a cada segundo. Quando voc para um relgio, a exibio no mais
atualizada. Voc adicionar uma funcionalidade ao aplicativo, que inicia e para os relgios, por
meio de delegates.

Conclua o a p licativo W o rld Clock


1. Inicie o Microsoft Visual Studio 2010 se ele ainda no estiver executando.
2. Abra o projeto Clock na pasta \Microsoft PressWisual CSharp Step By Step\Chapter 17\Clock na
sua pasta Documentos.
3. No menu Debug, clique em Start W ithout Debugging.
O projeto compilado e executado. Aparece um formulrio que exibe a hora local, assim como a
hora em Londres, Nova York e Tquio. O relgio exibe as horas atuais como 00:00:00".
4. Clique em Start para iniciar os relgios.
Nada acontece. O mtodo Start ainda no foi escrito, e o boto Stop est desabilitado, por pa
dro. Sua tarefa implementar o cdigo associado a esses botes.
5. Feche o formulrio e retorne ao ambiente do Visual Studio 2010.
6. Examine a lista de arquivos no Solution Explorer. O projeto contm alguns arquivos, como o
AmericanClock.cs, EuropeanClock.cs, JapaneseClock.cs e o LocalClock.cs, com as classes que
implementam os diversos relgios. No se preocupe ainda com o funcionamento desses relgios
(embora seja recomendvel que voc examine o cdigo). Entretanto, o mais importante conhe
cer os nomes dos mtodos que iniciam e param cada tipo de relgio. A lista a seguir apresenta
um resumo por tipo de relgio (todos eles so semelhantes):
O mtodo que inicia chamado de StartAm ericanClock, e o mtodo que
interrompe o StopAmericanClock. Nenhum desses mtodos aceita parmetros, e ambos
tm um tipo de retorno void.

AmericanClock.

O mtodo que inicia chamado de StartEuropeanClock, e o mtodo que


interrompe o StopEuropeanClock. Nenhum desses mtodos aceita parmetros, e ambos
tm um tipo de retorno void.

EuropeanClock.

O mtodo que inicia chamado de StartJapaneseClock, e o mtodo que


interrompe o StopJapaneseClock. Nenhum desses mtodos aceita parmetros, e ambos
tm um tipo de retorno void.

JapaneseClock.

LocalClock. O mtodo que inicia chamado de StartLocalClock, e o mtodo que inter


rompe o StopLocalClock. Nenhum desses mtodos aceita parmetros, e ambos tm um
tipo de retorno void.

Captulo 17

Interrompendo o fluxo do programa e tratando eventos

367

7. Abra o arquivo ClockWindow.xami.es na janela Code and Text Editor. Esse o cdigo do formu
lrio WPF, parecido com o seguinte:
p ub lic p a r tia ! c la s s ClockWindow : Window

{
p riv a te
p riv a te
p riv a te
p riv a te

LocalClock lo c a lC lo c k = n u ll ;
EuropeanClock londonClock = n u l l ;
AmericanClock newYorkClock = n u ll;
lapaneseClock tokyoClock = n u ll;

p u b lic ClockWindowO

{
In itia liz e C o m p o n e n tO ;
lo ca lC lo c k = new L o c a lC lo c k (lo c a lT im e D is p la y );
londonClock = new EuropeanClock(londonTim eD isplay);
newYorkClock = new Am ericanClock(newYorkTim eDisplay);
tokyoClock = new Jap an eseClock(tokyoT im eD isplay);

r iv a te void s t a r t C lic k (o b je c t sender, RoutedEventArgs e)

r iv a te void s to p C lic k (o b je c t sender, RoutedEventArgs e)

}
Os quatro campos privados representam os quatro objetos relgio, utilizados pelo aplicativo. 0
construtor inicializa cada um desses objetos relgio. Em cada caso, o parmetro para o constru
tor especifica o campo de texto no formulrio que o objeto relgio atualizar quando comear a
funcionar. 0 mtodo startClick executado quando o usurio clica no boto Start no formul
rio, e seu objetivo iniciar cada relgio. De modo semelhante, o mtodo stopClick executado
quando o usurio clica no boto Stop e sua finalidade parar os relgios. possvel constatar
que esses dois mtodos esto atualmente vazios.
Uma abordagem simplria seria to somente chamar os mtodos adequados que iniciam cada
relgio no mtodo startClick, e os mtodos que param cada relgio, no mtodo stopClick. Entre
tanto, como vimos anteriormente neste captulo, essa abordagem associa o aplicativo ao modo
como cada relgio implementado, e no muito extensvel. Em vez disso, voc criar um
objeto Controller para iniciar e parar os relgios, e utilizar um par de delegates para especificar
os mtodos que o objeto Controller deve usar.

368

Parte III

Criando componentes

8. No menu Project, clique em Add Class. Na caixa de dilogo Add New Item - Delegates, na caixa
de texto Name, digite Controller.es e clique em Add.
0 Visual Studio cria a classe Controller e exibe o arquivo Controller.es na janela Code and Text
Editor.
9. Na classe Controller, adicione os tipos delegate, startClocksDelegate e stopClocksDelegate, como
mostrado em negrito a seguir. Esses tipos delegate podem fazer referncia a mtodos que no
aceitam parmetros e que tm um tipo de retorno void. Essa assinatura e o tipo de retorno cor
respondem a vrios mtodos de iniciar e parar das classes de relgio.
c la s s C o n tro lle r

{
public delegate void StartClocksDelegateO;
public delegate void StopClocksDelegateO;

}
10. Adicione dois delegates pblicos, chamados StartClocks e StopClocks, classe Controller, por
meio desses tipos delegate, como mostrado em negrito a seguir.
c la s s C o n tro lle r

{
p u b lic delegate void Sta rtC lo c k sD e le g a te O ;
p u b lic delegate void StopC locksD eleg ateO ;

public StartClocksDelegate StartClocks;


public StopClocksDelegate StopClocks;

}
11. Adicione o mtodo StartClocksRunning classe Controller. Esse mtodo apenas chama o delega
te StartClocks. Todos os mtodos associados a esse delegate sero executados.
c la s s C o n tro lle r

{
public void StartClocksRunningO

{
t h i s . S t a r t C lo c k s O ;

}
}
12. Adicione o mtodo StopClocksRunning classe Controller. Esse mtodo semelhante ao
Start-ClocksRunning, exceto pelo fato de que ele chama o delegate StopClocks.
c la s s C o n tro lle r

{
public void StopClocksRunningO

{
this.StopClocks O ;

}
}
13. Retorne ao arquivo ClockWindow.xaml.cs na janela Code and Text Editor. Adicione uma varivel
privada Controller, chamada controller, classe ClockWindow e instancie essa varivel, como a
seguir;
p u b lic p a r t ia l c la s s ClockWindow : Window

Captulo 17

Interrompendo o fluxo do programa e tratando eventos

369

private Controller co ntro lle r = new C o n t r o lle r Q ;

14. No construtor ClockWindow, adicione as instrues mostradas em negrito a seguir. Essas instru
es adicionam os mtodos que iniciam e param os relgios aos delegates expostos pela classe
Controller.
p u b lic ClockWindow()

{
In itia liz e C o m p o n e n tO ;
lo c a lC lo c k = new L o c a lC lo c k (lo c a lT im e O is p la y );
londonClock = new EuropeanClock(londonTim eD isplay);
newYorkClock = new Am ericanClock(newYorkTim eDisplay);
tokyoClock = new Jap an eseC lock(tokyoT im eD isplay);

co ntr oller.S ta rtC lo cks


c o n tr o lle r.S ta r td o c k s
controller.S tartClo cks
c o n tr o lle r.S ta r td o c k s
co n tro lle r. StopClocks
controller.StopClocks
controller.StopClocks
controller.StopClocks

+=
+=
+=
+=
+=
+=
+=
+=

loc a lC loc k.StartLocalClock;


londonClock.StartEuropeanClock;
newYorkClock.StartAmericanClock;
tokyoClock.StartJapaneseClock;

localC lock.StopLocaldock;
londonClock.StopEuropeanClock;
newYorkClock.StopAmericanClock;
tokyoClock.StopJapaneseClock;

}
15. No mtodo startC/ick, chame o delegate StartClocks do objeto controller, desabilite o boto Start
e habilite o boto Stop, como a seguir:
p riv a te void s t a r t C lic k (o b je c t sender, RoutedEventArgs e)

{
c o n tr o lle r .S t a r t C lo c k s ( );
start.IsEnabled = false;
stop.IsEnabled = true;

}
Convm lembrar que, quando voc chama um delegate, todos os mtodos a ele associados so
executados. Nesse caso, a chamada a StartClocks chamar o mtodo que inicia cada relgio.
Vrios controles WPF expem a propriedade booleana IsEnabled. Por padro, os controles so
habilitados quando voc os adiciona ao formulrio. Isso significa que voc pode clicar neles e
eles faro alguma coisa. Entretanto, neste aplicativo, a propriedade IsEnabled do boto Stop
est definida comfa ls e porque no faz sentido parar os relgios antes de inici-los. As duas
ltimas instrues nesse mtodo desabilitam o boto Start e habilitam o boto Stop.
16. No mtodo stopClick, chame o delegate StopClocks do objeto controller, habilite o boto Start e
desabilite o boto Stop:
p riv a te void s to p C lic k (o b je c t sender, RoutedEventArgs e)

{
c o n tro lle r.S to p C lo ck sO ;
start.IsEnabled = true;
stop.IsEnabled = false;

>
17. No menu Debug, clique em Start W ithout Debugging.

370

Parte III

Criando componentes

18. No formulrio WPF, clique em Start.


O formulrio exibe as horas corretas e atualiza a cada segundo, como na imagem a seguir. (Es
tou no Reino Unido, de modo que minha hora local a mesma de Londres.)

l 3 Times Around the Worid

Local Time

10: 35:50

London

10: 35: 50

New York

05: 35: 50

Tokyo

19: 35: 50
|

Start

!_slop

19. Clique em Stop.


A exibio para de atualizar os relgios.
20. Clique em Start novamente.
A exibio retoma o processamento, corrige a hora e atualiza a hora a cada segundo.
21. Feche o formulrio e volte ao Visual Studio 2010.

Expresses lambda e delegates


Todos os exemplos para adicionar um mtodo a um delegate vistos at aqui utilizam o nome do
mtodo. Por exemplo, retornando ao cenrio da fbrica automatizada mostrado anteriormente, voc
adiciona o mtodo StopFolding do objetofolder ao delegate stopMachinery, desta maneira:
th is.sto p M ach in ery += fo i d e r. Sto p Fo ld in g ;

Essa abordagem muito til se houver um mtodo conveniente que corresponda assinatura do
delegate, mas e se esse no for o caso? Suponha que o mtodo StopFolding tivesse na verdade a
assinatura a seguir:
void S to p F o ld in g (in t shutDownTime); // D eslig a pelo nmero esp e c ifica d o de segundos

Essa assinatura agora diferente daquela dos mtodos FinishW elding e Pain tO fft, desse modo, voc
no pode utilizar o mesmo delegate para manipular os trs mtodos. Ento, o que possvel fazer?

Captulo 17

Interrompendo o fluxo do programa e tratando eventos

371

Criando um mtodo adaptador


Uma maneira de contornar esse problema criar outro mtodo que chame StopFolding, mas que no
tenha parmetros, como mostrado:
void F in ish Foi dingC)

{
fo ld e r.S to p F o ld in g (O ); // D eslig a imediatamente

}
Voc pode ento adicionar o mtodo FinishFolding ao delegate stopMachinery em vez do mtodo
StopFolding, utilizando a mesma sintaxe:
th is.sto p M ach in ery += fo ld e r.F in is h F o ld in g ;

Quando o delegate stopMachinery invocado, ele chama FinishFolding que, por sua vez, chama o
mtodo StopFolding, passando no parmetro 0.

Em muitos casos, mtodos adaptadores como esse so pequenos, e fcil perd-los em um mar de
mtodos, especialmente em uma classe grande. Alm disso, seu uso principal na adaptao do m
todo StopFolding para ser utilizado pelo delegate, sendo pouco provvel que seja chamado em outro
momento. O C# fornece expresses lambda para situaes como essa.

Utilizando uma expresso lambda como um adaptador


Uma expresso lambda uma expresso que retorna um mtodo, isso parece bastante estranho
porque a maioria das expresses que voc encontrou at agora no C# realmente retorna um valor. Se
voc conhece linguagens de programao funcionais como Haskell, esse conceito deve ser fcil. Para
os demais, no se assustem: expresses lambda no so particularmente complicadas e, depois de se
acostumar com a sintaxe, voc ver que so bem teis.
Vimos no Captulo 3, Escrevendo mtodos e aplicando escopo , que um mtodo tpico consiste em
quatro elementos: um tipo de retorno, um nome de mtodo, uma lista de parmetros e um corpo de
mtodo. Uma expresso lambda contm dois desses elementos: uma lista de parmetros e um corpo
de mtodo. As expresses lambda no definem um nome de mtodo; e o tipo de retorno (se houver
algum) inferido do contexto em que a expresso lambda utilizada. No mtodo StopFolding da
classe FoldingMachine, o problema que esse mtodo agora recebe um parmetro, portanto, voc
precisa criar um adaptador que no receba parmetro algum e que possa ser adicionado ao delegate
stopMachinery. Voc pode utilizar a seguinte instruo para fazer isso:
th is.sto p M ach in ery += ( ( ) => { fo ld e r.S to p F o ld in g (0) ; } ) ;

372

Parte III

Criando componentes

Todo o texto direita do operador + = uma expresso lambda, que define o mtodo a ser adiciona
do ao delegate stopMachinery. Ele tem os seguintes itens sintticos:
Uma lista de parmetros includa entre parnteses. Como ocorre com um mtodo normal, mes
mo que o mtodo que voc esteja definindo (como no exemplo anterior) no receba parmetro
algum, voc deve adicionar os parnteses.
O operador =>, que indica ao compilador C# que isso uma expresso lambda.
O corpo do mtodo. O exemplo mostrado aqui muito simples, ele contm uma nica instru
o. Mas uma expresso lambda pode conter mltiplas instrues, e voc pode format-la da
maneira que achar mais legvel. Apenas lembre-se de adicionar um ponto e vrgula aps cada
instruo como voc faria em um mtodo comum.
Estritamente falando, o corpo de uma expresso lambda pode ser um corpo de mtodo contendo
mltiplas instrues ou ele pode ter uma nica expresso. Se o corpo de uma expresso lambda s
contm uma nica expresso, voc pode omitir as chaves e o ponto e vrgula (mas um ponto e vrgu
la ainda necessrio para completar a instruo inteira), desta maneira:
th is.sto p M ach in ery += ( ) => fo ld e r.S to p F o ld in g (O );

Ao invocar o delegate stopMachinery, ele executar o cdigo definido pela expresso lambda.

A forma das expresses lambda


As expresses lambda podem assumir algumas formas sutilmente diferentes. Essas expresses eram
originalmente parte de uma notao matemtica chamada Clculo Lambda que fornece uma notao
para descrever funes (voc pode pensar em uma funo como um mtodo que retorna um valor).
Embora a linguagem C# tenha estendido a sintaxe e a semntica do Clculo Lambda na implemen
tao de expresses lambda, boa parte dos princpios originais ainda se aplica. Veja alguns exemplos
que mostram as diferentes formas da expresso lambda disponveis no C#:
x => x * x

// Uma expresso simples que retorna o quadrado do seu parmetro


// 0 tip o do parmetro x in f e r id o do contexto.

x => { return x * x ; } // Semanticamente, a mesma expresso a n te rio r


// mas u tiliz a n d o um bloco de in s tru o C# como
// um corpo em vez de uma expresso simples
( i n t x) => x / 2

// Uma expresso sim ples que reto rna o v a lo r do


// parmetro d iv id id o por 2
// 0 tip o do parmetro x declarado ex p licita m en te.

( ) => fo ld e r.S to p F o ld in g (O ) // Chamando um mtodo


// A expresso no a c e it a parmetro algum.
// A expresso pode ou no
// reto rn ar um v a lo r .

Captulo 17

Interrompendo o fluxo do programa e tratando eventos

373

(x, y ) => { x++; retu rn x / y; } // M ltip lo s parmetros; o compilador


// in f e r e os tip o s dos parmetros.
// 0 parmetro x passado por v a lo r , assim
// o e f e it o da operao ++
// lo c a l expresso.
( r e f in t x, in t y ) { x++; return x / y; } // M ltip lo s parmetros
// com tip o s e x p lc ito s
// 0 parmetro x passado por
// re fe r n c ia , portanto, o e fe it o da
// operao ++ permanente.

Resumindo, seguem alguns recursos das expresses lambda que voc precisa conhecer:
Se uma expresso lambda receber parmetros, voc ir especific-los nos parnteses esquerda
do operador =>. Voc pode omitir os tipos dos parmetros, e o compilador C# ir inferir os
tipos a partir do contexto da expresso lambda. Voc pode passar parmetros por referncia (uti
lizando a palavra-chave ref] se quiser que a expresso lambda seja capaz de alterar os valores
alm de localmente, mas isso no recomendvel.
As expresses lambda podem retornar valores, mas o tipo de retorno deve corresponder ao tipo
do delegate ao qual eles esto sendo adicionados.
O corpo de uma expresso lambda pode ser uma expresso simples ou um bloco de cdigo C#
composto de mltiplas instrues, chamadas de mtodo, definies de variveis e outros itens
de cdigo.
As variveis definidas em um mtodo de expresso lambda saem do escopo quando o mtodo
termina.
Uma expresso lambda pode acessar e modificar todas as variveis fora da expresso lambda
que esto em escopo quando a expresso lambda definida. Seja cuidadoso com esse recurso!
Voc aprender mais sobre expresses lambda e ver outros exemplos que recebem parmetros e
retornam valores em captulos posteriores deste livro.

Expresses lambda e mtodos annimos


As expresses lambda so um novo recurso adicionado linguagem C# na verso 3.0. O C#
2.0 introduziu os mtodos annimos que podem realizar uma tarefa semelhante, mas que
no so to flexveis. Os mtodos annimos foram adicionados principalmente para que
voc possa definir delegates sem criar um mtodo nomeado; voc simplesmente fornece a
definio do corpo de mtodo em vez do nome de mtodo, assim:
th is.sto p M ach in ery += delegate { fo ld e r.S to p F o ld in g (O ); } ;

Voc pode passar um mtodo annimo como um parmetro no lugar de um delegate, como
mostrado aqui:
control.AddCdelegate { fo ld e r.S to p F o ld in g (O ); } ) ;

374

Parte III

Criando componentes

Observe que sempre que voc introduz um mtodo annimo, preciso prefix-lo com a
palavra-chave

delegate. Todos os parmetros necessrios so especificados entre chaves se


delegate. Por exemplo:

guindo a palavra-chave

c o n tro l.A d d (d e le g a te (in t paraml, s trin g param2){ /* cdigo que usa paraml e param2

*/
Depois de se acostumar com elas, voc notar que as expresses lambda fornecem uma
sintaxe mais sucinta do que aquela dos mtodos annimos e permeiam muitos dos aspectos
mais avanados do C#, como veremos mais adiante neste livro. Falando em termos gerais,
voc deve utilizar expresses lambda em vez de mtodos annimos no seu cdigo.

Ativando notificaes por meio de eventos


Voc viu como declarar um tipo delegate, chamar um delegate e criar instncias de delegate. Mas
isso apenas metade da histria. Embora com delegates voc possa invocar qualquer mtodo indi
retamente, voc ainda tem de invocar o delegate explicitamente. Em muitos casos, seria til que o
delegate executasse automaticamente quando algo significativo ocorresse. Por exemplo, no cenrio
da fbrica automatizada, pode ser vital conseguir invocar o delegate stopMachinery e interromper o
equipamento se o sistema detectar o superaquecimento de uma mquina.
O .NET Framework fornece eventos, que voc pode usar para definir e capturar aes significativas
e providenciar para que um delegate seja chamado para tratar a situao. Muitas classes no .NET
Framework expem eventos. A maioria dos controles que voc pode colocar em um formulrio WPF
e a prpria classe Windows utilizam eventos que permitem executar um cdigo quando, por exem
plo, o usurio clica em um boto ou digita algo em um campo. Voc tambm pode declarar seus
prprios eventos.

Declarando um evento
Voc declara um evento em uma classe projetada para atuar como uma origem de eventos. Uma
origem de eventos (event source) normalmente uma classe que monitora seu ambiente e dispara
um evento quando algo significativo acontece. Na fbrica automatizada, uma origem de eventos
pode ser uma classe que monitora a temperatura de cada mquina. A classe de monitoramento de
temperatura dispararia um evento superaquecimento da mquina se detectasse que uma mquina
excedeu seu limite de radiao trmica (isto , esquentou demais!). Um evento mantm uma lista
de mtodos a serem chamados quando ele disparado. Esses mtodos so s vezes chamados de
subscribers (assinantes), devendo ser preparados para manipular o superaquecimento da mquina
e executar a ao corretiva necessria: desligar as mquinas.

Captulo 17

Interrompendo o fluxo do programa e tratando eventos

375

Voc declara um evento de maneira semelhante a como declara um campo. Mas, como os eventos
sero utilizados com delegates, o tipo de um evento deve ser um delegate, e voc deve iniciar a decla
rao com a palavra-chave event. Empregue a sintaxe a seguir para declarar um evento:
evento nomeDoTipoDoDelegate nomeDoEvento

Como exemplo, segue o delegate StopMachineryDelegate da fbrica automatizada. Ele foi realocado
em uma nova classe chamada TemperatureMonitor, que fornece uma interface para as vrias pes
quisas que monitoram a temperatura do equipamento (esse um local mais lgico para o evento do
que a classe Controllery.
c la s s TemperatureMonitor

{
p u b lic delegate void StopM ach in eryD eleg ateQ ;

}
Voc pode definir o evento MachineOverheating ( mquina superaquecendo"), que invocar o Stop
M achineryDelegate, como a seguir:
c la s s TemperatureMonitor

{
p u b lic delegate void StopM achineryD eleg ateO ;
p u b lic event StopMachineryDelegate MachineOverheating;

}
A lgica (no mostrada) da classe TemperatureMonitor dispara o evento MachineOverheating, se
necessrio. Veremos como disparar um evento na prxima seo, Disparando um evento . Alm
disso, voc adiciona mtodos a um evento (um processo conhecido como assinatura ou inscrio
para o evento) em vez de adicion-los ao delegate em que o evento est baseado. Examinaremos esse
aspecto dos eventos a seguir.

Fazendo a inscrio em um evento


Como os delegates, os eventos tornam-se disponveis para uso com um operador + =. Voc se inscre
ve em um evento utilizando esse operador. Na fbrica automatizada, o software que controla cada
mquina pode determinar que os mtodos que desligam as mquinas sejam chamados quando o
evento MachineOverheating for disparado, como mostrado aqui:
c la s s TemperatureMonitor

{
p u b lic delegate void StopM achineryD elegateO ;
p u b lic event StopMachineryDelegate MachineOverheating;

}
TemperatureMonitor tempMonitor = new Tem peratureM onitorO ;
tempMonitor.MachineOverheating += ( Q

=> { fo ld e r.S to p F o ld in g (O ); } ) ;

376

Parte

III

Criando componentes

tempMonitor.MachineOverheating += w eld er.Fin ish W eld in g ;


tempMonitor.MachineOverheating += p a in t e r .P a in t O ff;

Note que a sintaxe a mesma empregada para adicionar um mtodo a um delegate. Voc at pode se
inscrever utilizando uma expresso lambda. Ouando o evento tempMonitor.MachineOverheating for
executado, ele chamar todos os mtodos inscritos e desligar as mquinas.

Cancelando a inscrio em um evento


Sabendo que o operador + = utilizado para anexar um delegate a um evento, provavelmente voc
pode imaginar que utilizar o operador -= para desvincular um delegate de um evento. Chamar
esse operador remove o mtodo da coleo de delegates internos do evento. Essa ao costumar ser
referida como cancelar a inscrio a um evento .

Disparando um evento
Um evento pode ser disparado, exatamente como um delegate, chamando-o como um mtodo.
Quando voc dispara um evento, todos os delegates anexados so chamados em sequncia. Por
exemplo, veja a classe TemperatureMonitor com um mtodo N otify privado que dispara o evento
MachineOverheating-.
c la s s TemperatureMonitor

{
p u b lic delegate void StopM achineryD eleg ateO ;
p u b lic event StopMachineryDelegate MachineOverheating;
p riv a te void N o t ify O

{
i f (th is.M achin eO verheatin g != n u ll)

{
th is.M a c h in e O ve rh e a tin g O ;

}
}
}
A verificao null necessria porque um campo de evento implicitamente n u ll e s se torna no
nu ll quando um mtodo se inscreve nele utilizando o operador + = . Se tentar disparar um evento
null, voc obter uma NullReferenceException. Se o delegate que define o evento espera algum par
metro, os argumentos apropriados devem ser fornecidos quando voc disparar o evento. Voc ver
alguns exemplos mais adiante.

Importante Os e ve n to s tm um re cu rso d e se g u ra n a e m b u tid o m uito til. Um even to p b lico


(co m o MachineOverheating) s p o d e ser d is p a ra d o p o r m to d o s da classe q u e o d e fin e (a classe
TemperatureMonitor). Q u a lq u e r ten tativa d e d is p a ra r o m to d o fo ra da classe resulta em um erro
d e co m p ila o .

Captulo 17

Interrompendo o fluxo do programa e tratando eventos

377

Entendendo eventos de interface WPF


Como mencionado anteriormente, as classes e os controles do .NET Framework utilizados para cons
truir interfaces grficas do usurio [graphical user interfaces - GUIs) empregam eventos extensa
mente. Voc ver e usar os eventos GUI em muitas ocasies na segunda metade deste livro. Por
exemplo, a classe WPF Button deriva da classe ButtonBase, herdando um evento pblico chamado
Click do tipo RoutedEventHandler. O delegate RoutedEventHandler espera dois parmetros: uma re
ferncia ao objeto que fez o evento disparar e um objeto RoutedEventArgs que contm informaes
adicionais sobre o evento:
p u b lic delegate void RoutedEventHandler(O bject sender, RoutedEventArgs e ) ;

A classe Button se parece a:


p u b lic c la s s ButtonBase:

...

{
pu b lic event RoutedEventHandler C lic k ;

}
p u b lic c la s s Button: ButtonBase

{
}
A classe Button dispara automaticamente o evento Click quando voc clica no boto na tela (a ma
neira como isso acontece est alm do escopo deste livro). Essa disposio facilita a criao de um
delegate para um mtodo escolhido e anexa esse delegate ao evento necessrio. O exemplo a seguir
mostra o cdigo para um formulrio WPF que contm um boto chamado okay e o cdigo para co
nectar o evento Click do boto okay ao mtodo okayClick
.
p u b lic p a r tia l c la s s Example : System.Windows.Window, System.Windows.Markup.
IComponentConnector

{
in te rn a i System .W indows.Controls.Button okay;
void System.Windows.Markup.IComponentConnector.Connect(.. . )

{
t h i s . o k a y.C lic k += new System .W ind ow s.R outedEven tH andler(this.o kayC lick);

}
}
Normalmente voc no v esse cdigo. Ouando utiliza a janela Design View no Visual Studio 2010 e
configura a propriedade Click do boto okay como okayClick na descrio do formulrio da Extensible
Application Markup Language (XAML), o Visual Studio 2010 gera esse cdigo para voc. Tudo o que

378

Parte III

Criando componentes

precisa fazer escrever a lgica do seu aplicativo no mtodo de tratamento de evento, okayClick, na
parte do cdigo qual voc tem acesso, nesse caso, no arquivo Example.xaml.es:
p u b lic p a r tia ! c la s s Example : System.Windows.Window

{
p riv a te void o k a y C lic k (o b je c t sender, RoutedEventArgs args)

{
// seu cdigo para t r a t a r o evento C lic k

}
>
Os eventos que os vrios controles GUI geram sempre seguem o mesmo padro. So de um tipo delegate cuja assinatura tem um tipo de retorno void e dois argumentos. O primeiro argumento sempre
o emissor (a origem) do evento, e o segundo argumento sempre um argumento EventArgs (ou uma
classe derivada dt EventArgs).
Com o argumento sender, voc pode reutilizar um nico mtodo para mltiplos eventos. O mtodo
pode examinar o argumento sender e responder de acordo. Por exemplo, voc pode utilizar o mes
mo mtodo para inscrev-lo ao evento Click de dois botes (voc adiciona o mesmo mtodo a dois
eventos diferentes). Quando o evento disparado, o cdigo no mtodo pode examinar o argumento
sender para se certificar de qual boto foi clicado.
Voc aprender mais sobre como tratar eventos para controles WPF no Captulo 22, Apresentando o
Windows Presentation Foundation .

Utilizando eventos
No exerccio anterior, voc concluiu um aplicativo World Clock, que exibe a hora local, assim como
a hora em Londres, Nova York e Tquio. Voc utilizou delegates para iniciar e parar os relgios.
Voc ainda deve se lembrar de que cada relgio tinha um construtor que esperava o nome do campo
existente no formulrio no qual a hora deveria ser exibida. Entretanto, um relgio deve realmente
se concentrar em ser um relgio e no se preocupar, necessariamente, com o modo de exibio da
hora - melhor deixar a lgica do prprio formulrio WPF se encarregar dessa funcionalidade. Neste
exerccio, voc modificar o relgio local para acionar um evento a cada segundo. Voc far uma
inscrio nesse evento, no formulrio WPF, e chamar um manipulador de eventos que exibe a nova
hora local.

M o d ifiq u e o ap licativo W o rld Clock para utilizar eventos


1. Retorne janela do Visual Studio 2010 que exibe o projeto Clock.
2. Exiba o arquivo LocalClock.es na janela Code and Text Editor.

Captulo 17

Interrompendo o fluxo do programa e tratando eventos

379

Esse arquivo contm a classe LocalClock que implementa o relgio local. Inclumos a seguir os
principais elementos dessa classe:
Um objeto DispatcherTimer, chamado ticker. A classe DispatcherTim er fornecida como
parte do .NET Framework, e seu objetivo acionar eventos, em intervalos de tempo espe
cificados.
Um campo TextBox, chamado display. O construtor inicializa esse campo com o objeto
TexCBox passado como parmetro. A classe LocalClock define a propriedade Text desse
objeto para exibir a hora, no mtodo RefreshTime.
Um objeto TimeZonelnfo, chamado timeZoneForThisClock. A classe TimeZonelnfo tambm
faz parte do .NET Framework. Essa classe utilizada para obter a hora em um fuso horrio
especfico. O construtor inicializa esse objeto com TimeZone.Local, que o fuso horrio
local do computador que executa o aplicativo.
O mtodo StartLoca/C/ock, que inicia o funcionamento do DispatcherTimer. A classe D is
patcherTim er fornece o evento Tick, utilizado para especificar um mtodo a ser executado
sempre que ocorrer um evento Tick, e a propriedade In terval, usada para especificar a
frequncia com que ocorrem os eventos Tick. O cdigo da classe LocalClock aciona um
evento a cada segundo. Na realidade, o mtodo Start da classe DispatcherTim er inicia o
funcionamento do cronmetro. No exerccio anterior, voc chamou esse mtodo por meio
do delegate StartClocksDelegate na classe Controller.
O mtodo StopLocalClock, que chama o mtodo Stop do objeto DispatcherTimer. Esse mto
do interrompe o funcionamento do cronmetro, e ele no acionar quaisquer outros even
tos antes de voc chamar o mtodo Start novamente. No exerccio anterior, voc chamou
esse mtodo por meio do delegate StopClocksDelegate na classe Controller.
O mtodo OnTimedEvent. O mtodo StartLocalClock adiciona esse mtodo ao evento Tick
do objeto DispatcherTimer, e, quando ocorre um evento Tick, esse mtodo executado. Os
parmetros para esse mtodo so exigidos pela definio do delegate utilizado pelo evento
Tick, mas eles no so usados neste exemplo, de modo que voc pode ignor-los. Esse
mtodo recupera a data e a hora atuais, por meio da propriedade esttica Now da classe
DateTime. Em seguida, ele converte a hora recuperada na hora local, ao utilizar o mtodo
TimeZonelnfo.ConvertTime. As horas, os minutos e os segundos so extrados da hora e
passados como parmetros para o mtodo RefreshTime.

380

Parte III

Criando componentes

O mtodo RefreshTime, que formata as horas, os minutos e segundos, passados como


parmetros em uma string, e depois exibe essa string na TextBox referenciada pelo campo
display.
3. O objetivo deste exerccio retirar da classe LocalClock a responsabilidade pela exibio da hora,
de modo que voc deve transformar em comentrio a definio do campo display
.
c la s s LocalClock

{
// private TextBox display = n u ll;

}
4. Remova o parmetro do construtor LocalClock e comente a instruo que define o campo display
com esse parmetro. O construtor corrigido ficar parecido com o seguinte:
p u b lic Local C lo c k ()

{
this.tim eZ oneForThisC lo ck = T im eZo n eln fo .Lo cal;

// t h is .d is p la y = displayBox;

}
5. Adicione um delegate pblico, chamado DisplayTime, classe LocalClock, antes do construtor.
Esse delegate deve especificar um mtodo que aceita um parmetro de string e retorna um void.
O formulrio WPF fornecer um mtodo que combina com esse delegate. Esse mtodo atualiza
r a hora exibida no formulrio com a string passada como parmetro.
c la s s LocalClock

{
public delegate void DisplayTime(string tim e);

}
6. Adicione um evento pblico, chamado LocalClockTick, classe LocalClock, depois do delegate
DisplayTim e. Esse evento deve se basear no delegate DisplayTime.
c la s s LocalC lock

{
p u b lic delegate void D isp la yT im e (strin g tim e );

public event DisplayTime LocalClockTick;

}
7. Localize o mtodo RefreshTime no final da classe LocalClock. Esse mtodo define atualmente a
propriedade Text do campo display com uma string formatada que contm a hora atual. Modi
fique esse mtodo de modo que ele acione, em substituio, o evento LocalClockTick, e passe a
string formatada como parmetro para quaisquer mtodos que se inscrevam nesse evento.

Captulo 17

Interrompendo o fluxo do programa e tratando eventos

381

p riv a te void R efresh T im e(in t hh, in t mm, in t ss)

{
i f (this.LocalC lockTick != n u ll)

{
t h is .L o c a lC l o c k T ic k ( S t r i n g .F o r m a t ( " { 0 :D 2 } :{l:D 2 } :{ 2 : D 2 }" . hh, mm, s s ) ) ;

}
}

8. Exiba o arquivo ClockWindow.xaml.cs na janela Code and TextEditor. No construtor ClockWindow, modifique a instruo que instancia a varivel localClock e remova o parmetro da chama
da ao construtor.
p u b lic ClockWindowO

{
localClock = new L ocalC lo ckO ;

}
9. No menu Debug, clique em Start W ithout Debugging. Quando o formulrio WPF aparecer, clique
em Start. Voc deve ver os relgios de Londres, Nova York e Tquio funcionando como anterior
mente, mas a exibio da hora local continua parada em 00:00:00. Isso ocorre porque, embora
o objeto LocalClock acione eventos a cada segundo, voc ainda no se inscreveu nesses eventos.
Feche o formulrio WPF e retorne ao Visual Studio.
10. No arquivo ClockWindow.xaml.cs, adicione o seguinte mtodo ao final da classe ClockWindow.
p riv a te void d isp layLo calT im eC string tim e)

{
lo calT im eD isp lay.T ex t = tim e;

}
Esse mtodo exibe a string passada como parmetro no TextBox localTimeDisplay, no formulrio.
11. No mtodo startClick, adicione a instruo mostrada em negrito a seguir, que faz uma inscrio
do mtodo displayLocalTim e no evento LocalClockTick do objeto localClock
.
p riv a te void s t a r t C lic k (o b je c t sender, RoutedEventArgs e)

{
c o n t r o lle r .S t a r tC lo c k s C );

localClock. LocalClockTick += displayLocalTime;


s ta rt.Is E n a b le d = f a ls e ;
sto p .IsEn ab led = tru e ;

382

Parte III

Criando componentes

12. No mtodo stopClick, cancele a inscrio do mtodo displayLocalTim e no evento LocalClickTick.


p riv a te void sto p C lick C ob ject sender, RoutedEventArgs e)

{
c o n tro ll e r . StopClocks O ;

1ocalClock.LocalClockTick -= displayLocalTime;
s ta rt.Is E n a b le d = tru e ;
sto p .IsEn ab led = f a ls e ;

}
13. No menu Debug, clique em Start W ithoutDebugging.
14. Clique em Start. Dessa vez, o relgio local exibe a hora correta e atualizado a cada segundo.
15. Clique em Stop e verifique se o relgio local interrompido. Em seguida, feche o formulrio e
retorne ao Visual Studio 2010.
Neste exerccio, voc atualizou o relgio local para sinalizar para o formulrio que ele deve atuali
zar sua exibio, ao utilizar eventos, mas que os outros relgios devem continuar exibindo a hora.
Quando tiver um tempo, modifique as demais classes de relgio da mesma maneira.
Neste captulo, voc aprendeu a utilizar delegates para fazer referncia a mtodos e chamar esses
mtodos. Voc viu como definir mtodos annimos e expresses lambda que podem ser executados
por meio de um delegate. Por ltimo, voc aprendeu a definir e utilizar eventos para acionar a exe
cuo de um mtodo.
Se voc quiser seguir para o prximo captulo agora:
Mantenha o Visual Studio 2010 em execuo e v para o Captulo 18.
Se quiser sair do Visual Studio 2010:
No menu File, clique em Exit. Se vir uma caixa de dilogo Save, clique em Yes e salve o projeto.

Captulo 17

Interrompendo o fluxo do programa e tratando eventos

383

Referncia rpida do Captulo 17


Para

Faa isto

Declarar um tipo delegate

Escreva a palavra-chave delegate, seguida pelo tipo de retorno, segui


da pelo nome do tipo delegate, seguida por qualquer tipo de parme
tro. Por exemplo:
delegate void m yD elegateQ ;

Criar uma instncia de um delegate inicializado com um nico mtodo especfico

Utilize a mesma sintaxe que voc utilizou para uma classe ou estrutura: escreva a palavra-chave new, seguida pelo nome do tipo (o nome
do delegate), seguido pelo argumento entre parnteses. 0 argumento
deve ser um mtodo cuja assinatura corresponda exatamente assina
tura do delegate. Por exemplo:
delegate void m yD elegateO ;
p riv a te void myMethodO { }
myDelegate del = new m yDelegate(this.m yM ethod);

Invocar um delegate

Utilize a mesma sintaxe que uma chamada de mtodo. Por exemplo:


myDelegate d e i;
dei O ;

Declarar um evento

Escreva a palavra-chave event, seguida pelo nome do tipo (o tipo deve


ser um tipo delegate), seguido pelo nome do evento. Por exemplo:
delegate void myEvent 0 ;
c la s s MyClass
{
p u b lic event myDelegate MyEvent;
}

Fazer a inscrio a um evento

Crie uma instncia do delegate (do mesmo tipo do evento) e vincule-a


ao evento utilizando o operador +=. Por exemplo:
c la s s MyEventHandlingClass
{
p riv a te MyClass myClass = new M yClassO ;
p u b lic void S t a r t O
{
myClass.MyEvent += new myDelegate
(t h i s . eventHandli ngMethod);
>
p riv a te void eventHandlingMethodO
{
}
}
Voc tambm pode fazer o compilador gerar automaticamente o novo
delegate simplesmente especificando o mtodo inscrito:
p u b lic void S t a r t O
{
myClass.MyEvent += this.eventH andlingM ethod;
}

'continua)

384

Parte III

Criando componentes

Para

Faa isto

Cancelar a inscrio a um evento

Crie uma instncia do delegate (do mesmo tipo do evento) e desanexe


a instncia do delegate do evento utilizando o operador-=. Por ex
emplo:
c la s s MyEventHandlingClass

{
p riv a te MyClass myClass = new M yC lassO ;
p u b lic void StopQ

{
m yClass.MyEvent -= new myDelegate
(th is .e v e n tH a n d li ngMethod);

}
}
Ou:
p u b lic void Sto pO

{
myClass.MyEvent -= this.eventHandlingM ethod;

}
Disparar um evento

Use a mesma sintaxe de uma chamada a um mtodo. Fornea ar


gumentos que combinem com o tipo de parmetros esperados pelo
delegate referenciado pelo evento. No se esquea de verificar se o
evento null. Por exemplo:
c la s s MyClass

{
p u b lic event myDelegate MyEvent;
p riv a te void R aiseE ven tO

{
i f ( t h i s . MyEvent != n u ll)

{
th is .M y E v e n tO ;

}
}
}

Captulo 18

Apresentando genricos
Neste captulo, voc vai aprender a:
Definir uma classe segura (type-safe) utilizando genricos.
Criar instncias de uma classe genrica com base nos tipos especificados com o parm e
tros de tipo.
Implementar uma interface genrica.
Definir um m todo genrico que im plem enta um algoritm o independente do tipo de
dados em que ele opera.

No Captulo 8, Entendendo valores e referncias, voc aprendeu a empregar o tipo object para refe
renciar uma instncia de qualquer classe. Voc pode utilizar o tipo object para armazenar um valor
de qualquer tipo e tambm definir parmetros atravs do tipo object, quando precisar passar valores
de qualquer tipo em um mtodo. Um mtodo tambm pode retornar valores de qualquer tipo especi
ficando object como o tipo de retorno. Embora seja muito flexvel, essa prtica deixa ao programador
a responsabilidade de lembrar que tipos de dados esto realmente sendo utilizados, e podem ocorrer
erros de tempo de execuo se o programador cometer um erro. Neste captulo, voc vai aprender
sobre os genricos, um recurso projetado para ajud-lo a evitar esse tipo de erro.

O problema com objects


Para entender os genricos, vale a pena examinar detalhadamente os problemas que eles foram pro
jetados para resolver, especificamente ao empregar o tipo object.
Voc pode utilizar o tipo object para referenciar a um valor ou a uma varivel de qualquer tipo. Todos
os tipos-referncia herdam automaticamente (direta ou indiretamente) da classe System .Object no
Microsoft .NET Framework. Voc pode utilizar essas informaes para criar classes e mtodos alta
mente generalizados. Por exemplo, muitas das classes no namespace System .Collections exploram
esse fato, portanto, voc pode criar colees que armazenam praticamente qualquer tipo de dados.
(J vimos classes de coleo no Captulo 10, Utilizando arrays e colees .) Examinando uma classe
de coleo especfica, como um exemplo detalhado, voc tambm perceber na asst System.Collec
tions. Queue que possvel criar filas contendo praticamente qualquer coisa. O exemplo de cdigo a
seguir mostra como criar e manipular uma fila de objetos Circle
.
using S y ste m .C o lle ctio n s;
Queue myQueue = new QueueO;
C ir c le m yCircle = new C ir c l e O ;
myQueue. Enqueue(myCi r c le ) ;
m yCircle = (C ircle)m yQ ueue.D equeue();

386

Parte III

Criando componentes

O mtodo Enqueue adiciona um object ao incio de uma fila e o mtodo Dequeue remove o object na
outra extremidade da fila. Esses mtodos so definidos assim:
p u b lic void EnqueueC o b ject item ) ;
p u b lic o b ject DequeueO;

Como os mtodos Enqueue e Dequeue manipulam objects, voc pode operar em filas de Circles, PhoneBooks, Clocks ou qualquer outra classe vista nos exerccios anteriores deste livro. Mas importante
observar que voc tem de fazer o casting do valor retornado pelo mtodo Dequeue, a fim de realizar a
converso para o tipo apropriado, porque o compilador no executar automaticamente a converso
para o tipo de objeto. Se no fizer o casting do valor retornado, voc receber o erro de compilador
Cannot implicitly convert type object to Circle. [No foi possvel converter implicitamente o tipo
object para Circle.]
Essa necessidade de fazer um casting explcito inibe grande parte da flexibilidade propiciada pelo
tipo object. muito fcil escrever um cdigo como este:
Queue myQueue = new QueueO;
C ir c le m yCircle = new C ir c l e O ;
myQueue.Enqueue(m y C irc le );
Clock myClock = (Clock)myQueue.DequeueO; // erro de tempo de execuo

Embora esse cdigo seja compilado, ele no vlido e gera uma System .InvalidCastException em
tempo de execuo. O erro causado por tentar armazenar uma referncia a um Circle em uma va
rivel Clock e os dois tipos no so compatveis. Esse erro s identificado em tempo de execuo
porque o compilador no tem informaes suficientes para realizar essa verificao em tempo de
compilao. O tipo real do objeto sendo desenfileirado somente torna-se aparente quando o cdigo
executa.
Outra desvantagem de utilizar a abordagem object para criar classes e mtodos generalizados que
ele pode utilizar memria e tempo adicionais do processador, se o runtime precisar converter um
object em um tipo-valor e vice-versa. Considere o seguinte fragmento de cdigo que manipula uma
fila de variveis int:
Queue myQueue = new QueueO;
in t mylnt = 99;
myQueue.Enqueue(m y ln t);

// faz o boxing do in t para um objeto

mylnt = (int)m yQueue.DequeueO; // faz o unboxing do objeto para um in t

O tipo de dados Queue espera que os itens que ele armazena sejam tipos-referncia. Enfileirar um
tipo-valor, como um int, requer que ele sofra boxing para convert-lo em um tipo-referncia. Da mes
ma maneira, remover da fila um in t requer que o item sofra unboxing para convert-lo novamente
em um tipo-valor. Consulte as sees Boxing e Unboxing , no Captulo 8, para obter mais deta
lhes. Embora os procedimentos de boxing e unboxing ocorram de forma transparente, eles adicionam
uma sobrecarga ao desempenho porque envolvem alocaes dinmicas de memria. Essa sobrecarga
pequena para cada item, mas aumenta quando um programa cria filas de numerosos tipos-valor.

Captulo 18

Apresentando genricos

387

A soluo dos genricos


0 C# fornece genricos para eliminar a necessidade de casting, melhorar a segurana, reduzir a
quantidade de boxing necessrio e facilitar a criao de classes e mtodos generalizados. Classes e
mtodos genricos aceitam parmetros de tipo, que especificam o tipo dos objetos em que eles ope
ram. A biblioteca de classes do .NET Framework inclui verses genricas de boa parte das classes de
coleo e interfaces no namespace System. Collections.Generic. O exemplo de cdigo a seguir mostra
como utilizar a classe Queue genrica, encontrada nesse namespace, para criar uma fila de objetos
Circle:
usi ng System.Col1ecti ons.Ceneri c ;
Queue<Circ1e> myQueue = new Queue<Circ1e>();
Circle myCircle = new C ircleO ;
myQueue. Enqueue(myCi r d e ) ;
myCi rd e = myQueue.DequeueO ;
H duas coisas novas a notar no cdigo do exemplo anterior:
O uso do parmetro de tipo entre os colchetes angulares, < Circle>, na declarao da varivel
myQueue.
A falta de um casting na execuo do mtodo Dequeue.
O parmetro de tipo entre os colchetes angulares especifica o tipo de objetos aceitos pela fila. Todas
as referncias aos mtodos nessa fila vo utilizar automaticamente esse tipo em vez de um object,
tornando desnecessrio o casting para o tipo Circle, ao invocar o mtodo Dequeue. O compilador ve
rificar se os tipos no foram misturados acidentalmente e gerar um erro de tempo de compilao,
em vez de um de tempo de execuo, se voc tentar remover um item de circleQueue em um objeto
Clock, por exemplo.
Se examinar a descrio da classe Queue genrica, na documentao do Microsoft Visual Studio
2010, voc notar que ela est definida como:
public class Queue<T> : ...
O T identifica o parmetro do tipo e atua como um espao reservado para um tipo real em tempo de
compilao. Ao escrever cdigo para instanciar uma Queue genrica, voc fornece o tipo que deve ser
substitudo por T (Circle no exemplo anterior). Alm disso, se ento examinar os mtodos da classe
Queue<T>, voc observar que alguns deles, como Enqueue e Dequeue, especificam 7* como um
parmetro de tipo ou um valor de retorno:
public void Enqueue( T item );
public T DequeueO;

388

Parte III

Criando componentes

O parmetro de tipo, T, substitudo pelo tipo que voc especifica ao declarar a fila. Alm disso, o
compilador agora tem informaes suficientes para executar uma verificao de tipo estrita quando
voc compilar o aplicativo e pode interceptar todos os erros de no correspondncia de tipo anteci
padamente.
Voc deve estar ciente de que essa substituio de 7" por um tipo especificado no simplesmente um
mecanismo de substituio textual. Em vez disso, o compilador executa uma substituio semntica
completa para que voc possa especificar qualquer tipo vlido para T. Veja mais exemplos:
s tru c t Person

{
}
Queue<int> intQueue = new Q ueue<int>();
Queue<Person> personQueue = new Queue<Person>();
Queue<Queue<i n t queueQueue = new Queue<Queue<i n t ( ) ;

Os dois primeiros exemplos criam filas de tipos-valor, enquanto o terceiro cria uma fila de filas (de
in ts). Por exemplo, para a varivel intQueue, o compilador tambm gera as seguintes verses dos
mtodos Enqueue e Dequeue-,
p u b lic void EnqueueC in t item ) ;
p u b lic in t DequeueO;

Compare essas definies com aquelas da classe Queue no genrica mostradas na seo anterior.
Nos mtodos derivados da classe genrica, o parmetro item para Enqueue passado como um
tipo-valor que no exige boxing. Da mesma forma, o valor retornado por Dequeue tambm um
tipo-valor que no precisa sofrer unboxing.
Uma classe genrica tambm pode ter mltiplos parmetros de tipo. Por exemplo, a classe genrica
System .Collections.Generic.Dictionary espera dois parmetros de tipo: um tipo para as chaves e outro
para os valores. A definio a seguir mostra como especificar mltiplos parmetros de tipo:
p u b lic c la s s Dictionary< TKey, TValue>

Um dicionrio fornece uma coleo de pares chave/valor. Voc armazena os valores (tipo TValue) com
uma chave associada (tipo TKey) e ento os recupera especificando a chave a ser pesquisada. A clas
se D ictionary fornece um indexador que permite acessar os itens utilizando uma notao de array.
Ela definida da seguinte forma:
p u b lic v ir t u a l TValue t h i s [ TKey key ] { g et; s e t; }

Captulo 18

Apresentando genricos

389

Observe que o indexador acessa os valores do tipo TValue por meio de uma chave do tipo TKey. Para
criar e utilizar um dicionrio chamado directory contendo os valores Person identificados por chaves
string, use o cdigo a seguir:
s tru c t Person

{
}
D ictio n ary< strin g , Person> d ire c to ry = new D ic tio n a ry < strin g , Person> ();
Person john = new Perso n O ;
d ir e c to r y ["]o h n " ] = john;
Person author = d ir e c t o r y [ " J o h n " ] ;

Como na classe genrica Queue, o compilador detecta tentativas de armazenar valores diferentes das
estruturas Person no diretrio, assim como garante que a chave seja sempre um valor string. Para
obter mais informaes sobre a classe D ictionary, leia a documentao do Visual Studio 2010.

Classes genricas versus generalizadas


importante estar ciente de que uma classe genrica que utiliza parmetros de tipo diferente de
uma dasse generalizada projetada para receber parmetros que podem ser convertidos via casting
em tipos diferentes. Por exemplo, a classe System .Collections.Queue uma classe generalizada.
H uma nica implementao dessa classe e seus mtodos recebem parmetros object e retornam
tipos object. Voc pode utilizar essa classe com tipos int, string e muitos outros, mas em cada caso
voc usa instncias da mesma classe e precisa fazer um casting dos dados utilizados para e do
tipo object.
Compare essa classe com a classe System .Collections.Generic.Queue<T>. Sempre que utiliza essa
classe com um parmetro de tipo (como Queue<int> ou Queue < string> ), na realidade voc faz o
compilador gerar uma classe totalmente nova que tem funcionalidades definidas pela classe genri
ca. Isso significa que uma Queue<int> um tipo totalmente diferente de um Queue<string>, mas
ambos tm o mesmo comportamento. Voc pode pensar numa classe genrica como uma classe que
define um template que , ento, utilizado pelo compilador para gerar novas classes de tipo especfico
sob demanda. As verses de tipo especfico de uma classe genrica {Queue<int>, Queue<string> ,
etc.) so conhecidas como tipos construdos, e voc deve trat-las como tipos bem diferentes (apesar
daquelas que tm um conjunto semelhante de mtodos e propriedades).

390

Parte III

Criando componentes

Genricos e restries
Eventualmente, recomendvel assegurar que o parmetro de tipo utilizado por uma classe genrica
identifique um tipo que fornece certos mtodos. Por exemplo, se estiver definindo uma classe PrintableCollection, voc pode querer garantir que todos os objetos armazenados na classe tenham um
mtodo Print. possvel especificar essa condio utilizando uma restrio.
Utilizando uma restrio, voc pode limitar os parmetros de tipo de uma classe genrica queles
que implementam um conjunto especfico de interfaces e, portanto, fornecer os mtodos definidos
por essas interfaces. Por exemplo, se a interface IPrintable definisse o mtodo P rin t, voc poderia
criar a classe PrintableCollection assim:
p u b lic c la s s Prin tableC ollectio n< T> where T : IP r in t a b le

Quando voc criar essa classe com um parmetro de tipo, o compilador far uma verificao para
garantir que o tipo utilizado por T realmente implementa a interface IPrintable e, caso isso no acon
tea, terminar com um erro de compilao.

Criando uma classe genrica


A biblioteca de classes do .NET Framework contm vrias classes genricas prontamente disponveis
para voc. possvel definir suas prprias classes genricas, que o que voc far nesta seo. Antes
de voc fazer isso, forneo um pouco de teoria bsica.

A teoria das rvores binrias


Nos exerccios a seguir, voc definir e utilizar uma classe que representa uma rvore binria.
Esse um exerccio prtico porque, casualmente, essa classe est faltando no namespace System.
Collections.Generic. Uma rvore binria uma estrutura de dados til utilizada em vrias operaes,
incluindo classificar e pesquisar dados de forma muito rpida. H livros inteiros escritos sobre as mi
ncias das rvores binrias, mas abord-las em detalhe no a finalidade deste livro. Em vez disso,
vamos examinar apenas os detalhes pertinentes. Se estiver interessado, consulte um livro como The
A rt o fComputer Programming, Volume 3: Sorting and Searching, de Donald E. Knuth (AddisonWesley Professional; 2a edio, 1998).
Uma rvore binria uma estrutura de dados recursiva (de autorreferenciao) que pode estar vazia
ou conter trs elementos: um dado, que em geral conhecido como o n, e duas subrvores, que
so rvores binrias. As duas subrvores so chamadas convencionalmente de subrvore esquerda
e subrvore direita porque em geral so representadas esquerda e direita do n, respectivamente.
Cada subrvore esquerda ou direita est vazia ou contm um n e outras subrvores. Teoricamente,
a estrutura inteira pode continuar ad infinitum . A seguinte imagem mostra a estrutura de uma pe
quena rvore binria.

Captulo 18

Apresentando genricos

391

0 verdadeiro poder das rvores binrias torna-se evidente quando voc as utiliza para ordenar da
dos. Se iniciar com uma sequncia no ordenada de objetos do mesmo tipo, voc poder construir
uma rvore binria ordenada e ento percorr-la para visitar cada n em uma sequncia ordenada. O
algoritmo para inserir um item em uma rvore binria ordenada mostrado a seguir:
Se a rvore T est va z ia
Ento
Construa uma nova rvore T, com o novo item I como o n, e subrvores
esquerda e d i r e it a va zia s
Seno
Examine o v a lo r do n a tu a l, N, da rvore T
Se o v a lo r de N fo r maior do que o do novo item I
Ento
Se a subrvore esquerda de T e st va z ia
Ento
Construa uma nova subrvore esquerda de T, com o novo item I como o n,
e subrvores esquerda e d i r e it a vazias
Seno
In s ir a I na subrvore esquerda de T
Fim_Se
Seno
Se a subrvore d i r e it a de T e st va z ia
Ento
Construa uma nova subrvore d i r e it a de T, com o novo item I como o n, e
subrvores esquerda e d i r e it a va zia s
Seno
In s ir a T na subrvore d i r e it a de T
Fim_Se
Fim Sp

fim St

392

Parte III

Criando componentes

Observe que esse algoritmo recursivo, chamando a si prprio para inserir o item na subrvore da
esquerda ou da direita, dependendo de como o valor do item comparado com o n atual da rvore.

Se comear com uma rvore binria vazia e uma sequncia no ordenada de objetos, voc poder
iterar por essa sequncia inserindo cada objeto na rvore binria com esse algoritmo, o que resultar
em uma rvore ordenada. A imagem a seguir mostra os passos do processo para a construo de
uma rvore a partir de um conjunto de cinco inteiros.

| Dados: 1,5,-2,1,6

Dados: 5,-2,1,6

rvore: (Vazia)

I 1

Dados: -2, 1, 6

Dados: 6

Dados: 1,6

Dados:

Depois de construir uma rvore binria ordenada, voc pode exibir seu contedo em sequncia vi
sitando um n de cada vez e imprimindo o valor encontrado. O algoritmo para executar essa tarefa
tambm recursivo:
Se a subrvore esquerda no est v a z ia
Ento
E x ib ir o contedo da subrvore esquerda
Fim_Se
E x ib ir o v a lo r do n atual

Captulo 18

Apresentando genricos

393

Se a rvore d i r e it a no e st va z ia
Ento
E x ib ir o contedo da subrvore d i r e it a
Fim_Se

A imagem a seguir mostra os passos no processo de gerar a sada da rvore. Observe que os inteiros
agora so exibidos em ordem crescente.

'

Sada: -2,1

Sada: -2

0
^ (T }|
Sada:-2,1,1

j !

x .

I 1

I 1

Sada:-2,1,1, 5

101
Salda:-2,1,1,5, 6

Construindo uma classe de rvore binria com genricos


No exerccio a seguir, voc utilizar genricos para definir uma classe de rvore binria capaz de
armazenar quase todos os tipos de dados. A nica restrio que os tipos de dados devem fornecer
uma maneira de comparar valores entre diferentes instncias.
A classe de rvore binria uma classe que pode ser til em vrias aplicaes diferentes. Ento voc
a implementar como uma biblioteca de classes, em vez de um aplicativo independente. Voc pode
ento reutilizar essa classe em qualquer lugar sem ter de copiar o cdigo-fonte e recompil-lo. Uma
biblioteca de classes um conjunto de classes compiladas (e outros tipos como estruturas e delega
tes) armazenadas em um assembly. Um assembly um arquivo que normalmente tem o sufixo .dll.
Outros projetos e aplicativos podem fazer uso dos itens de uma biblioteca de classes adicionando
uma referncia ao seu assembly e ento trazendo seus namespaces para o escopo com as instrues
using. Voc far isso quando testar a classe de rvore binria.

394

Parte III

Criando componentes

As interfaces System.IComparable e System.


IComparable < T>
O algoritm o para inserir um n em uma rvore binria exige que voc com pare o valor do n
sendo inserido com os ns j existentes na rvore. Ao utilizar um tipo numrico, com o o int,
voc pode usar os operadores <, > e = = . Entretanto, se usar outro tipo, com o M am m al ou
Circle descritos nos captulos anteriores, com o voc vais com parar os objetos?
Se precisar criar uma classe que exija a com parao de valores de acordo com algum a ordem
natural (ou possivelm ente no natural), voc deve im plem entar a interface
Essa interface contm um m todo cham ado

CompareTo,

IComparable.

que recebe um nico parm etro

especificando o objeto a ser com parado com a instncia atual e retorna um inteiro que indi
ca o resultado da com parao, com o resumido na tabela a seguir.

Valor

Significado

Menor que 0

A instncia atual menor que o valor do parmetro.

A instncia atual igual ao valor do parmetro.

Maior que 0

A instncia atual maior que o valor do parmetro.

Com o um exemplo, considere a classe

Circle

que foi descrita no Captulo 7, "C riando e ge-

renciando classes e objetos", e que reproduzida a seguir:


c la s s Ci rc le

{
p u b lic C i r c l e ( i n t in i t i al Radius)

{
radius = in it ia lR a d iu s ;

}
p u b lic double A re a ()

{
return M ath.PI * radius * rad iu s;

}
p riv a te double rad ius;

}
Voc pode tornar a classe Circle "co m p ar vel" im plem entando a interface System.IComparable e fornecendo o m todo CompareTo. Neste exemplo, o m todo CompareTo compara os
objetos Circle com base em suas reas. Um crculo com rea m aior considerado maior que
um crculo com rea menor.
c la s s C ir c le : System.IComparable

Captulo 18

Apresentando genricos

395

p u b lic in t CompareToCobject o b j)

{
C ir c le circO bj = (C ir c le )o b j;

// faz o casting do parmetro para seu tip o

real
i f (t h is .A r e a O == c i rcObj .A re a O )
return 0;
i f (t h is .A r e a O > c i rcObj .A re a O )
return 1;
return -1;

}
}
Se voc examinar a interface
um

object.

System.IComparable,

ver que seu parm etro definido como

No entanto, esse enfoque no seguro quanto aos tipos

(type-safe).

Para enten

der a razo desse fato, considere o que pode acontecer se voc tentar passar algo que no
seja um

Circle

para o m todo

CompareTo.

A interface

um casting para conseguir acessar o m todo

Area.

System.IComparable

algum outro tipo de objeto, esse casting falhar. Mas o nam espace
interface

IComparable<T>

requer o uso de

Circle, mas
System tam bm define a

Se o parm etro no for um

genrica, que contm os seguintes mtodos:

in t CompareToCT o th er) ;
Observe que esse m todo recebe um parm etro de tipo (7) em vez de um

object

e, desse

modo, m uito mais seguro do que a verso no genrica da interface. O cdigo a seguir
mostra com o voc pode im plem entar essa interface na classe

Circle:

c la s s C ir c le : System.IComparab1e<Ci rc1e>

{
pu b lic in t CompareTo(Ci r d e o th er)

{
i f ( th is .A r e a O == o th e r .A r e a O )
return 0;
i f ( th is .A r e a O
return 1;

> o th e r.A re a O )

return -1;

}
}

CompareTo e Equals deve corresponder ao tipo especificado


IComparable<Circle> . Em geral, prefervel im plem entar a interface System.
IComparable<T>, em vez da interface System.IComparable. Voc tam bm pode im plem en
O parm etro para o m todo
na interface,

tar as duas da mesma m aneira com o faz grande parte dos tipos no .NET Framework.

396

Parte III

Criando componentes

Crie a classe

Tree<Tltem >

1. Inicie o Visual Studio 2010 se ele ainda no estiver em execuo.

2. Se voc estiver utilizando o Visual Studio 2010 Standard ou o Visual Studio 2010 Professional,
siga estes passos para criar um novo projeto de biblioteca de classe.
2.1. No menu File, aponte para New e ento clique em Project.

2.2. Na caixa de dilogo New Project, no painel central, selecione o template Class Library.
2.3. Na caixa de texto Name, digite BinaryTree.
2.4. Na caixa de texto Location,especifique W icrosoft PressW isual CSharp Step By Step\Chapter
18 na sua pasta Documentos.

2.5. Clique em OK.


3. Se voc estiver utilizando o Microsoft Visual C# 2010 Express, siga estes passos para criar um
novo projeto de biblioteca de classe:

3.1. No menu Tools, clique em Options.


3.2. Na caixa de dilogo Options, marque a caixa de seleo Show a li settings.
3.3. Clique em Projects and Solutions na visualizao de rvore, no painel esquerdo.
3.4. No painel direito, na caixa de texto Visual Studio projects location, especifique a locali
zao como a pasta W icrosoft PressW isual CSharp Step By Step\Chapter 18 na sua pasta
Documentos.

3.5. Clique em OK.


3.6. No menu File, clique em New Project.
3.7. Na caixa de dilogo New Project, clique no cone Class Library.
3.8. No campo Name, digite BinaryTree.
3.9. Clique em OK.
4. No Solution Explorer, clique com o boto direito do mouse em C lassl.cs, clique em Rename e
mude o nome do arquivo para Tree.cs. Deixe o Visual Studio mudar o nome da classe bem
como o nome do arquivo quando solicitado.

5. Na janela Codeand TextEditor, mude a definio da classe Tree para Tree<TItem>, como mos
trado em negrito no cdigo a seguir:
p u b lic c la s s Tree<TItem>

{
}

Captulo 18

Apresentando genricos

397

6. Na janela Code and Text Editor, modifique a definio na classe Tree<TItem>, como mostrado
a seguir em negrito, para especificar que o parmetro de tipo TItem deve denotar um tipo que
implementa a interface genrica IComparable<TItem> .
A definio modificada da classe Tree<TItem> deve ficar assim:
pu b lic c la s s Tree<TItem> where TItem : IComparable<TItem>

{
}
7. Adicione trs propriedades pblicas e automticas classe Tree<TItem >: uma propriedade
TItem chamada NodeData e duas propriedades Tree<TItem> chamadas LeftTree e RightTree,
como no texto em negrito a seguir:
p u b lic c la s s Tree<TItem> where TItem : IComparable<TItem>

{
public TItem NodeData { get; set; }
public Tree<TItem> LeftTree { get; set; }
public Tree<TItem> RightTree { get; set; }

}
8. Adicione um construtor classe Tree<TItem> que aceita um nico parmetro TItem chamado
nodeValue. No construtor, configure a propriedade NodeData como nodeValue e inicialize as
propriedades LeftTree e RightTree como null, como mostrado em negrito no cdigo a seguir:
p u b lic c la s s Tree<TItem> where TItem : IComparable<TItem>

{
public Tree(TItem nodeValue)

{
t h i s . NodeData = nodeValue;
this.Le ftT re e = n u ll;
this.RightTre e = n u ll;

Nota

Note que o nome do construtor no inclui o parmetro de tipo; ele chamado Tree e
no Tree<Tltem>.

9. Adicione um mtodo pblico chamado Insert classe Tree<TItem> como mostrado em negrito
neste cdigo. Esse mtodo insere um valor TItem na rvore.
A definio do mtodo deve ser semelhante a este:
p u b lic c la s s Tree<TItem> where TItem : IComparable<TItem>

{
public void Insert(TItem newltem)

{
}

398

Parte III

Criando componentes

O mtodo Insert implementa o algoritmo recursivo, descrito anteriormente, para criar uma r
vore binria ordenada. O programador ter utilizado o construtor para criar o n inicial da
rvore (no h um construtor padro), portanto, o mtodo Insert pode supor que a rvore no
est vazia. A parte do algoritmo depois da verificao se a rvore est vazia reproduzida aqui
para ajud-lo a entender o cdigo que voc escrever para o mtodo Insert nos passos a seguir:

Examinar o v a lo r do n, N, da rvo re, T


Se o v a lo r de N fo r maior do que aquele do novo item , I
Ento
Se a subrvore esquerda de T e s t iv e r va z ia
Ento
C o n stru ir uma nova subrvore esquerda de T com o novo item I como o n,
e subrvores esquerda e d i r e it a va zia s
Seno
In s e r ir I na subrvore esquerda de T
Fim_Se

10. No mtodo Insert, adicione uma instruo que declare uma varivel local do tipo TItem, chama
da currentNodeValue. Inicialize essa varivel com o valor da propriedade NodeData da rvore,
como mostrado a seguir:
p u b lic void In s e rt(T Ite m newltem)

{
TItem currentNodeValue = this.NodeData;

}
11. Adicione a seguinte instruo if-else, mostrada em negrito, ao mtodo Insert depois da defi
nio da varivel currentNodeValue. Essa instruo utiliza o mtodo CompareTo da interface
IComparable<T> para determinar se o valor do n atual maior do que o novo item:
p u b lic void In s e rt(T Ite m newltem)

{
TItem currentNodeValue = t h i s . NodeData;

i f (currentNodeValue.CompareTo(newItem) > 0)

{
// I n s e r ir o novo item na subrvore esquerda

}
el se

{
// I n s e r ir o novo item na subrvore d i r e it a
}
}

Captulo 18

Apresentando genricos

399

12. Substitua o comentrio //Inseri r o novo item na subrvore esquerda pelo seguinte
bloco de cdigo:
i f (t h is .LeftTree == nuTI)
{
th is . LeftTree = new Tree<TItem>(newItem);
}
else
{
thi s . LeftT ree. Insert(newltem);
}
Essas instrues verificam se a subrvore esquerda est vazia. Se afirmativo, uma nova rvore
ser criada utilizando o novo item e ser anexada como a subrvore esquerda do n atual; caso
contrrio, o novo item ser inserido na subrvore esquerda existente chamando o mtodo Insert
recursivamente.

13. Substitua o comentrio//Inseri r o novo item na subrvore di rei ta pelo cdigo equi
valente que insere o novo n na subrvore direita:
i f (this.RightTree == nuTI)
{
this.RightTree = new Tree<TItem>(newItem);
}
else
{
this.RightTree.Insert(newItem);
}

14. Adicione outro mtodo pblico chamado WalkTree classe Tree<TItem> depois do mtodo
Insert. Esse mtodo percorre a rvore, visitando cada n na sequncia e imprimindo seu valor.
A definio do mtodo deve ser esta:
public void WalkTree()
{
}

15. Adicione as instrues seguintes ao mtodo WalkTree. Estas instrues implementam o algorit
mo descrito anteriormente para imprimir o contedo de uma rvore binria:
i f (t h is . LeftTree != null)
{
thi s . LeftT ree.WalkT re e Q ;
}
Consol e. Wri teLi ne(thi s . NodeData .ToStri ngQ) ;
i f (this.RightTree != null)
{
this.RightTree.WalkTreeO;
}

400

Parte III

Criando componentes

16. No menu Build, clique em Bui/d Solution. A classe deve ser compilada inteiramente, mas corrija
todos os erros que forem reportados e recompile a soluo, se necessrio.

17. Se voc estiver utilizando o Visual C# 2010 Express, no menu File, clique em SaveA ll. Se a
caixa de dilogo Save Project aparecer, clique em Save.
No prximo exerccio, voc testar a classe Tree<TItem> criando rvores binrias de inteiros e strings.

Teste a classe

Tree<Tltem >

1. No Solution Explorer, clique com o boto direito do mouse na soluo BinaryTree, aponte para
Add e, ento, clique em New Project.

2. Adicione um novo projeto utilizando o template Console Application. Nomeie o projeto como
BinaryTreeTest. Configure Location como \Microsoft PressW isual CSharp Step By Step\Chapter
18 na sua pasta Documentos e ento clique em OK.

3. Assegure-se de que o projeto BinaryTreeTest est selecionado no Solution Explorer. No menu


Project, clique em Set as Startup Project.
O projeto BinaryTreeTest destacado no Solution Explorer. Ouando voc executar o aplicativo,
esse o projeto que realmente executar.
4. Certifique-se de que o projeto BinaryTreeTest ainda est selecionado no Solution Explorer. No
menu Project, clique em Add Reference. Na caixa de dilogo Add Reference, clique na guia Projects. Clique no projeto BinaryTree e, ento, em OK.
O assembly BinaryTree aparece na lista