Você está na página 1de 334

Variáveis para Números Inteiros

byte = Comporta de 0 até 255


byte idade = 45;

short = Comporta de -32.768 até 32.767


short idade2 = 50;

int = Comporta de -2.147.483.648 até 2.147.483.647


int idade_planeta = 234982347;

long = Comporta de -9.223.372.036.854.775.808 até 9.223.372.036.854.775.807


long valor_enorme = 3245683746354;

Variáveis para Números Decimais (Utilizadas em Operações Financeiras)

float = Comporta até 7 casas depois da vírgula ex: 0,0000000


float valor1 = 10.5f;

double = Comporta até 15 casas depois da vírgula ex: 0,000000000000000


double valor2 = 20.3d;

Variáveis Alfanuméricas

*** Aceitam todos os tipos de caracteres (números, letras, símbolos) que serão todos
lidos no formato texto.
*** Números serão lidos como texto, sem função numérica.
*** Estas variáveis podem ser concatenadas com outras variáveis ex:
caixa_texto.Text = "O meu nome é " + nome + " " + sobrenome + " e a minha idade é
" + idade.ToString() + " anos.";
continuação de Variáveis do tipo Alfanumérico...

string texto = “Wagner da Paixão Santos”;

char caracter1 = ‘a’;


char caracter2 = ‘5’;

Variável do tipo Booleana (verdadeiro ou falso)

bool decisao1 = true;


bool decisao2 = false;

Introdução a Variáveis
no C#
Facebook Twitter
(18) (0)

Veja neste artigo dicas de como criar variáveis no C#, além de entender os
principais tipos de variáveis.

No desenvolvimento de software um recurso muito utilizado é a variável e este artigo


pretende demonstrar como criá-las, os tipos e algumas regras para criação das mesmas.

As variáveis são utilizadas para armazenar informações na memória em tempo de


execução da aplicação, isso significa que essas informações estarão disponíveis
enquanto a aplicação estiver em execução e, mais precisamente, enquanto a classe ou
método em que ela foi criada estiver em execução.

No C# toda variável deve ter: modificador de acesso, tipo de dados e nome.


A regra para nomear uma variável é que o nome dela sempre comece por uma letra ou
_. No meio do nome podem-se usar números, mas não se devem usar caracteres
especiais e também não pode ser uma palavra reservada. Entende-se por palavras
reservadas os comandos do C# e que são facilmente identificadas quando digitadas, por
ficarem da cor azul. Exemplos de palavras que não podem ser utilizadas são: if, for,
while, string e etc.

Exemplo de variável:

int numero;

No exemplo, int é o tipo e numero é o nome da variável. Mas, e o modificador?

Como dito anteriormente, toda variável deve ter o modificador de acesso. No exemplo
acima não tem um modificador porque no C# quando não há um modificador em uma
variável e é atribuído a ele o modificador padrão private.

Modificadores
No caso das varáveis, os modificadores definem a visibilidade delas, se elas poderão ser
acessadas por outras classes que não seja a sua própria, se serão acessadas somente por
classes derivadas a classe que ela está e assim por diante, como mostra a Listagem 1.

Listagem 1.Utilização de modificadores.

Public int numero;

No C# existem os seguintes modificadores:

Modificador Funcionamento

public O acesso não é restrito.

protected O acesso é limitado às classes ou tipos derivados da classe que a variável está.

Internal O acesso é limitado ao conjunto de módulos (assembly) corrente.

protected internal O acesso é limitado ao conjunto corrente ou tipos derivados da classe recipiente.

private O acesso é limitado à classe que a variável está.


Tabela 1: Modificadores de acesso -Fonte: Adaptado de Microsoft

Tipos de dados
C# é uma linguagem de programação fortemente tipada, isso significa que todas as
variáveis e objetos devem ter um tipo declarado. Os tipos de dados são divididos
em value types e reference types. Os value types são derivados de System.ValueType
e reference types são derivados de System.Object.

Tipo de dados Intervalo

byte 0 ..255

sbyte -128 ..127

short -32,768 ..32,767

ushort 0 ..65,535

int -2,147,483,648 ..2,147,483,647

uint 0 ..4,294,967,295

long -9,223,372,036,854,775,808 ..9,223,372,036,854,775,807

ulong 0 ..18,446,744,073,709,551,615

float -3.402823e38 ..3.402823e38

double -1.79769313486232e308 ..1.79769313486232e308

decimal -79228162514264337593543950335 ..79228162514264337593543950335

char U+0000 .. U+ffff


float -3.402823e38 ..3.402823e38

double -1.79769313486232e308 ..1.79769313486232e308

Variáveis Value Type


As variáveis value type contém dentro delas um valor, enquanto as reference type
contém uma referência. Isso significa que se copiar uma variável do tipo value type para
dentro de outra o valor é copiado e, se o mesmo for feito com uma do tipo reference
type será copiado apenas a referência do objeto.

Dentro de Value Type existem duas categorias: struct e enum.

a) Struct: é dividida em tipos numéricos, bool e estruturas personalizadas pelo usuário.

Os tipos numéricos são os presentes na tabela abaixo:

Tipo bool representa um valor que pode ser verdadeiro ou falso, o seu valor padrão é
false.

b) Enum: permite criar um tipo que é formado por várias constantes. Normalmente é
usada quando em algum momento existe a necessidade de um atributo que pode ter
múltiplos valores, como por exemplo, em uma aplicação de venda que tem a tela de
pedidos: cada pedido tem a sua situação que pode ser Aberto, Faturado e Cancelado.
Para criar a classe Pedido, o tipo do atributo situação será int, conforme mostra
a Listagem 2.

Listagem 2. Classe pedido.

class Pedido

public int numero;

public DateTime dataHora;

public int situacao;


}

Agora é necessária a criação do Enum Situacao. Um Enum é criado da mesma porque se


cria uma classe, mas ao invés de utilizar a palavra class usa-se enum, conforme mostra
a Listagem 3.

Listagem 3. Enum Situacao.

enum Situacao

Aberto,

Faturado,

Cancelado

Na Listagem 4 contém o código da utilização deste Enum que foi criado.

Listagem 4. Utilizando o Enum criado.

class Program

static void Main(string[] args)

Pedido pedido = new Pedido();

pedido.numero = 1;

pedido.dataHora = DateTime.Now;
pedido.situacao = (int) Situacao.Faturado;

Console.WriteLine("Número do pedido: "

+ pedido.numero.ToString());

Console.WriteLine("Número do pedido: "

+ pedido.dataHora.ToString());

Console.WriteLine("Número do pedido: "

+ pedido.situacao.ToString());

Console.ReadLine();

O Enum por padrão retorna int e neste exemplo temos três opções, onde cada uma
possui um índice. Então quando o valor de um Enum é atribuído a um outro atributo ,o
que é atribuído é seu índice, no caso do exemplo da Listagem 4 é atribuído o valor 1.

Variáveis Reference Types


As variáveis reference type armazenam apenas a referência do objeto. Os tipos de
referência são: class, interface, delegate, object, string e Array.

a) Tipo object: todos os tipos são derivados da classe Object, sendo assim é possível
converter qualquer tipo para object.

b) Tipo string: é utilizado para se armazenar caracteres e uma string deve estar entre
aspas, como mostra a Listagem 5.
Listagem 5. Utilização de string

string nome = "DevMedia";

Para concatenar (juntar) uma ou mais strings é usando o sinal de +, como mostra
a Listagem 6.

Listagem 6. Concatenando strings.

string a = "C#";

string b = ".net";

string c = a + b;

Outro recurso também é a possibilidade de se obter um determinado caractere de uma


string, como mostra a Listagem 7.

Listagem 7. Pegando um caractere de uma string.

string a = "C#";

string b = ".net";

string c = a + b;

char d = c[3];

A variável d vai ficar com o valor da letra n que é a letra que está no índice 3, conforme
informado no código.

Também é possível fazer a mesma coisa com uma palavra, como mostra a Listagem 8.

Listagem 8. Pegando um caractere de uma palavra.

char d = "C#.net"[3];
Existem muitas outras operações para fazer com variáveis, mas isso é assunto para um
próximo artigo. O que foi mostrado nesse artigo são passos iniciais para entender como
funcionam as variáveis no C#.

Tipos de Conversões de Variáveis:

(Implícito, Explícito, Boxing, Unboxing e Var)

Uma situação comum para o desenvolvedor é a conversão de valores em uma


aplicação, por exemplo, quando possui um formulário onde o usuário digita as
informações e as mesmas são passadas para variáveis ou atributos de objetos para
depois serem gravadas no banco de dados ou realizar alguma outra operação.

No nosso exemplo, os dados vindos do formulário serão do tipo String e no caso de um


campo que contenha número precisará ser convertido para inteiro para poder ser
armazenado na variável. O conteúdo poderá ser convertido para inteiro utilizando o
código da Listagem 1.

Listagem 1. Conversão de valor string para inteiro.

int numero;

numero = Convert.ToInt32(textBox1.Text);

Tipos de conversão

No C# existem vários tipos de conversões de dados. Vejamos alguns deles.

1. Conversão implícita: é aquela em que não é necessário nenhum código especial,


pois se trata de uma conversão segura, sem risco de perda de dados. Observe
a Listagem 2.

Listagem 2. Conversão implícita

int numero = 1234;

decimal numero2 = numero;


A Tabela 1 contém os tipos que permitem conversões implícitas.

Tabela 1. Tabela de conversões numéricas implícitas - Fonte: Adaptado de Microsoft

De Para

sbyte short,int,long,float,double, ou decimal

Byte short,ushort,int,uint,long,ulong,float,double, ou decimal

short int,long,float,double, oudecimal

ushort int,uint,long,ulong,float,double, ou decimal

int long,float,double, oudecimal

uint long,ulong,float,double, oudecimal

Long float,double, oudecimal

char ushort,int,uint,long,ulong,float,double, ou decimal

float double

ulong float,double, ou decimal

2.Conversão explícita (casts): esse tipo de conversão necessita de um operador. É


realizado quando há a necessidade de se converter um valor e pode ocorrer perda de
informações.

Para fazer um cast, deve ser informado o tipo entre parênteses na frente da variável que
será convertida, como é demonstrado na Listagem 3.

Listagem 3. Exemplo de cast.

double num = 123.4;

int num2;

De Para
sbyte byte,ushort,uint,ulong, ouchar

Byte Sbyteou char

short sbyte,byte,ushort,uint,ulong, ouchar

ushort sbyte,byte,short, ouchar

int sbyte,byte,short,ushort,uint,ulong ou char

uint sbyte,byte,short,ushort,int ouchar

Long sbyte,byte,short,ushort,int,uint,ulong ouchar

ulong sbyte,byte,short,ushort,int,uint,long ouchar

char sbyte,byte oushort

float sbyte,byte,short,ushort,int,uint,long,ulong,char oudecimal

double sbyte,byte,short,ushort,int,uint,long,ulong,char,float ou decimal

decimal sbyte,byte,short,ushort,int,uint,long,ulong,char,float oudouble

num2 = (int)num;

A Tabela 2 contém as possíveis conversões explicitas que podem ser realizadas.

Tabela 2. Possíveis conversões explicitas - Fonte: Adaptado de Microsoft.

É preciso cuidado e atenção ao utilizar conversões explicitas devidos as possíveis


perdas de informações e arredondamentos.

3.Conversões definidas pelo usuário: são conversões realizadas por métodos


criados pelos desenvolvedores que contém o código que converta as informações para
a maneira desejada.
4.Conversões com classes auxiliares: são as conversões em que, por exemplo, o
desenvolvedor utiliza a classe System.Convert para converter para o tipo desejado ou
o método Parse dos tipos de dados.

Neste link: http://msdn.microsoft.com/pt-br/library/system.convert.aspx, há uma tabela


que contém todos os métodos e mostra as possíveis conversões utilizando a classe
System.Convert.

A Listagem 4 demonstra como utilizar a classe Convert.

Listagem 4. Exemplo de uso da classe Convert.

int num;

num = Convert.ToInt32("123");

A Listagem 5 demonstra como realizar uma conversão utilizando o método Parse.

Listagem 5. Exemplo de uso do método parse.

int num;

num = Int32.Parse("123");

No caso da Listagem 4 foi realizada uma conversão para inteiro, mas o método Parse
está presente em outros tipos de dados.

Também existe o método TryParse que é como o método Parse. A diferença é que ele
lança uma exceção se algum erro ocorrer.

Outra conversão que é bastante útil é a conversão para String. No caso do código
da Listagem 4, se for necessário exibir o número que está na variável num através de
um MessageBox, será necessário converte-lo para String. Isso é possível utilizando o
método ToString, conforme mostra a Listagem 6.

Listagem 6. Exemplo de uso do método ToString.

int num;

num = Int32.Parse("123");
MessageBox.Show(num.ToString());

5. Boxing : é uma conversão implícita que converte um valor de tipo de valor para um
tipo de objeto, ou seja, de um Value Type para Reference Type.

Na Listagem 7 temos uma demonstração de um boxing.

Listagem 7. Exemplo de boxing.

int num;

num = 123;

object o;

o = num;

6.Unboxing é quando acontece o inverso, ou seja, quando há uma conversão de um


tipo de objeto para um tipo de valor, de Reference Type para Value Type.

A Listagem 8 contém o código que demonstra um unboxing.

Listagem 8. Exemplo de unboxing.

int num;

num = 123;

object o;

o = num;

int num2;
num2 = (int)o;

7.Var: essa palavra reservada pode ser usada como um tipo de dados para declaração
de variáveis. Quando ela é usada, o compilador irá escolher qual é o tipo de dados mais
apropriado para a variável.

A Listagem 9 demonstra o seu uso.

Listagem 9. Exemplo de uso de var.

var num = 123;

var texto = "Olá";

Vale ressaltar que o var não significa variant, apenas significa que o compilador é que
vai decidir o tipo apropriado.

Há também algumas considerações que devem ser levadas em conta:

 Var só pode ser usada em variáveis locais e devem ser inicializadas;


 Não podem ser inicializadas com valor nulo;
 Não podem ser utilizadas em campos no escopo de classe;
 Não pode ser usada em expressões de inicializações;
 Múltiplas variáveis não podem ser inicializadas em uma única instrução;
 Se existir um tipo chamado Var no escopo, isso será resolvido e esse nome de
tipo não será tratado como parte de uma declaração de variável local implícita.

Com isso, neste artigo foi apresentado como realizar conversão de dados entre tipos. A
conversão de tipos é uma operação muito útil e frequentemente utilizada no
desenvolvimento de uma aplicação, mas deve-se sempre tomar cuidado para não
ocorrer perda de dados e também é preciso se atentar sobre os arredondamentos que
ocorrem em algumas conversões com casas decimais. Também foi explicado o conceito
de boxing e unboxing e apresentado o uso a palavra reservada var.
Documentação: C#:
if/else e o operador
ternário
Facebook Twitter
(7) (0)

Nesta documentação você irá aprender a utilizar as estruturas de condição


if/else e o operador ternário, os quais possibilitam a criação de diferentes fluxos
de execução para atender a lógica de um programa em C#.

Aprender a criar estruturas de condição é fundamental para programar a lógica de


negócio de um sistema. Essas estruturas permitem a execução de diferentes linhas de
código (diferentes fluxos de execução) a partir da avaliação de uma condição. Na
linguagem C# temos dois recursos com esse propósito: if/elsee switch/case.

Neste documento apresentamos a opção if/else e o operador ternário.

Tópicos

if/else

else if

Operador ternário

Exemplo prático

if/else
A estrutura condicional if/else é um recurso que indica quais instruções o sistema
deve processar de acordo com uma expressão booleana. Assim, o sistema testa se uma
condição é verdadeira e então executa comandos de acordo com esse resultado.

Sintaxe do if/else:
if (expressão booleana)

// código 1

else

// código 2

Caso a expressão booleana seja verdadeira, as instruções entre chaves presentes no


código 1 serão executadas; caso contrário, serão executadas as instruções presentes no
código 2.

As chaves, ou delimitadores de bloco, têm a função de agrupar um conjunto de


instruções. O uso desses delimitadores é opcional caso haja apenas uma linha de código.
Ainda assim, seu uso é recomendado, pois essa prática facilita a leitura e manutenção do
código, tornando-o mais legível.

Nota: A declaração do else não é obrigatória. Para muitas situações apenas o if é suficiente para a lógica sendo
implementada.

else if
Complementar ao if/else temos o operador else if que traz uma nova condição a
ser testada no caso de falha no teste da condição anterior.

Sintaxe do else if:

if (expressão booleana 1)

// código 1

else if (expressão booleana 2)

// código 2

else

{
// código 3

Dessa forma, é testada a expressão booleana 1. Caso ela seja atendida, o bloco de
código 1 é executado. Caso não seja, testamos a expressão 2. Sendo atendida, o bloco de
código 2 é executado. Não sendo atendida, o programa executa o bloco de código 3.

Saiba que podemos criar vários else if, o que nos possibilita atender a cenários com
três ou mais condições a serem avaliadas.

Nota: Em casos com muitos else if, considere o uso da estrutura switch/case, ou mesmo padrões de
projeto.

Operador ternário
O operador ternário é um recurso para tomada de decisões com objetivo similar ao
do if/else mas que é codificado em apenas uma linha.

Sintaxe do operador ternário:

expressão booleana ? código 1 : código 2;

Caso a expressão booleana retorne verdadeiro, é executado o código declarado logo


após o ponto de interrogação (?); do contrário, o sistema irá executar o código que vem
após os dois pontos (:).

As principais diferenças entre a estrutura condicional if/else e o operador ternário é


que esse último, como já mencionado, é descrito em apenas uma linha de código,
executando apenas uma instrução, e geralmente está contido em uma instrução maior.

Exemplo prático
Para demonstrar a estrutura condicional if/else, considere um programa que verifica
se um aluno foi aprovado, se está em recuperação ou se foi reprovado. Para ser
aprovado, o aluno precisa ter média maior ou igual (>=) a 7. Para a
recuperação, ele precisa ter média menor (<) que 7 e (&&) maior ou igual
(>=) a 5. Por fim, para ser reprovado, precisa ter média menor (<) do que 5.

Note que temos três condições a serem verificadas, o que nos leva à necessidade de uso
do if, else if, else.

Exemplo de uso:
double media = 8;

if (media >= 7)

Console.WriteLine("Aluno aprovado!");

else if (media < 7 && media >=5)

Console.WriteLine("Aluno em recuperação!");

else

Console.WriteLine("Aluno reprovado!");

Run

Para demonstrar um exemplo de uso do operador ternário, suponha que precisamos


apresentar ao aluno o resultado com base na sua média. Para isso, criamos uma variável
que recebe um texto padrão e que depois será incrementado para informar se o aluno
está aprovado ou reprovado. Essa concatenação será feita a partir do resultado obtido
com o operador ternário, que, neste caso, também nos devolve um texto.

Exemplo de uso:

double media = 8;

string resultado = "Olá aluno, você foi ";

resultado += media >= 7 ? "aprovado." : "reprovado.";

Console.WriteLine(resultado);

Run
Documentação: C#:
Switch/Case
Facebook Twitter
(6) (0)

Nesta documentação você irá aprender a utilizar a estrutura condicional


Switch/Case da linguagem C#.

Aprender a criar estruturas de condição é fundamental para programar a lógica de um


sistema. Essas estruturas permitem a execução de diferentes linhas de código (diferentes
fluxos de execução) a partir da avaliação de uma condição. Na linguagem C# temos
dois recursos com esse propósito: if/else e switch/case.

Curso relacionado: O que é C#?

Como utilizar a estrutura condicional


Switch/Case do C#
Neste documento apresentamos como criar estruturas de condição utilizando
o switch/case.

Tópicos

Switch/Case

Default

Exemplo prático

Switch/Case
Switch/case é uma estrutura de condição que define o código a ser executado com base
em uma comparação de valores.
Para que isso fique mais claro, vejamos a sintaxe do switch/case:

switch (variável ou valor)

case valor1:

// código 1

break;

case valor2:

// código 2

break;

Na linha 1, em switch (variável ou valor), definimos a variável ou valor


que desejamos comparar. Na linha 3, informamos que se o valor declarado
neste case for igual ao contido no switch, código 1 será executado. O mesmo
comportamento se aplica ao segundo case. Ademais, caso o valor contido
no switch não seja atendido em uma das condições, nenhum bloco será executado.

E o comando break? O comando break é utilizado para especificar a última linha de


código a ser executada dentro da condição. Se não declarado, os códigos implementados
dentro dos cases subsequentes serão executados.

Caso deseje executar o mesmo código para valores diferentes, você pode programar
o switch/case da seguinte forma:

switch (variável ou valor)

case valor1:

case valor2:

case valor3:

// código 1

break;

case valor4:

case valor5:

case valor6:

// código 2

break;

}
Neste caso, declaramos dois blocos de código. O primeiro (código 1) será executado
caso o valor do switch seja igual a valor1, valor2 ou valor3; e o segundo (código 2),
caso o valor seja igual a valor4, valor5 ou valor6.

Default
O operador default é utilizado quando precisamos definir um fluxo alternativo para
as situações em que o valor contido no switch não seja atendido por nenhum
dos cases especificados.

switch (variável ou valor)

case valor1:

// código 1

break;

case valor2:

// código 2

break;

default:

// código 3

break;

Caso o valor do switch não seja igual a um dos valores contidos nos cases, o sistema
irá executar o código implementado no bloco default; neste exemplo, o código 3.

Nota: O operador default é opcional e o código nele implementado também deve ser finalizado com a
instrução break.

Exemplo prático
Para demonstrar um exemplo de switch/case, considere um programa que informa
ao usuário a quantidade de dias de um mês a partir do nome de um mês por ele
informado.

Com esse intuito, a partir de uma variável do tipo string (mes) implementamos o código
abaixo:
switch (mes)

case "Janeiro":

case "Março":

case "Maio":

case "Julho":

case "Agosto":

case "Outubro":

case "Dezembro":

Console.WriteLine("Este mês tem 31 dias");

break;

case "Fevereiro":

Console.WriteLine("Este mês tem 28 ou 29 dias");

break;

default:

Console.WriteLine("Este mês tem 30 dias");

break;

Run

Com esse código informamos ao sistema para comparar o valor da variável mes aos
valores contidos nos cases. Se o valor da variável mes for igual a “Janeiro”, “Março”,
“Maio”, “Julho”, “Agosto”, “Outubro” ou “Dezembro”, o sistema irá informar que o
mês tem 31 dias; se o valor presente em mes for “Fevereiro”, que o mês tem 28 ou 29
dias; e, para qualquer outro valor, que o mês possui 30 dias.
Documentação: C#:
Estrutura de repetição
While
Facebook Twitter
(6) (0)

Nesta documentação você irá aprender a utilizar a estrutura de repetição


WHILE da linguagem C#.

Utilizando a estrutura de repetição WHILE


com C#
Quando precisamos executar um bloco de código repetidas vezes devemos recorrer às
estruturas de repetição. Assim, conseguimos programar o código desejado sem que para
isso criemos cópias desse mesmo conjunto de instruções. Com a linguagem C# temos
três opções para implementar estruturas de repetição: For, Foreach e While.

Curso relacionado: Curso de C# Avançado

Neste documento apresentamos como utilizar o While e o do while.

Tópicos

While

Do-while

Break

Continue

Exemplo prático
While
O while trata-se da estrutura de repetição mais utilizada quando programamos com
C#. Com ela, enquanto a condição for verdadeira o bloco de código será executado.
Primeiramente o sistema testa essa condição. Caso verdadeira, executa as linhas
declaradas dentro do while; do contrário, sai do loop.

Sintaxe da estrutura de repetição while:

while (condição)

//bloco de código

A sintaxe consiste em declarar a instrução while e, entre parênteses, a condição a ser


testada. Em seguida, entre chaves, o bloco de código a ser executado a cada iteração.

Nota: No bloco de código deve ser implementada alguma lógica que torne a condição falsa. Caso isso não seja feita
criaremos um loop infinito, que deve ser evitado por “travar” a aplicação.

Do-while
Esta estrutura de repetição funciona de forma semelhante ao while, porém, ela garante
que o código dentro do loop seja executado pelo menos uma vez. Para isso, a condição é
declarada após o bloco de código.

Sintaxe da estrutura de repetição do-while:

do

//bloco de código

} while (condição);

A sintaxe consiste na declaração da instrução do seguida do bloco de código. Por fim,


tem-se a instrução while, que traz entre parênteses o código a ser testado.

Nota: Conforme demonstrado, essa estrutura deve ser finalizada com um ponto e vírgula (;). Além disso, lembre-se
de inserir no bloco de código a lógica necessária para que a condição se torne falsa em alguma execução do loop.
Break
Por padrão um loop só é finalizado quando a condição de parada é atendida. Porém é
possível interromper a execução de um laço de repetição utilizando o comando break.
Esse comando não espera nenhum parâmetro, portanto basta informar a palavra
reservada break na linha em que o loop deve ser interrompido.

No código abaixo o laço deveria ser executado enquanto a variável i fosse menor que
10. Porém, nas linhas 4 e 5 usamos o comando break para interromper o loop caso i seja
igual a 5. Neste caso, as instruções das linhas 6 e 7 não serão executadas, então o
resultado desse código será a impressão dos valores 1 a 4.

01 int i = 1;

02 while (i < 10)

03 {

04 if(i == 5)

05 break;

06 Console.WriteLine(i);

07 i++;

08 }

Run

Continue
Além do break há outro comando capaz de modificar o fluxo natural de execução de
loop: o continue. Esse comando faz com que a execução da iteração/repetição seja
finalizada e o fluxo seja direcionado novamente para o início do loop. Dessa forma a
condição de parada será novamente avaliada e o loop será executado novamente.

No código abaixo o laço deveria ser executado enquanto a variável i fosse menor que
10, imprimindo todos os valores entre 1 e 10. Porém, nas linhas 4 e 5 fazemos com que
o loop seja desviado caso o valor de i seja divisível por 2 (número par). Quando isso
ocorrer a linha 6 não será executada e o fluxo do código será enviado novamente para a
linha 2, executando o while mais uma vez. O resultado desse código será então a
impressão dos valores entre 1 e 10 que não são múltiplos de 2 (números ímpares).

01 int i = 0;

02 while(i < 10){


03 i++;

04 if(i %2 == 0)

05 continue;

06 Console.WriteLine(i);

07 }

Run

Exemplo prático
Para demonstrar o funcionamento do while, criamos um loop que imprimirá no
console os valores de 1 a 10.

Exemplo de uso:

int i = 1;

while (i <= 10)

Console.WriteLine(i);

i++;

Run

Analisando o código:

Linha 01: Declaramos a variável i, cujo valor será testado a cada iteração;

Linha 02: Declaramos o while e a condição a ser testada (i ser menor ou igual a 10);

Linha 04: Informamos ao sistema para imprimir no console o valor de i;

Linha 05: Modificamos o valor de i. Essa é a linha responsável por alterar o valor dessa variável e fazer com que
a condição declarada no while se torne falsa, interrompendo a execução do loop.
Documentação: C#:
Estrutura de repetição
Foreach
Facebook Twitter
(6) (0)

Nesta documentação você aprenderá a utilizar a estrutura de repetição


Foreach da linguagem C#.

As estruturas de repetição possibilitam executar o mesmo código diversas vezes sem


que seja necessário repetir instruções. Assim, sempre que você precisar programar uma
lógica que necessita ser processada mais de uma vez, considere o uso desse recurso.
A linguagem C# nos fornece três estruturas de repetição: For, Foreach e While.

Curso relacionado: Curso Básico de C#

Neste documento apresentamos como utilizar o foreach.

Tópicos

Foreach

Exemplo prático

Foreach
O foreach é um recurso do C# que possibilita executar um conjunto de comandos
para cada elemento presente em uma coleção (Array, List, Stack, Queue e outras).
Portanto, diferentemente do while e do for, não precisamos definir uma condição de
parada. Isso é definido de forma implícita, pelo tamanho da coleção.

Sintaxe da estrutura de repetição foreach:

foreach (tipo elemento in coleção)


{

//bloco de código

Na declaração do foreach, entre parênteses criamos


um elemento do tipo utilizado na coleção e, com o operador in, informamos a
coleção a ser percorrida. Assim, a cada loop os dados presentes em uma posição da
coleção são atribuídos ao elemento. Por fim, entre chaves, inserimos o código a ser
executado no loop.

Exemplo prático
Considere que desejamos imprimir na tela todos os nomes presentes em um array. Para
isso, em vez de criar um while e nos preocuparmos com a condição de parada,
podemos fazer uso do foreach.

Exemplo de uso:

string[] nomes = { “André”, “Bruna”, “Carla”, “Daniel” };

foreach (string nome in nomes)

Console.WriteLine(nome);

Run

Analisando o código:

Linha 01: Criamos um array, chamado nomes, do tipo string, onde armazenamos os nomes de algumas pessoas;

Linha 03: Declaramos a estrutura foreach, que traz entre parênteses o tipo (string) e o nome (nome) do
elemento que irá receber os dados contidos na coleção a cada iteração. Ainda nessa linha, após o
operador in informamos que a estrutura irá iterar pelo array de strings (nomes);

Linhas 04 a 06: Declaramos o bloco de código que será executado a cada loop - neste caso, a
instrução Console.WriteLine(nome).
Documentação: C#:
Trabalhando com
arrays
Facebook Twitter
(4) (0)

Os arrays representam uma das coleções mais utilizadas em qualquer


linguagem de programação e no C# não é diferente. Neste documento veremos
como declarar e utilizar arrays na linguagem C#.

Arrays são coleções de dados extremamente importantes em qualquer linguagem de


programação. Sua grande vantagem está no fato de serem estruturas indexadas, ou seja,
os itens dos arrays podem ser acessados através de índices, o que garante grande
velocidade para essas operações.

Neste documento veremos como declarar e utilizar arrays na linguagem C#.

Tópicos

Declaração de arrays

Inserção de dados no array

Acesso aos dados do array

Iterações sobre arrays

Exemplo prático

Declaração de arrays
A declaração de arrays é algo relativamente simples no C#. No entanto, é importante
destacar que é essencial sabermos exatamente o tamanho que o array terá, o que acaba
sendo uma limitação em alguns casos.

A declaração padrão para um array qualquer obedece a seguinte estrutura:

tipo[] nomeDoArray = new tipo[tamanho_do_array];

Por exemplo, para um array do tipo inteiro (int), com 10 posições, a declaração seria da
seguinte forma:

int[] array = new int[10];

Inserção de dados no array


Existem duas formas básicas para inserirmos dados nos arrays em C#. A primeira é
realizarmos isso durante a declaração do mesmo. Dessa forma, poderíamos ter a
declaração das seguintes formas:

int[] array1 = new int[5] { 1, 3, 7, 12, 8 };

int[] array2 = { 1, 3, 2, 7, 6 };

No primeiro caso temos uma declaração explícita de um array que conterá cinco
números inteiros. Aqui, se passarmos menos ou mais de cinco números entre as chaves
haverá um erro de compilação, uma vez que sabemos que o array tem cinco posições.

No segundo caso, a declaração da quantidade de elementos no array é implícita e


depende de quantos itens temos entre as chaves. Nesse exemplo passamos cinco
números; assim, array2 possui cinco posições e não há possibilidade de erro de
compilação.

Outra forma de inserirmos os dados no array é acessando o item através de seu índice.
Por exemplo, podemos fazer uma declaração simples e então iterarmos sobre todos os
índices do array, adicionando os valores específicos para cada um deles, como a seguir:

01 int[] array = new int[50];

02 for (int i = 0; i < 50; i++)

03 {
04 array[i] = i + 1;

05 }

Linha 01: declaração de um array inteiro com 50 posições;

Linha 02: definição de um loop for com 50 iterações, entre 0 e 49, com incremento unitário;

Linha 04: inserção de dados no array. Para cada iteração, o array, na posição i, receberá o valor de i + 1, ou seja,
array[0] == 1, array[1] == 2, e assim por diante.

Acesso aos dados do array


O acesso aos dados do array é feito de forma extremamente simples, uma vez que
estamos lidando com uma coleção indexada. Assim, basta acessarmos o índice
específico e podemos obter o valor do elemento, como podemos ver abaixo:

Console.WriteLine(array[10]);

É importante notarmos, entretanto, que esse tipo de acesso pode gerar problemas. Se, no
caso anterior, tivermos um array com menos que 11 posições (o índice 10 é o 11º
elemento da coleção (0 a 10)), o código irá levantar uma exceção do
tipo IndexOutOfRangeException. Portanto, é fundamental assegurar que estamos
acessando um índice válido.

Iterações sobre os arrays


Uma das principais características das coleções, em qualquer linguagem de
programação, são as iterações que podem ser realizadas sobre elas. No caso dos arrays
em C# isso não é diferente. Assim, para iterarmos sobre os arrays existem basicamente
duas opções: loops em que acessamos os índices no array diretamente (while ou for) ou
o loop foreach, que faz isso internamente para nós. No código abaixo temos ambas as
opções:

01 for (int i = 0; i < 10; i++)

02 {

03 Console.WriteLine(array[i]);

04 }

05

06 int i = 0;
07 while (i < 10)

08 {

09 Console.WriteLine(array[i]);

10 i++;

11 }

12

13 foreach (int x in array)

14 {

15 Console.WriteLine(x);

16 }

Linhas 01 a 04: acesso via índices do array utilizando um loop for, no qual o iterador é o inteiro “i”. Esse tipo de
acesso era bem comum em linguagens como C e C++;

Linhas 06 a 11: acesso via índices do array utilizando um loop while, no qual o iterador é o inteiro “i”. Esse tipo
de acesso é muito similar ao que vimos no loop for. Entretanto, existem algumas operações que precisam ser
realizadas manualmente, como a declaração da variável inteira “i” (linha 06) e o incremento do mesmo (linha 10);

Linhas 13 a 16: acesso através do loop foreach, que itera sobre todos os elementos da coleção. Esse tipo de
utilização só é possível porque no C# todas as coleções implementam a interface IEnumerable, ou sua forma
genérica IEnumerable<T>, que oferecem a base do suporte às iterações via foreach.

Exemplo prático
A seguir temos um exemplo prático (mostrado em execução na Figura 1) de como
utilizar os arrays em nossas aplicações C#:

01 string[] pilotos = new string[4] { "Ayrton Senna", "Michael Schumacher",

"Lewis Hamilton", "Alain Prost" };

02 Console.WriteLine(pilotos[3]);

03 Console.WriteLine();

04 pilotos[3] = "Rubens Barrichello";

05 foreach (string piloto in pilotos)

06 {

07 Console.WriteLine(piloto);

08 }

RUN
Linha 01: declaração do array “pilotos”, contendo quatro strings pré-definidas;

Linha 02: acesso ao índice “3” do array, mostrando na tela o nome “Alain Prost”;

Linha 03: inserção de linha em branco;

Linha 04: substituição do elemento no índice “3” do array (“Alain Prost”), que agora recebe o nome de “Rubens
Barrichello”;

Linhas 05 a 08: utilização do loop foreach para iterar sobre o array. Nesse caso, como o array
implementa IEnumerable<string>, podemos garantir que os quarto pilotos serão mostrados na tela com o código
da linha 06.

Figura 1. Execução do exemplo C# Arrays

Receba nossas novidades


Receber Newsletter!

por Henrique Machado (4) (0)

Ficou com alguma dúvida?


Elio Junior

Boa noite

Gostaria de saber se por exemplo uma array guardaria um objeto

há 3 meses

Joel Rodrigues há 3 meses

DEVMEDIA

Olá, Elio. Tudo bem? Você pode criar arrays de qualquer tipo. Veja abaixo algumas
possibilidade de criação de arrays de objetos (aqui usei uma classe Aluno que tem
apenas Nome):

//Instanciando um array vazio

Aluno[] alunos1 = new Aluno[5];

//Instanciando um array com tamanho e itens predefinidos

Aluno[] alunos2 = new Aluno[5]

new Aluno { Nome = "João" },

new Aluno { Nome = "Maria" },

new Aluno { Nome = "Pedro" },

new Aluno { Nome = "Carlos" },


new Aluno { Nome = "Tereza" }

};

//Instanciando um array com itens predefinidos

Aluno[] alunos3 =

new Aluno { Nome = "Carla" },

new Aluno { Nome = "Antonio" },

new Aluno { Nome = "Marcia" }

};

Documentação:
Nullable Types em C#
Facebook Twitter
(6) (0)

Nesta documentação você aprenderá o que é e como utilizar Nullable Types,


recurso da linguagem C# para manipulação de valores nulos em tipos não
anuláveis.

Nullable Types é um recurso do C# que nos permite atribuir o valor null a um tipo de
dado que, por padrão, não aceita valores nulos: os tipos primitivos.
Esse recurso é útil quando precisamos realizar operações em banco de dados e
desejamos armazenar um valor nulo em um campo que, posteriormente, receberá um
valor de tipo primitivo, como int, float, bool, etc.

Curso relacionado: Curso de Introdução ao C#

Tópicos

Utilizando nullable types


Exemplo prático

Utilizando nullable types


Para fazer a conversão de um tipo em um Nullable Type, basta adicionarmos um ponto
de interrogação (?) logo após a declaração do tipo.

int? x = null;

Com esse código, atribuímos o valor null à variável x, de tipo int. Para verificarmos
se a variável x recebeu algum valor, podemos utilizar uma estrutura condicional que
testa se o seu valor é diferente de null, conforme o código abaixo:

if (x != null)

// x possui um valor inteiro

else

// x possui um valor nulo

Run

Ou podemos simplificar esse código com o uso de um operador ternário:

Console.WriteLine("O valor de x é " + ((x != null) ? "um inteiro" : "nulo"));


Exemplo prático
Para demonstrar esse recurso na prática, criamos uma tabela Clientes com as
colunas IdCliente, Nome e NumeroConta. Neste exemplo, suponha que a
coluna NumeroConta permite a atribuição de valores nulos para que eles sejam
preenchidos posteriormente.

No código abaixo, criamos uma variável do tipo string e uma do tipo int?, que
recebe null e, logo após, solicitamos a inserção desses dados no banco:

string nome = "João";

int? numeroConta = null;

ClienteRepositorio repositorio = new ClienteRepositorio();

repositorio.Inserir(nome, numeroConta);

Após a execução, teríamos uma tabela conforme a Figura 1, com a


coluna NumeroConta com o valor null:

Figura 1. Coluna
NumeroContaNota: Para gravação no banco de dados é necessário que o Nullable Type seja tratado na classe de
persistência. Caso ele tenha valor nulo, será convertido para o tipo nulo do banco de dados em uso.

Manipulação de
Strings em C#
Facebook Twitter
(2) (0)

A manipulação de textos é uma tarefa comum em uma aplicação. Ao


desenvolver um software, em determinado momento pode ser necessário
conhecer mais a fundo como manipular strings.
Atenção: esse artigo tem um vídeo complementar. Clique e
assista!
De que se trata o artigo

Este artigo apresenta as principais rotinas para manipulação de textos do tipo String
utilizando o C#, tarefas do dia a dia como criar, formatar, substituir, copiar, verificar
tamanho e conteúdo nulo, remover caracteres etc.

Para que serve

Conhecer como manipular strings é essencial para que o desenvolvedor possa criar
aplicações em uma determinada linguagem, com C# não é diferente, desde uma simples
apresentação de texto na tela a até inclusão ou remoção de valores dentro da string,
essas tarefas fazem parte do dia a dia do desenvolvedor.

Em que situação o tema é útil

Para manipular strings evitando recriar métodos que já existam, o desenvolvedor precisa
conhecer o que a plataforma de desenvolvimento pode oferecer, pois assim saberá como
lidar com a manipulação de textos da melhor forma e aproveitando os recursos
existentes.

Strings

O desenvolvedor, durante a criação de aplicativos em qualquer linguagem de


programação, lida com algumas tarefas consideradas básicas e essenciais, uma delas é a
manipulação de textos. A linguagem C# juntamente com o framework .net possui
diversos recursos que visam prover suporte a essas tarefas, veremos no artigo como
utilizar alguns desses recursos.

A plataforma .net da Microsoft atualmente é uma das mais poderosas tecnologias para o
desenvolvimento de aplicações, podendo criar aplicativos para diversas finalidades. O
framework é maduro e com tecnologia capaz de proporcionar ao desenvolvedor a
possibilidade de aplicar as melhores práticas de desenvolvimento do mercado.

Desde o início do Framework .net e do Visual C#, diversos recursos dessas tecnologias
foram disponibilizadas para a manipulação de strings, a cada nova versão, novos
recursos são adicionados e melhorias são implementadas visando uma melhor
performance e a inclusão de suporte a tecnologias mais recentes. Tudo isso faz da
plataforma .net uma das principais tecnologias na área de desenvolvimento de software.
Juntamente com o Framework .net e do Visual C#, diversas ferramentas de apoio ou
tecnologias compatíveis foram se integrando a plataforma. Um destaque em relação aos
recursos e produtividade é o Visual Studio, o IDE da Microsoft para a criação de
softwares é parte fundamental do sucesso da plataforma, ele cresce rapidamente
acompanhando o que existe de mais novo no mundo de desenvolvimento de aplicativos.

Atualmente a versão do Framework .net é a 4.0, o Visual Studio está na versão 2010 e
possui diversas divisões, tendo uma versão gratuita chamada Express que é ideal para o
desenvolvedor iniciante na plataforma, esta versão é compatível com os exemplos deste
artigo.

A manipulação de textos é uma tarefa comum em uma aplicação. Ao desenvolver um


software, em determinado momento pode ser necessário conhecer mais a fundo como
manipular strings. Por mais básica que a tarefa possa parecer, ela tem muitos detalhes e
o desenvolvedor que está começando com a sua jornada de aprendizado em uma
linguagem, precisa conhecer como realizar a manipulação de textos em uma aplicação.

Em C# uma string é uma coleção de caracteres, derivada da classe System.String.


Quando utilizamos um objeto do tipo String, nós estamos utilizando um objeto
imutável, ou seja, ele não sofre alterações. Por exemplo, vamos supor que tenhamos
uma variável chamada nome, do tipo string e que contém o valor "alexandre". Se
utilizarmos o método ToUpper() da string, com o objetivo de transformar o conteúdo da
variável em maiúsculo, o objeto string não é alterado, na realidade, é criado um novo e
passado como retorno. O objeto original fica imutável, sendo assim, nenhuma operação
com este objeto poderá modificá-lo.

Outra classe da plataforma .net oferece a possibilidade de mudar o objeto, a classe


StringBuilder. As operações que incidem sobre esse objeto modificam o elemento
originalmente criado, como ele não cria um novo retorno, esse tipo de classe é mais
utilizada em operações onde uma string será incrementada em etapas, como por
exemplo, a criação de uma string dentro de um loop. Conforme o sistema percorre o
loop, a string é criada através do StringBuilder, consumindo menos memória e
aumentando a performance.

"

Coleções
O .NET Framework é um conjunto de ferramentas e classes que suportam o

desenvolvimento de aplicações com a linguagem C# (além de outras

linguagens). Entre as várias classes do framework que são de uso frequente

em nosso dia a dia estão as coleções, que implementam diferentes estruturas

de dados, tais como listas, filas, pilhas, tabelas hash. Nos artigos a seguir você

poderá conhecer algumas das principais classes de coleções que você pode
usar em suas aplicações com C#:
SortedList -
Dominando as classes
do .NET
Facebook Twitter
(3) (0)

Veja neste artigo como e por que utilizar a classe SortedList do namespace
System.Collections, seus principais métodos e propriedades.

Sobre a classe
A classe SortedList funciona, em parte, de forma semelhante ao Hashtable, onde os
elementos são compostos por pares chave/valor. Porém, a principal diferença é que,
como o nome sugere, o SortedList é uma lista ordenada. Os elementos são
automaticamente ordenados pelas chaves no momento em que são inseridos na coleção.

Vale observar que, como no Hashtable, as chaves devem ser valores únicos.

Quando as chaves são de tipos básicos como int ou string, a ordenação é feita sem que
precisemos intervir. Porém, caso se deseje usar chaves de tipos complexos como classes
customizadas, é necessário fornecer, no construtor do SortedList, um objeto IComparer
específico.

Principais propriedades
 Capacity: Um valor inteiro que define a capacidade da lista, ou seja, o número máximo
de itens que podem ser inseridos na coleção.
 Count: Também é um valor do tipo int que retorna (não pode ser alterada
diretamente) a quantidade de itens atualmente constantes na lista.
 IsFixedSize: É um valor booleano que, quando verdadeiro, indica que a lista não pode
ter itens adicionados ou removidos, apenas é permitido alterar os itens existentes.
 IsReadOnly: Também booleano, esta propriedade indica se a coleção pode ser
alterada.
 Keys: Apenas para leitura, retorna uma coleção com as chaves contidas na lista.
 Values: Assim como a anterior, é uma lista do tipo ICollection que contém os valores
da lista (lembrando que a coleção é formada por pares chave/valor)

Principais métodos
 Add: Recebe dois parâmetros do tipo object que devem ser inseridos na coleção,
formando um par chave/valor.
 Clear: Como se pode imaginar, esse método limpa a lista, removendo todos os itens
existentes.
 Contains: Recebe como parâmetro um objeto a ser localizado na lista de chaves e
retorna true caso a chave exista. Caso contrário, o retorno é false.
 ContainsKey: Funciona exatamente como o método anterior, retornando um valor
booleano indicando se uma chave (passada como parâmetro) existe na lista.
 ContainsValue: Semelhante aos dois métodos anteriores, com a diferença de que o
objeto passado como parâmetro é procurado entre a lista de valores e não de chaves.
 CopyTo: Copia os valores da coleção para um vetor unidimensional (primeiro
parâmetro), iniciando a partir de um certo índice (segundo parâmetro).
 GetByIndex: Recebe um parâmetro do tipo inteiro e retorna um objeto representando
o valor contido na posição indicada.
 GetKey: Retorna a chave contida na posição passada como parâmetro (valor inteiro).
 GetKeyList: Este método retorna uma coleção do tipo IList contendo todas as chaves
da lista.
 GetValueList: Também retorna uma coleção de objetos do tipo IList, mas contendo os
valores e não as chaves, como o anterior.
 IndexOfKey: Através desse método, é possível obter o índice do item na lista a partir
de sua chave, a qual é passada como parâmetro.
 IndexOfValue: Semelhante ao anterior, o retorno desse método é o índice na lista do
item cujo valor é passado como parâmetro.
 Remove: Recebe como argumento uma chave e remove da lista o item
correspondente.
 RemoveAt: Remove da lista o item cujo índice é informado como argumento do
método.
 SetByIndex: Este método recebe dois argumentos: um índice (do tipo inteiro) e um
objeto (object). O valor do item na posição indicada passa a ser o objeto passado como
segundo argumento.

Exemplo prático
A seguir é exibido um exemplo de uso da classe SortedList. São inseridos alguns itens
de forma desordenada e, em seguida, toda a lista é exibida na tela.

Listagem 1: Exemplo de uso da classe SortedList

static void Main(string[] args)

SortedList lista = new SortedList();

lista.Add(4, "Quarto");

lista.Add(1, "Primeiro");

lista.Add(2, "Segundo");

lista.Add(3, "Terceiro");

foreach (object chave in lista.Keys)

Console.WriteLine("{0} - {1}", chave.ToString(),


lista[chave].ToString());

Console.Read();

O resultado é exibido a Figura 1.


Figura 1: Resultado do código da Listagem 1

Como se vê, os itens são automaticamente ordenados segundo sua chave.

Conclusão
A classe SortedList, assim como as demais do namespace System.Collections facilita o
trabalho com coleções de objetos, tornando prática a adição, exclusão, localização e,
neste caso, a ordenação.

Queue e Stack –
Trabalhando com os
modelos FIFO e LIFO
em .NET
Facebook Twitter
(4) (0)
Veja neste artigo como trabalhar com os modelos FIFO (First-In, First-Out) e
LIFO (Last-In, First-Out) no .NET Framework, utilizando as classes Queue e
Stack contidas no namespace System.Collections.

Introdução
Na computação, duas das estruturas de dados mais conhecidas e comumente utilizadas
são a FILA e a PILHA. Muitas vezes, é necessário desenvolver classes específicas para
se trabalhar com essas estruturas, mas no .NET Framework já estão presentes
nativamente as classes Queue e Stack cujo funcionamento é baseado no conceito de fila
e pilha, respectivamente.

Neste artigo, conheceremos de forma detalhada essas duas classes. Serão apresentados
seus principais métodos e propriedades, bem como exemplos práticos de uso. Porém,
antes vamos entender melhor o funcionamento dessas estruturas.

Fila (First-in, First-out)


A fila implementa o conceito “First-in, First-out”, ou “Primeiro a entrar, Primeiro a
sair”. Ou seja, o primeiro elemento inserido na lista é também o primeiro a ser
removido. Podemos pensar, por exemplo, em uma fila de banco. A primeira pessoa a
entrar na fila será a primeira a ser atendida, logo, a primeira a sair da fila.

Também é muito comum se usar a expressão FIFO (abreviação de first-in, first-out)


para definir a pilha.

Pilha (Last-in, First-out)


O conceito implementado pela pilha é o de “Last-in, First-out” (também
chamado LIFO ou FILO, de first-in, last-out, que tem o mesmo sentido prático) ou
“Último a entrar, Primeiro a sair”.

Imagine, por exemplo, que você esteja organizando vários pratos. Inicialmente você
organiza todos uns sobre os outros, formando uma pilha de pratos. Logo após, você irá
retirar um a um para organizar no seu devido local. Perceba que o primeiro prato
removido foi o último a ser inserido na pilha, aquele que ficou na parte superior.
Enquanto isso, o primeiro prato inserido na pilha, aquele que está abaixo de todos, será
o último removido.
Agora que os conceitos teóricos já foram apresentados, vejamos na prática como aplicá-
los no .NET Framework.

Observação 1: aqui serão apresentadas as propriedades e métodos relevantes ao


contexto do artigo. Métodos como ToString e GetHashCode, por exemplo, não serão
abordados por serem herdados e não estarem diretamente relacionados com as
estruturas citadas.

Queue
A classe Queue encontra-se no namespace System.Collections e implementa o conceito
de FIFO.

A única propriedade relevante dessa classe nesse contexto é o Count que retorna a
quantidade de elementos atualmente existente na lista.

Por outro lado, mais de um método merece destaque, principalmente o Enqueue e o


Dequeue, como veremos abaixo.

 Enqueue: recebe como parâmetro um objeto a ser inserido na lista, nesse caso, no
final da fila.
 Dequeue: este método não recebe parâmetros, mas retorna o primeiro objeto da fila,
aquele que, pela ordem, é o próximo a ser removido. Após a chamada desse método,
o objeto retornado é também removido da fila.
 Peek: semelhante ao Dequeue, retorna o primeiro objeto da lista, mas não o remove.
Pode ser usado quando se deseja apenas conhecer o valor do primeiro elemento.
 TrimToSize: altera a capacidade da lista para a quantidade atual de elementos. Com
isso, memória é poupada, pois a classe Queue reserva memória para armazenar uma
quantidade de elementos, se nem todos forem usados, parte da memória reservada
fica sem uso. Usando o TrimToSize essa memória é liberada.
 Construtor: a classe Queue possui três sobrecargas do construtor original, que não
recebe nenhum parâmetro. A primeira delas recebe um valor inteiro que define
capacidade inicial da lista. A segunda recebe uma coleção (ICollection) da qual os itens
são copiados para a lista. A terceira recebe um valor inteiro para a capacidade inicial e
um fator de expansão do tipo float. Esse fator de expansão é utilizado para aumentar a
capacidade da fila quando for necessário. Originalmente esse valor é pequeno para
que não seja utilizada memória desnecessariamente.

Caso se conheça previamente o número de elementos a ser inserido da lista, é


interessante definir a capacidade inicial (no constructor), evitando que memória extra
seja reservada. Caso a quantidade de itens ultrapasse a capacidade inicialmente definida,
esta é automaticamente expandida.

Stack
Também contida no namespace Sytem.Collections, a classe Stack implementa o
conceito de LIFO, onde o último elemento inserido é o primeiro a ser removido.

A propriedade Count, também nesse caso, é a única com relevância nesse contexto e
também retorna a quantidade de elementos contidos na lista.

Os métodos que merecem destaque são:

 Push: insere um objeto (recebido como parâmetro) no fim da lista.


 Pop: retorna e remove o elemento do topo da pilha, ou seja, o último que foi inserido.
 Peek: retorna o elemento do topo da pilha, porém sem que este seja removido.
 Construtor: além do construtor original, sem parâmetros, existem duas sobrecargas. A
primeira recebe uma coleção do tipo ICollection da qual os itens são copiados para a
pilha. A segunda recebe um valor inteiro que define a capacidade inicial da lista. Sobre
a capacidade inicial, valem os mesmos comentários feitos a respeito da outra classe.

Métodos em comum
As duas classes possuem métodos em comum que vale a pena citar aqui:

 Clear: remove todos os itens da lista, não sendo possível recuperá-los.


 Contains: recebe como parâmetro um objeto a ser localizado na lista. Caso esse objeto
esteja contido na fila, o retorno desse método é true, caso contrário, o retorno é false.

Exemplos práticos
Para os exemplos a seguir, foi utilizada uma aplicação console.

No exemplo a seguir, são inseridos três elementos em uma fila e, em seguida, todos são
removidos e exibidos na ordem.

Listagem 1: Exemplo de uso da classe Queue em C#

Queue q = new Queue();

//Inserindo três elementos

q.Enqueue(1);
q.Enqueue(2);

q.Enqueue(3);

Console.WriteLine("Listando elementos da fila:");

//Enquanto houver elementos na lista, exibir e remover o primeiro

while (q.Count > 0)

Console.WriteLine(q.Dequeue());

//Exibe a quantidade de elementos restantes, ou seja, zero

Console.WriteLine("A lista agora possui " + q.Count.ToString() + "


elementos.");

Console.Read();

Listagem 2: Exemplo de uso da classe Queue em VB.NET

Dim q As System.Collections.Queue = New System.Collections.Queue()

'Inserindo três elementos

q.Enqueue(1)

q.Enqueue(2)
q.Enqueue(3)

Console.WriteLine("Listando elementos da lista:")

'Enquanto houver elementos na lista, exibir e remover o primeiro

While q.Count > 0

Console.WriteLine(q.Dequeue())

End While

'Exibe a quantidade de elementos restantes, ou seja, zero

Console.WriteLine("A lista possui agora " + q.Count.ToString() + "


elementos.")

Console.Read()

O resultado desse código é mostrado na Figura 1.

Figura 1: Resultado do exemplo com a classe Queue


Note que os itens foram removidos na mesma ordem em que foram inseridos. O
elemento 1 foi o primeiro a ser adicionado e também o primeiro a ser retirado da fila.
Ao fim, não resta nenhum item.

A seguir temos um exemplo semelhante, mas utilizando a classe Stack. Nesse caso, os
itens devem ser exibidos na ordem inversa da adição.

Listagem 3: Exemplo de uso da classe Stack em C#

Stack s = new Stack();

//Inserindo três elementos

s.Push(1);

s.Push(2);

s.Push(3);

Console.WriteLine("Listando elementos da lista:");

//Enquanto houver elementos na lista, exibir e remover o primeiro

while (s.Count > 0)

Console.WriteLine(s.Pop());

//Exibe a quantidade de elementos restantes, ou seja, zero


Console.WriteLine("A lista agora possui " + s.Count.ToString() + "
elementos.");

Console.Read();

Listagem 4: Exemplo de uso da classe Stack em VB.NET

Dim s As System.Collections.Stack = New System.Collections.Stack()

'Inserindo três elementos

s.Push(1)

s.Push(2)

s.Push(3)

Console.WriteLine("Listando elementos da lista:")

'Enquanto houver elementos na lista, exibir e remover o primeiro

While s.Count > 0

Console.WriteLine(s.Pop())

End While

'Exibe a quantidade de elementos restantes, ou seja, zero

Console.WriteLine("A lista possui agora " + s.Count.ToString() + "


elementos.")
Console.Read()

O resultado é exibido na Figura 2.

Figura 2: Resultado do exemplo com a classe Stack

Como era esperado, os itens foram exibidos na ordem contrária a da inserção. O


elemento 3 foi o último a ser inserido, mas foi o primeiro listado. Novamente, ao final
do código não restam itens da pilha.

Conclusão
Na informática, são muitas as aplicações dos modelos FIFO e LIFO e, como vimos, o
.NET Framework facilita bastante o trabalho com estruturas desse tipo, oferecendo duas
classes eficientes e de fácil manipulação.

Sugiro que o leitor busque conhecer os demais métodos e propriedades dessas classes
que, em geral, são comuns às classes que representam coleções nesse namespace.
ArrayList, Hashtable e
BitArray em C#
Facebook Twitter
(4) (0)

Veja nesse artigo uma descrição sobre as classes ArrayList, Hashtable e


BitArray na linguagem C#.

ArrayList
A classe ArrayList é uma solução alternativa ao uso de arrays em .NET, tendo como
principal diferencial a possibilidade de se expandir quando se adiciona novos elementos,
não sendo necessário definir sua capacidade suportada na inicialização. De forma
semelhante, quando um elemento é removido, o tamanho da lista é reduzido, poupando
memória.

Esta classe funciona como uma lista de objetos (System.Object), ou seja, pode-se
adicionar elementos de qualquer tipo a um ArrayList e, mesmo assim, é possível
localizar itens, ordenar a lista, identificar quais elementos são de determinado tipo, entre
outras funcionalidades.

Vejamos os principais métodos da classe:

 Add(object): adiciona um elemento no final da lista.


 AddRange(ICollection): adiciona à lista os elementos de uma coleção pasada como
parâmetro.
 AsParallel(): retorna uma ParallelQuery que utiliza vários processadores, caso existam,
para efetuar consulta aos
 elementos com Linq (inclusive com lambda expressions).
 AsQueryable(): retorna um objeto IQueryable, no qual é possível efetuar consultas
com Linq, inclusive usando
 expressões lambda.
 BinarySearch(object): busca pelo objeto passado como parâmetro na lista, retornando
seu índice, caso exista.
 BinarySearch(object, IComparer): busca pelo objeto parâmetro na lista, considerando a
comparação feita pelo
 IComparer. No caso de classes próprias, é preciso definir um IComparer adequado.
 Cast<Tipo>(): converte os elementos da lista para o Tipo definido, retornando uma
coleção com os novos itens.
 Clear(): remove todos os elementos da lista.
 Clone(): retorna uma cópia do ArrayList.
 Contains(object): retorna true se o objeto passado como parâmetro existe na lista,
caso contrário, retorna false.
 CopyTo(int, Array, int, int): copia uma certa quantidade(quarto parâmetro) de
elementos da lista, a partir de um índice
 (primeiro parâmetro), para um Array(segundo parâmetro) a partir de um outro índice
nesse array(terceiro parâmetro).
 GetEnumerator(): retorna uma coleção IEnumerator com os itens da lista, porém, no
IEnumerator, os elementos não
 podem ser alterados, apenas lidos.
 GetRange(int, int): retorna um ArrayList contendo uma certa quantidade (segundo
parâmetro) de elementos a partir do
 índice especificado (primeiro parâmetro).
 IndexOf(object): esta função retorna o índice (zero-based) do objeto procurado na
lista, caso este exista.
 Insert(int, object): o método Insert adiciona um elemento na lista em uma
determinada posição. Os itens após este
 ponto são deslocados uma posição adiante.
 InsertRange(int, ICollection): semelhante ao Insert, porém insere uma coleção de itens
ao invés de um único elemento.
 LastIndexOf(object): retorna o índice da última ocorrência do objeto procurado na
lista, caso o objeto não seja
 encontrado, o resultado é -1 (assim como na maioria dos métodos de busca).
 OfType<Tipo>(): retorna uma coleção com os itens da lista que são do tipo
especificado. Essa lista resultante, porém, é
 apenas para visualização, seus itens não podem ser alterados.
 Remove(object): caso o objeto passado como parâmetro seja localizado, ele é
removido da lista.
 RemoveAt(int): remove da lista o elemento da posição indicada no parâmetro.
 RemoveRange(int, int): remove uma quantidade de elementos (segundo parâmetro) a
partir do índice indicado (primeiro
 parâmetro).
 Reverse(): inverte a ordem dos elementos.
 SetRange(int, ICollection): insere os itens de uma coleção passada como parâmetro
para o ArrayList a partir de uma
 determinada posição. Os elementos existentes inicialmente nessa faixa de valores são
substituídos pelo que foram
 adicionados.
 Sort(): ordena os elementos em ordem crescente. Caso os elementos sejam de uma
classe específica cuja comparação
 não é explícita, é necessário usar uma sobrecarga desse método que recebe um objeto
IComparer.
 ToArray(): retorna um Array com os objetos contidos na lista.

É fácil perceber que se torna muito mais prático trabalhar com ArrayList em
comparação com arrays, principalmente pelo fato de não precisar se preocupar com o
tamanho da lista e o posicionamento dos elementos.
Hashtable
A classe Hashtable localizada no namespace System.Collections, como o próprio
namespace sugere, é uma coleção de objetos onde cada elemento é composto por uma
chave e um valor. Assim como a classe ArrayList, o Hashtable é flexível, ou seja, pode-
se adicionar objetos de qualquer tipo (pois é uma coleção de object) e seu tamanho se
adapta à quantidade de itens que contém, expandindo e contraindo suas dimensões.

Os elementos podem ser acessados pela respectiva chave e essa chave deve ser única
para cada item, facilitando a localização de um objeto específico. Em comparação com
o acesso aos elementos de um array, o hashtable adapta-se mais facilmente a diversas
situações, pois a chave não necessariamente é um número, pelo contrário, trata-se de um
object. Por exemplo, podemos usar um hashtable para representar um determinado
objeto, quando não é preciso criar um struct ou uma classe para isso. Vejamos a
Listagem 1 onde usamos uma coleção desse tipo para representar as configurações de
um sistema, armazenando informações de vários tipos diferentes.

Como foi dito, as chaves são valores únicos. Ao tentar inserir uma chave duplicada, é
gerada uma exceção do tipo ArgumentException, informando que a chave que se está
tentando inserir já existe.

Listagem 1: Exemplo de utilização do Hashtable

Hashtable config = new Hashtable();

//Valor do tipo Int32

config[“VERSAO”] = 1;

//Valor do tipo String

config[“DIRETORIO”] = “C:\\Sistema”;

//Valor do tipo DateTime

config[“VALIDADE”] = DateTime.Today.AddMonths(1);

Principais propriedades
 Count: Quantidade de elementos contidos na lista.
 Keys: Coleção de objetos usados como chaves na lista.
 Values: Coleção de objetos contidos na lista (os valores e não as chaves).

Principais métodos
 Add(object key, object value): Adiciona um objeto à lista, juntamente com uma chave
que o identifica.
 AsQueryable(): Retorna uma coleção IQueryable, na qual se pode executar consultas
com LINQ (inclusive com Lambda Expressions).
 Cast(): Converte os elementos da coleção para o tipo especificado, os quais são
retornados em uma coleção IEnumerable.
 Clear(): Exclui todos os elementos da lista.
 Clonte(): Cria uma cópia do objeto (da lista).
 Contains(object key) e ContainsKey(object key): funcionam igualmente, retornando
true se a chave especificada nos parâmetros existe na lista, ou false em caso contário.
 ContainsValue(object value): Retorna true se o valor informado está contido na lista
(nos valores e não nas chaves).
 Remove(object key): Exclui da lista o valor relativo à chave informada nos parâmetros.

BitArray
A classe BitArray, como o nome sugere, é uma coleção de bits, que porém são tratados
como bool. Ou seja, os elementos possuem valor true se o respectivo bit vale 1, ou false
se o bit vale 0.

De modo geral, o BitArray funciona como um vetor de bool (bool[]), a principal


diferença é a possibilidade de inverter todos os valores de uma única vez e comparar
duas listas (comparando cada elemento e retornando um BitArray com os resultados).

Principais propriedades
 Count: Quantidade de elementos contidos na coleção (apenas leitura).
 Length: Semelhante ao count, com a diferença de que pode ser alterado.

Principais métodos
 And(BitArray value): Compara dois objetos BitArray, item a item, e retorna um terceiro
BitArray com o resultado da operação AND
 entre os itens dos dois primeiros.
 Get(int index): Retorna true ou false, de acordo com o valor do elemento na posição
indicada.
 Not(): Inverte o valor de todos os elementos, ou seja, os que forem true passam a ser
false e vice-versa.
 Or(BitArray value): Semelhante ao método And, porém a operação realizada entre os
itens é a operação booleana OR.
 Set(int index, bool value): Define o valor do elemento na posição index conforme o
parâmetro informado.
 SetAll(bool value): Define o valor de todos os elementos da lista como true ou false de
acordo com o parâmetro value.
 Xor(BitArray value): Funciona da mesma forma que as funções And e Or, porém, a
operação booleana é a XOR (OU Exclusivo).

As demais operações como Clone, AsQueryable e CopyTo funcionam da mesma forma


que nas demais coleções.

Conclusão
Ficamos por aqui com esse artigo sobre as classes ArrayList, Hashtable e BitArray na
linguagem C#, caso tenham alguma dúvida podem ficar a vontade em me perguntar nos
comentários.

Com o conteúdo desse artigo eu acredito que seja possível qualquer pessoa começar a
entender melhor o funcionamento de classes em C#.

Essas e outras classes de coleções implementam várias interfaces que padronizam seu
funcionamento e que você mesmo pode implementar em suas classes próprias. Essas
interfaces encontram-se no namespace System.Collections, que você pode conhecer
em maiores detalhes no artigo abaixo:

System.Collection:
Conheça as interfaces
Facebook Twitter
(3) (0)
Temos neste artigo as principais interfaces, genéricas ou não, e estruturas de
dados presentes no namespace System.Collections do .NET Framework, que
permitem a implementação de diversos tipos de coleções de dados.

Fique por dentro


A utilização de interfaces é uma das principais alternativas de herança do C# e outras
linguagens orientadas a objetos.

Ao longo desse artigo traremos as principais interfaces do namespace


System.Collections, bem como a relação delas com algumas estruturas de dados
importantes no desenvolvimento de software. Mostraremos a importância da interface
ICollection, bem como o funcionamento básico de Generics, além da comparação entre
alguns dos elementos relacionados mais importantes que enxergamos no
desenvolvimento C#.

O conceito de herança é um dos mais importantes da orientação a objetos. A ideia é que


as classes filhas sejam capazes de reaproveitar o código das classes pai. No caso do C#,
por exemplo, quando pensamos em herança, pensamos na classe Object, da qual todas
as demais herdam, direta ou indiretamente.

Mas na maioria dos casos a herança é um recurso para os desenvolvedores utilizarem e


criarem um código mais conciso, de fácil reutilização e manutenção.

Algumas linguagens orientadas a objetos utilizam um conceito conhecido como herança


múltipla. Esse conceito aparece, por exemplo, em C++, e consiste em permitir que uma
única classe herde de várias (tenha vários “pais”).

Isso pode gerar problemas, especialmente em classes da terceira geração, como mostra
a Figura 1. Esse tipo de comportamento impede que a classe de hierarquia mais baixa
(ClasseFilha3) saiba de onde estão vindo cada uma das propriedades que utiliza, caso
tenham o mesmo nome.

Isso gera uma dose extra de cuidado para que a aplicação possa funcionar corretamente.
Figura 1. Exemplo herança múltipla

O problema da herança múltipla foi resolvido em C# e outras linguagens mais modernas


como Java com o acréscimo das interfaces. Esses são elementos

abstratos que precisam ser implementados por alguma classe para funcionarem. Eles
podem trazer assinaturas de métodos e propriedades que serão compartilhadas por todas
as classes que os implementam. Em outras palavras, eles formam uma base de
representação para classes com características semelhantes.

Ao longo desse artigo, veremos como funcionam algumas das interfaces padrão do C#
.NET, porque elas existem e o que podem fazer por nós, além da relação que as
interfaces têm com algumas estruturas de dados importantes para a aplicação.

Além disso, veremos como é importante a utilização desse tipo de artifício para a
criação de um sistema de qualidade.

Conhecendo as principais Interfaces do C#


O C# possui uma série de namespaces que podem ser utilizados diretamente. Um deles
é o System.Collections, que traz uma série de interfaces, classes e estruturas de dados de
uso comum no desenvolvimento de aplicações.
Essas interfaces são muito úteis, formando uma base de representação para futuras
classes que as implementarão. Veremos as principais delas ao longo do artigo em
detalhes.

Vamos começar apresentando as interfaces. A principal delas é a ICollection, que define


alguns atributos essenciais das coleções não-genéricas (as entenderemos a seguir). Essa
interface fornece a base necessária às demais, que formam representações de dados,
trazendo diversas opções para os desenvolvedores.

São as características que as definem, e precisamos entender qual delas é mais


interessante para nós de acordo com o aplicativo que estamos desenvolvendo. As
opções são muitas, como mostra a Tabela 1, que não traz todas as interfaces presentes
no namespace System.Colletions, apenas as mais importantes, que entenderemos ao
longo desse artigo.

Interface Descrição

ICollection Tamanho, Enumeradores e métodos de sincronização para coleções não-ge

IDictionary Coleção não-genérica de par chave/valor

IEnumerable Enumerador que oferece suporte a iterações simples sobre uma coleção nã
genérica

IEnumerator Oferece suporte a iterações simples sobre uma coleção não-genérica

IList Coleção simples de objetos não-genéricos

Tabela 1. Principais interfaces do namespace System.Collections

O entendimento das interfaces fica mais fácil quando analisamos as implementações das
mesmas. Por exemplo, o namespace fornece a classe ArrayList, que implementa a
interface IList através de uma matriz cujo tamanho é aumentado dinamicamente quando
necessário.

A partir desse exemplo, podemos concluir que a interface IList é, verdadeiramente, uma
representação básica de uma coleção simples de objetos não-genéricos.

Em outras palavras, ela traz uma base a ser seguida, com alguns atributos essenciais
para a criação de estruturas de dados do tipo Lista. Outras implementações que podemos
destacar são:

· Queue: Implementa ICollection, IEnumerable e ICloneable, esta última não


pertencente ao namespace System.Collections (e sim ao System). Representa uma fila
de dados, baseado no padrão FIFO (First in, First out): primeiro a entrar, primeiro a
sair.

· CollectionBase: Implementa IList, ICollection e IEnumerable. É a classe abstrata


básica para criação de coleções de dados fortemente tipadas.

· DictionaryBase: Implementa IDictionary, ICollection, IEnumerable. É a classe


abstrata básica para a criação de um dicionário de dados, ou seja, uma estrutura
fortemente tipada com pares chave/valor.

Quando analisamos estas implement" [...]

Tratamento de exceções
Exceções ocorrem com frequência no código e saber lidar com elas é

fundamental para garantir que as aplicações se comportem adequadamente

nessas situações, sem travar ou parar de funcionar. Nesse sentido o

tratamento de exceções é parte fundamental de qualquer linguagem, pois

permite identificar os problemas que ocorreram e definir fluxos alternativos para


o programa. Observe a Figura 1.
Figura 1. Tratamento de exceções

Para aprender a tratar exceções em C#, sugerimos os seguintes conteúdos:

INTRODUÇÃO ÀS EXCEÇÕES
CONTEÚDO DA AULA GUIAS RELACIONADOS

Iniciando nosso curso, entenderemos o que são exceções e como as


aplicações se comportam quando exceções são lançadas e não há tratamento
para elas.

Ir para o código

Conteúdo de apoio
Nenhum sistema está totalmente imune a erros e falha em algum momento
seja por falta de internet, ausência de um periférico, por um erro no código ou
em alguma biblioteca. A partir de um programa que calcula o total dos itens em
uma venda, veremos neste vídeo que situações imprevisíveis acontecem e é
preciso saber como se preparar para elas. Faremos isso a partir de um
mecanismo totalmente orientado a objetos, que permite construir código coeso
para prevenção e detecção de falhas.

O que são exceções?

Uma exceção é qualquer evento encontrado durante a execução do programa


que interrompe o fluxo de processamento esperado. Elas podem ser lançadas
por que o código possui um erro, quando um recurso está indisponível ou por
muitas outras condições imprevistas. A fim de garantir sua integridade e
também dos dados processados, um sistema de qualidade deve ser capaz de
capturar exceções de forma consistente.

Por que aprender sobre exceções?

Exceções estão presentes em todas as aplicações, portanto seu conhecimento


é fundamental para o desenvolvedor. Além disso, o mecanismo de tratamento
de exceções oferece diversas vantagens:

o Permite uma separação coesa entre as rotinas que descrevem o que fazer

quando algo sai do controle e aquelas para as quais a aplicação foi planejada;
o Uma vez que todas exceções lançadas em um programa são objetos, podemos

agrupar e diferenciar entre tipos diferentes de exceções, aproveitando recursos

da orientação a objetos;

o A partir do mecanismo de captura de exceções, um método pode tratar

internamente o erro ou delegar que ele seja tratado um nível acima na pilha de

execução, sem que para isso seja necessário verificar seu retorno.

.NET Exceptions:
Tratamento de
Exceções em .NET
Facebook Twitter
(3) (0)

Veja nesse artigo uma abordagem básica de como tratar os tipos de erros e
exceções possíveis em .NET

Fique por dentro


Este artigo será útil principalmente para desmistificar o tratamento de exceções somente
como base nos erros ocorridos pelo framework. Um programa bem formulado no tratamento
de possíveis exceções pode se tornar, de uma maneira mais complexa, um programa de mais
qualidade por não interromper o seu fluxo quando uma dessas exceções ocorrerem.

Entende-se por exceções não somente os erros providos da ferramenta, mas também os
desvios do fluxo principal da sua aplicação. Neste artigo detalharemos uma forma
padrão para esse tipo de tratamento, usando exemplos de códigos para evitar justamente
que, ao ocorrer um desvio, o usuário seja pego com uma mensagem sem tratamento
adequado e fique sem saber o motivo da paralisação do seu sistema.
Hoje em dia, com o desenvolvimento de softwares cada vez mais complexos e
integrados com outros sistemas e plataformas, temos um alto índice de informações
requisitadas ao usuário.

Quando estas informações são transferidas através destas integrações, surge uma
preocupação a mais quando se trata da programação dos recursos, pois os erros que
podem ocorrer (ou podemos dizer exceções) são decorrentes, muitas vezes, da falta de
algum recurso ou de algum argumento passado de forma inválida.

Não se pode sair desenvolvendo linhas e linhas de códigos sem levar em conta os
desvios que possam ocorrer ao longo da execução do sistema. Desenvolver sistemas
com certas características sem nos atentar ao tratamento de exceções pode ser motivo de
dor de cabeça futuramente para a equipe de desenvolvimento.

Como podemos garantir a integridade do uso de uma aplicação? Afinal, ninguém gosta
de ver o seu sistema “travado” durante sua execução.

Quando ocorre uma exceção, o que acontece durante a execução do programa é que o
fluxo do mesmo é redirecionado para uma rotina de tratamento dessa exceção. Caso esta
não seja tratada, provavelmente surgirão aquelas mensagens na tela do usuário,
praticamente incompreensíveis pela maioria deles.

Por isso, é muito importante que os desvios do curso principal do sistema sejam tratados
de forma efetiva, afim de que os usuários possam entender realmente o erro na
utilização do programa, com uma mensagem mais agradável e efetiva, informando qual
foi à regra não cumprida que acarretou a exceção lançada.

Para ficar bem claro, um erro é quando o programa faz uma solicitação e a mesma não é
retornada ou o sistema não consegue encontrar o componente ou página solicitada. Já
uma exceção, por exemplo, pode se dar quando o usuário digita um valor inválido para
um determinado campo.

O tratamento dessas ocorrências é importante para conseguirmos nos comunicar com o


usuário, afim de que o mesmo entenda o motivo do lançamento de uma exceção no
sistema em determinada ocasião.

Porém, de nada adiantaria executar o desvio de uma possível exceção se a mesmo não
fosse tratada de forma conveniente, ou seja, sempre que for feito um desvio, mensagens
devem aparecer bem escritas, de forma que a informação do motivo da ocorrência seja
de fácil interpretação por parte do usuário do sistema.

Podemos verificar casos em que temos o retorno de uma exceção específica, porém,
fora do contexto. Para isso, pode ser feito o tratamento de uma ou mais exceções no
mesmo bloco de comando, sempre levando em consideração a ordenação deste
tratamento, da exceção mais específica para aquela menos específica.

Caso contrário, se colocarmos uma instrução de tratamento genérica antes de tratarmos


as mais específicas, estas jamais serão executadas.
Erros e Exceções
Existem três grandes grupos de erros que podem ocorrer num sistema:

1. Erros de sintaxe:

Fazem com que o programa nem execute, ou seja, o erro é retornado na hora da
compilação do programa. Estes erros não são passíveis de tratamento via rotina, porém,
são descriminado na hora de executar.

2. Erros em " [...]

Debug de aplicações
.NET e a propriedade
InnerException
Facebook Twitter
(3) (0)

Veja neste artigo como o acesso à propriedade InnerException de uma


exceção pode auxiliar no debug de aplicações .NET, esclarecendo informações
sobre um erro que à primeira vista podem não ser tão claras.

Durante o desenvolvimento de aplicações ou, mesmo, com um sistema já em operação,


não é um fato incomum que ocorram erros. As prováveis causas disto podem ser as
mais variadas possíveis: descuidos por parte de programadores envolvidos em um
projeto, uma funcionalidade não concluída conforme o esperado ou ainda, falhas
decorrentes de algum elemento externo ao próprio software (servidores de banco de
dados inoperantes, problemas em uma rede corporativa, falha no acesso a Web Services
etc.) são apenas alguns dos fatores que podem conduzir a situações inesperadas e, em
muitos casos, indesejáveis.
Considerando as principais plataformas de desenvolvimento atuais (com destaque para
.NET e Java), erros são representados através de objetos conhecidos como exceções. A
gravação de dados relativos a exceções geradas por uma aplicação se revela como um
instrumento de extrema importância, visto que a partir destas informações
desenvolvedores terão meios para tentar reproduzir falhas que afetaram um sistema.

No .NET Framework informações sobre exceções estão associadas a objetos baseados


no tipo Exception (namespace System). O comum é que existam diferentes classes que
herdam dessa construção básica, com cada uma das mesmas estando normalmente
vinculadas a um contexto bem específico.

Sobre a estrutura da classe Exception, esta última conta com algumas propriedades que
detalham um erro gerado dentro de uma aplicação:

 Message: mensagem que descreve o erro que levou ao lançamento da exceção que se
está considerando;
 InnerException: instância do tipo Exception que corresponde à falha original, estando
associada a uma nova exceção. O preenchimento desta propriedade é opcional (logo,
o valor desse elemento poderá ser “nul” em muitos casos);
 StackTrace: string em que são listados os diferentes pontos pelos quais passou a
aplicação antes de um erro. Graças a esta informação, é possível se rastrear a origem,
bem como compreender melhor o que levou a esta situação anormal. Uma única
ressalva deve ser feita quanto a disponibilizar essas informações a usuários, já que
existirão tanto aqueles incapazes de entender o conteúdo desta propriedade, assim
como outros que com conhecimentos mais avançados podem vir a explorar
vulnerabilidades do sistema.

A seguir estão listados alguns exemplos de exceções bastante comuns em aplicações


construídas sob o .NET Framework:

 InvalidOperationException (namespace System): exceção que acontece quando a


invocação de um método a partir de um objeto for inválida, normalmente devido a
problemas com o estado atual em que este se encontra;
 SqlException (namespace System.Data.SqlClient): geralmente lançada quando da
ocorrência de erros em instruções enviadas a um banco de dados do SQL Server;
 CommunicationException (namespace System.ServiceModel): representa erros em
processos de comunicação envolvendo serviços, sendo bastante comum em soluções
baseadas na tecnologia WCF (incluindo nisto aplicações-cliente que consomem
funcionalidades de serviços deste tipo);
 HttpUnhandledException (namespace System.Web): exceções deste tipo são lançadas
quando erros não são tratados de modo apropriado dentro de aplicações Web;
 ArithmeticException (namespace System): falha resultante de um erro durante o
processamento de uma operação aritmética;
 DivideByZeroException (namespace System): exceção derivada do tipo
ArithmeticException, sendo disparada em casos que envolvem a tentativa de divisão
de um número por zero;
 OutOfMemoryException (namespace System): erro associado à falta de memória e
que impossibilita a um programa de prosseguir com sua execução normal;
 SecurityException (namespace System.Security): falha geralmente relacionada à
detecção de problemas de segurança;
 FileNotFoundException (namespace System.IO): corresponde a falhas ao se tentar
realizar uma operação que acesse um arquivo;
 DirectoryNotFoundException (namespace System.IO): exceção disparada quando um
diretório que se está tentando acessar não existir.

A forma como estes problemas são registrados (técnica esta conhecida como "logging")
é também bastante diversa, podendo envolver desde a persistência dos dados em tabelas
de bases relacionais ou em mecanismos próprios de um sistema operacional (como o
Event Viewer do Windows), passando até mesmo pela gravação em arquivos (no
formato texto ou XML, por exemplo). Existem inclusive frameworks específicos que
simplificam a implementação deste tipo de funcionalidade, sendo possível citar no caso
do .NET Framework a geração de arquivos XML por meio do log4net
(http://logging.apache.org/log4net/).

Independentemente da maneira como informações sobre exceções venham a ser


gravadas, não será raro que programadores precisem se debruçar sobre o código-fonte, a
fim de identificar em que situação uma falha poderá ocorrer. Muitas ferramentas visuais
para desenvolvimento de software contam com mecanismos que facilitam tarefas desse
gênero, sendo que a atividade relacionada ao uso de funcionalidades de execução de
instruções e checagem de erros é conhecida como depuração ou, simplesmente, debug.

O Visual Studio oferece um amplo suporte para a depuração de soluções .NET. A partir
do menu Debug está disponível uma série de opções para a execução passo a passo de
trechos de uma aplicação, incluindo nisto as estruturas conhecidas como breakpoints.

Profissionais da área de software estão mais do que familiarizados com o poder e a


flexibilidade que o uso de breakpoints oferece. Graças a esses recursos, é possível
interromper o fluxo de execução de um sistema a partir de uma IDE de
desenvolvimento, de maneira que se consiga inclusive executar instruções uma a uma,
avaliando os dados e os resultados produzidos pelas mesmas. Trata-se, portanto, de um
instrumento bastante importante para a simulação de situações que resultem em
exceções.

Ainda sobre a interrupção no fluxo de execução de aplicações, o Visual Studio costuma


indicar o ponto em que uma falha ocorreu, pausando assim o processamento atual.
Quando isto acontece, a própria IDE permite a visualização da instância que
corresponde à exceção gerada, facilitando assim o trabalho de análise a ser
desempenhado por um desenvolvedor. Na Figura 1 é demonstrado um exemplo disto,
em que a tentativa de se acessar uma base de dados resultou em um erro do tipo
SqlException.
Figura 1: Visualizando informações sobre uma SqlException a partir do Visual Studio

Soluções construídas sob a tecnologia ASP.NET contam com algumas características


peculiares no que se refere à geração de exceções. Conforme já mencionado
anteriormente, um erro do tipo HttpUnhandledException será gerado caso uma falha
não venha a ser tratada em aplicações Web. Esse comportamento pode vir a causar
algumas dificuldades na depuração de uma aplicação, principalmente se um
programador não se atentar a detalhes como a propriedade InnerException de uma
exceção.

Supondo uma aplicação em que o evento Application_Error do arquivo Global.asax será


responsável pela gravação de informações sobre erros, utilizando para isto uma classe
de nome LogHelper (Listagem 1). O método Application_Error será acionado sempre
que uma exceção não for tratada adequadamente dentro desta aplicação Web. Por meio
do método GetLastError do objeto Server é possível se obter a instância da exceção que
corresponde a tal erro.

Listagem 1: Evento Application_Error

...
void Application_Error(object sender, EventArgs e)

Exception ex = Server.GetLastError();

LogHelper.RegistrarErro(ex);

...

Caso uma falha desconhecida esteja acontecendo na aplicação que se está considerando
como exemplo, uma possível forma de se rastrear tal problema seria incluir um
breakpoint dentro do evento Application_Error. A partir disto, torna-se possível
verificar detalhes de prováveis exceções disparadas em outros pontos do sistema.

Uma vez que uma exceção seja lançada, pode-se visualizar o conteúdo de sua
propriedade Message posicionando o mouse sobre a variável que armazena a mesma.
Conforme demonstrado na Figura 2, a mensagem exibida é vaga demais, apenas
indicando a ocorrência de um erro do tipo HttpUnhandledException.
Figura 2: Visualizando a mensagem associada a uma exceção

O botão “+” em que consta a mensagem vinculada a uma exceção permite verificar
maiores detalhes sobre uma falha. Neste exemplo específico (Figura 3) nota-se que a
propriedade InnerException foi preenchida com o erro original (uma exceção do tipo
DivideByZeroException), o qual é decorrente de uma operação aritmética que resultou
em divisão por zero.
Figura 3: Analisando o conteúdo da propriedade InnerException

O uso da propriedade InnerException é comum também em frameworks desenvolvidos


por terceiros. Em tais casos, é provável que um erro inicial seja associado a um tipo de
objeto mais genérico; nestas situações, apenas a consulta à propriedade InnerException
poderá fornecer maiores informações na tentativa de se chegar à causa real de um
problema.

Procurei com este artigo fornecer uma dica simples, mas que pode ser de grande
utilidade ao se efetuar o debug de aplicações .NET. Espero que o conteúdo aqui
apresentado possa auxiliá-lo em algum momento. Até uma próxima oportunidade!

Ainda no contexto de identificação e tratamento de erros é importante saber utilizar os


recursos de depuração/debug do Visual Studio. Um deles é o breakpoint, que permite
pausar a execução da aplicação em determinado ponto do código para que possamos
avaliar seu estado naquele momento. Para saber mais, acesse o link abaixo:
Breakpoints: Como
depurar suas
aplicações no Visual
Studio
Facebook Twitter
(1) (0)

O artigo mostra as principais ferramentas disponibilizadas pelo Visual Studio


para o programador realizar a depuração do seu código com o objetivo de
encontrar e corrigir erros que possam ter aparecido em sua aplicação.

Atenção: esse artigo tem um vídeo complementar. Clique e


assista!
De que se trata o artigo

O artigo mostra as principais ferramentas disponibilizadas pelo Visual Studio para o


programador realizar a depuração do seu código com o objetivo de encontrar e corrigir
erros que possam ter aparecido em sua aplicação. Um exemplo é quando desejamos ter
a certeza que tipo de valor está sendo retornado de um método.

Em que situação o tema é útil

O artigo é útil para quem deseja conhecer recursos que irão aumentar a qualidade da
aplicação ao tentar localizar erros nos programas e também irão permitir desenvolver
uma aplicação já sendo preparado para futuras depurações, acrescentando alguns
recursos, como atributos de depuração.

Breakpoints e além

Programas estão sujeitos a bugs. Aplicações que se definam estáveis também tem a
tendência de apresentar, em alguma parte do seu ciclo de vida, eventuais bugs que
podem ter sido acrescentados ao incluir ou alterar um requisito no projeto.
Boa parte do trabalho dos programadores, principalmente em projetos com ciclo de vida
longo e que prevê manutenção do código, consiste em encontrar bugs e corrigi-los.

Esta tarefa pode se tornar demorada, porque é fato que mesmo o mais correto código
deixa de ser claro, mesmo para o seu criador em poucas semanas, ficando difícil
encontrar o causador do bug.

O Visual Studio e o Framework .NET sempre providenciaram ferramentas para facilitar


a tarefa de depuração, desde suas versões iniciais. Além dessas, existem outros recursos
que consistem mais em como desenvolver o projeto, visando a sua facilidade de
depuração. Estes pontos serão alvo deste artigo e para demonstrar os mesmos, veremos
a criação de um projeto de exemplo que pode ser usado para realizar testes de
depuração.

Desenvolver um bom software sem realizar sua devida depuração é algo praticamente
impossível, considerando a realidade dos projetos de software que existem hoje em dia.
Sempre haverá algum ponto do código que irá estar sujeito a algum tipo de falha, seja
nos momentos em que novos requisitos forem incluídos, novas plataformas forem
suportadas ou mesmo ao interagir com outras partes de software.

Os bons projetos precisam reduzir o índice de bugs para o menor número possível, mas,
precisam também facilitar a localização e solução destes quando surgirem. Estas são
desde as mais básicas, como a interrupção do código para inspeção dos objetos na
memória – o que é feito pelo já conhecido breakpoint, até preparar o código das classes
para que apresentem informações que ajudem o programador a identificar o que está
“acontecendo” com o seu código em um determinado ponto da execução.

É fato que quanto maior e mais complexo o projeto, mais difícil será fazer a depuração,
entretanto, com as técnicas apresentadas aqui, até mesmo projetos imensos podem ser
depurados e ter seus problemas resolvidos.

Resolvendo problemas na aplicação


Tudo se inicia dentro da IDE do Visual Studio ao se executar o código do projeto que é
o objeto de depuração.

Neste artigo vamos considerar apenas a depuração de códigos baseados em aplicações


que tenham alguma interface com o usuário, como aplicativos Windows Forms, WPF,
ASP.NET etc. Os tópicos apresentados aqui são mais indicados para estes tipos de
projetos, mas, também é possível realizar a depuração em outros, como Webservices e
serviços do Windows, para serem depurados quando o código está disponível ou até
mesmo, fazer a depuração de processos remotamente localizados.

Para iniciar a depuração é preciso que o código esteja sendo executado - aqui cabe uma
observação importante para os programadores iniciantes: uma class library precisa estar
anexada com um projeto executável que seja uma UI (interface com o usuário) para
poder ser depurado. Se você tentar depurar um projeto deste tipo no Visual Studio, vai
receber uma mensagem de que não é possível executar diretamente um projeto deste
tipo.
Nota DevMan: Class Library é uma biblioteca de recursos criada a partir da plataforma .NET,
sendo normalmente gerada como um arquivo recebendo a extensão .dll. Você pode, por
exemplo, criar classes dentro da mesma. Desta forma, permite que as mesmas possam ser
reutilizadas ao longo de uma série de projetos.

Os primeiros passos para entender como funciona a depuração é conhecer as duas


modalidades para compilação e execução de um projeto, que são Debug e Release.
Projetos no modo Debug incluem informações que facilitam a localização no código,
como a call stack – que é a hierarquia de chamadas dos métodos e classes – contendo o
número da linha da chamada no código e outros dados importantes para depuração,
estes, que serão mostrados nos próximos tópicos.

Normalmente, a opção escolhida para a compilação do projeto é o modo Debug (este é


utilizado durante o desenvolvimento/testes da aplicação) e após, é alterado para o modo
Release para ser enviado ao usuário final.

A maneira mais simples de alterar o modo de compilação do projeto é através da barra


de ferramentas, como ilustrado na Figura 1. Este modo é selecionado como padrão ao
criar um novo projeto.

Figura 1. Barra de ferramentas com destaque para o seletor de modo de


execução/compilação

Existem algumas formas de iniciar a depuração de seu aplicativo: clicando no botão


executar (ao lado do seletor de modo, na barra de ferramentas), pressionando F5,
utilizando o meu Debug > Start Debugging ou na janela Solution Explorer, com o botão
direito sobre o ícone do projeto, escolhendo Debug > Start Debugging (Figura 2).
Generics
Gerenics é um recurso da linguagem C#, também presente em outras

linguagens atuais, que simplifica e torna mais seguro o trabalho com objetos

cujo tipo não é conhecido inicialmente. Ou seja, com esse recurso podemos

trabalhar com tipos de dados "genéricos" sem ter de recorrer a declarar todos

os objetos como um supertipo (como a classe Object) e realizar casts para

manipulá-los de acordo com seu tipo real. Para conhecer melhor esse recurso
sugerimos a leitura dos artigos a seguir:
C# - Generics- Artigo
easy .net Magazine 11
Facebook Twitter
(2) (0)

O artigo aborda como utilizar Generics, com foco em coleções de dados. Será
apresentada uma introdução ao recurso e alguns exemplos práticos, inclusive
utilizando de recursos anteriores aos Generics para entender seus benefícios.

Atenção: esse artigo tem um vídeo complementar. Clique e


assista!

De que se trata o artigo

O artigo aborda como utilizar Generics, com foco em coleções de dados. Será
apresentada uma introdução ao recurso e alguns exemplos práticos, inclusive utilizando
de recursos anteriores aos Generics para entender seus benefícios.

Para que serve

Generics é um poderoso recurso incluído na plataforma .net desde a versão 2.0 do


framework, com ele podemos criar classes e métodos reutilizáveis com mais eficiência,
seu uso mais comum estão na manipulação de coleções fortemente tipadas.

Em que situação o tema é útil

A plataforma .net é repleta de suporte a manipulação de coleções de dados, mas foi na


versão 2.0 que uma mudança significativa foi realizada, a inclusão de generics
possibilitou criar coleções de forma mais eficiente, reduzindo ou eliminando a
necessidade de conversão de tipos.

Generics

Generics são hoje uns dos principais fundamentos da programação para a plataforma
.NET, de forma que podemos encontrar a aplicação do recurso em várias situações no
.NET Framework. Permitem flexibilizar a forma como dados são tratados, pois é
definido um parâmetro para um tipo. O artigo apresentará os motivos pelos quais os
Generics surgiram, tratando de operações de Box, Unbox e conversões. Nos exemplos
práticos os Generics são demonstrados com coleções, como List e Dictionary. Ao final
criaremos uma coleção customizada.

Durante o desenvolvimento de um projeto procuramos sempre reutilizar o máximo de


códigos, procurando evitar a repetição de linhas e com isso temos um código mais
enxuto e flexível para mudanças. Felizmente as linguagens de programação mais
utilizadas estão repletas de recursos que nos possibilitam realizar esta tarefa. Conhecer
bem o funcionamento da linguagem é um início para criar bons projetos. Apesar de
atualmente existir uma série de aprendizados para que o programador possa utilizar das
melhores práticas de codificação e sempre ter um código bem elaborado, existem alguns
recursos antigos que ainda são considerados poderosos para auxiliar no
desenvolvimento de projetos, Generics é um deles.

O .net Framework é uma poderosa tecnologia, sua evolução está em constante


crescimento e sempre atualizada com as mais recentes necessidades de desenvolvimento
de software, madura e com suporte a uma séria de tecnologias. Hoje proporciona o
desenvolvimento para os mais diversos tipos de aplicativos. Atualmente o Visual Studio
é um dos IDEs mais poderosos do mercado, e o Visual C# cada vez está melhor, sendo
considerada uma das linguagens de programação mais relevantes da atualidade.

Atualmente o .net framework está na versão 4.0, porém foi na versão 2.0 que o assunto
deste tutorial foi adicionado ao framework. Nesta versão do .net framework houve
diversas melhorias importantes em relação à versão anterior, e uma das mais relevantes
e poderosas foi a inclusão de Generics. Com ele podemos criar um tipo especial que
recebe como parâmetro outro tipo.

Em C# temos os tipos de valor, chamados de Value Types e os tipos de referência,


chamados de Reference Types. Resumidamente, os Value Types armazenam um valor e
os Reference Types armazenam uma referência aos dados. Os tipos de valores derivam
implicitamente do System.ValueType. Os tipos de referência guardam nele o endereço
da memória onde o objeto está registrado, somente o endereço e não o objeto real. Já as
variáveis de tipos de valor (Values Type) contêm o próprio objeto.

Generics possibilitam a eliminação de conversão de tipos em sua aplicação. Para


entender melhor o que seria uma conversão de tipos, vamos pensar no conceito de Box e
Unbox. O Box seria quando adicionamos um elemento em uma caixa e fechamos, o
Unbox seria quando abrimos e retiramos o elemento da caixa. Enquanto o elemento está
dentro da caixa fechada, outras pessoas não saberão o que tem dentro, porém quando
alguém receber a caixa e abrir, pode não estar preparado para seu conteúdo. De forma
similar funcionam as conversões de tipos. Eu posso jogar qualquer valor dentro de um
objeto (caixa), mas na hora de usar esse valor, eu vou precisar estar preparado para
recebê-lo, ou seja, se for um tipo Double que está dentro do objeto, quando abrir a caixa
eu preciso convertê-lo para Double e depois inserir em uma variável desse tipo.
Benefícios das listas
genéricas no .Net -
Revista Easy .Net
Magazine 28
Facebook Twitter
(1) (0)

Neste artigo serão apresentados os benefícios da utilização de listas genéricas


no .NET, com uma explicação prévia de conceitos fundamentais para a
compreensão deste recurso.

Artigo do tipo Tutorial


Recursos especiais neste artigo:
Contém nota Quickupdate.

Listas genéricas
Neste artigo serão apresentados os benefícios da utilização de listas genéricas no .NET, com
uma explicação prévia de conceitos fundamentais para a compreensão deste recurso.
Começaremos falando sobre a memória da aplicação para então abordarmos os conceitos de
value type e reference type, dando todo o embasamento necessário para a compreensão dos
recursos de boxing e unboxing. A partir daí detalharemos o uso das listas genéricas, que são
usadas para armazenamento de objetos em memória, desde a sua conceituação até a sua
implementação, através de um exemplo prático e objetivo onde construiremos um cadastro de
pessoas em memória fazendo uso de diversos métodos da classe List.

Em que situação o tema é útil


Este tema é útil para qualquer projeto de software que venha a fazer uso de listas de
objetos em memória, auxiliando o desenvolvedor na compreensão do funcionamento e
dos recursos disponíveis para manipulação de listas, podendo ser utilizado, como por
exemplo, para a criação de cache de objetos em memória, possibilitando o aumento da
performance da aplicação.

Tradicionalmente no desenvolvimento de software nós temos a necessidade de trabalhar


com estruturas de dados que agrupam uma série de elementos. Inicialmente, nos tempos
da programação estruturada, nós tínhamos à nossa disposição apenas os arrays, que nos
permitiam armazenar informações dentro de uma estrutura indexada que fornecia
métodos básicos para inserir, remover e recuperar tais informações.

Com a chegada da orientação a objetos este cenário mudou e diversas implementações


surgiram para trabalharmos com listas de objetos. Tais objetos passaram a ser
disponibilizados diretamente pelas APIs das principais linguagens, como .NET e Java.

Lista é uma relação de objetos que podem representar Pessoas, Carros, Bicicletas, tipos
Inteiros, Strings, Decimais, dentre outros, ou seja, elas permitem que uma lista de
objetos seja armazenada e manipulada em memória.

Antes de entrarmos no mérito das listas, vamos ver melhor alguns conceitos
fundamentais para a compreensão das mesmas.

Memória do Computador X Memória da


Aplicação
A memória do computador é o local físico onde são alocados as instruções e dados
utilizados pelos processos em execução na máquina. Em operação normal, esta memória
contém partes do sistema operacional e algumas aplicações que estão sendo executadas.

A memória da aplicação é uma porção da memória do computador alocada pelo sistema


operacional para rodar determinada aplicação. Essa memória está dividida em duas
partes, Stack e Heap, conforme mostra a Figura 1.

Figura 1. Ilustração da divisão da memória da aplicação

O espaço da stack é onde ficam registradas as variáveis declaradas em nosso sistema. O


local onde o conteúdo desta variável será armazenado vai depender se esta variável é de
um value type ou um reference type.

Tipos de Dados por Valor (value type)

Uma variável declarada como um tipo de dados por valor é estruturada na memória para
conter um valor diretamente, ou seja, quando declaramos variáveis do tipo int, string,
char, byte e todos os tipos que derivam de System.ValueType, o valor destas variáveis
fica alocado diretamente na Stack, sem overhead (sobrecarga) de busca na heap,
fazendo com que os mesmos sejam mais leves, conforme podemos observar na Figura
2.

Figura 2. Exemplo de alocação de value type

Tipos de Dados por Referência

Uma variável declarada como um tipo de dados por referência é estruturada para conter
uma referência para um objeto existente na Heap, ou seja, quando criamos um tipo
através de palavras reservadas (Class, interface, array, delegate, etc.) estamos criando
um tipo de referência. Estes ficam localizados na memória Heap quando instanciados e
criam um endereço na Stack que aponta para um determinado local na Heap onde estará
o objeto. Quando um objeto desse tipo perde a referência o Garbage Collector entra em
ação para que não tenhamos objetos perdidos na Heap. Na Figura 3 temos um exemplo
de alocação de uma variável de um reference type.

Figura 3. Exemplo de alocação de um reference type


Manipulação de arquivos
Ler, escrever, criar e excluir arquivos são tarefas comuns em diferentes tipos

de aplicação. Em C# contamos com um conjunto de classes que torna bastante

simples implementar essas funcionalidades, como você poderá ver nos


seguintes artigos:

Trabalhando com
arquivos em C#
Facebook Twitter
(0) (0)

O artigo irá descrever os principais recursos da linguagem C# e do framework


.Net para trabalhar com arquivos tanto com leitura como gravação destes. Você
terá o primeiro contato com as classes disponibilizadas para escrever, ler e
apagar arquivos. Também terá contato com as classes que permitem obter
dados estatísticos do arquivo como tamanho, data da última modificação e se o
arquivo existe.

Atenção: esse artigo tem uma palestra complementar. Clique e


assista!

Atenção: esse artigo tem um vídeo complementar. Clique e


assista!

[lead]Do que trata o artigo

O artigo irá descrever os principais recursos da linguagem C# e do framework .Net para


trabalhar com arquivos tanto com leitura como gravação destes. Você terá o primeiro
contato com as classes disponibilizadas para escrever, ler e apagar arquivos. Também
terá contato com as classes que permitem obter dados estatísticos do arquivo como
tamanho, data da última modificação e se o arquivo existe. Além dos conceitos
envolvidos, será feita uma aplicação de demonstração para que você possa ver os
recursos em uso.

Para que serve

Sempre é necessário ler dados de arquivos quer sejam de texto, arquivos XML ou
qualquer outro formato. Existem muitos recursos dentro do framework .Net, mas nem
sempre é muito fácil lembrar-se de como usá-los.

Em que situação o tema é útil

Mesmo com todas as funcionalidades oferecidas pelo sistema operacional, ao


desenvolver aplicativos comerciais sempre se acaba precisando escrever dados em
arquivos geralmente do tipo texto ou outro formato qualquer para troca de dados entre
aplicações. Caso você esteja trabalhando com dados no formato texto e precise de
referências rápidas para executar suas tarefas, este artigo oferece o que é necessário para
executá-las. Além disto, existem alguns cuidados que você precisará tomar para poder
escrever e ler em arquivos e que são expostos neste artigo.

Resumo do DevMan

Todas as informações armazenadas no computador estão dentro de arquivos. Estudando


os fundamentos da computação um pouco mais a fundo, você perceberá que até mesmo
as pastas (ou diretórios para os mais antigos) consistem de arquivos especiais onde
outros arquivos são armazenados. As tarefas mais comuns do programador incluem
manipular arquivos verificando se existem ou não dentro do sistema de arquivos da
máquina onde o programa está sendo executado. Também é necessário criar os
arquivos, atualizar seus dados sobrescrevendo os dados existentes, renomear ou ainda
excluir um arquivo que não é mais necessário. Dentro da proposta de não precisar
começar de um ponto muito elementar, você tomará conhecimento do que é preciso
fazer usando a linguagem C# para executar estas tarefas. Além do contato com os
códigos e classes que serão usados, também levaremos em conta alguns pontos com
relação à segurança. Outro ponto a ser abordado são os elementos usados pela interface
do Windows para facilitar o trabalho com os arquivos. E como sempre, é bom ter um
exemplo prático onde tudo o que se aprendeu possa ser aplicado. [/lead]

Uma das maiores vantagens dos computadores modernos é a possibilidade de se


poderem armazenar os dados do trabalho que está sendo feito para se usar mais tarde.

Pense por um momento em quais atividades que você executa diariamente usando os
PC´s que fazem uso dos arquivos? Vão aqui algumas da minha lista:

1. Eu faço login em um computador que armazena em arquivo o meu ID e minha senha;

2. Após o login, o sistema lê as minhas preferências de uso do computador como o


papel de parede e ícones da minha área de trabalho, mais uma vez, armazenado em
arquivos;
3. Quando abro o programa para ler os e-mails, primeiramente preciso fazer o download
das mensagens que estão armazenadas em um servidor de internet remoto e gravá-los
em minha máquina. Mesmo que eu use um Webmail (como o Gmail ou o Hotmail), as
mensagens estão armazenadas em arquivos de um servidor;

4. Como eu trabalho como programador, o código fonte fica gravado no disco do meu
computador em arquivos texto especiais que são abertos pelo Visual Studio;

5. Se eu preferir jogar, os dados usados para os jogos estão em arquivos;

6. Músicas também são gravadas geralmente em arquivos MP3.

São tantas as operações feitas usando-se arquivos que se for citar todas elas, o artigo
fica sem graça. Mas, esteja certo: uma hora destas você vai precisar fazer o uso da
manipulação de arquivos. No início, quando não havia ainda o Windows e os sistemas
operacionais eram muito básicos, os programadores precisavam preocupar-se em
resolver muitos problemas ao manipular os arquivos. Vejamos alguns destes problemas:

• Era necessário localizar o arquivo dentro do disco, ler os bytes do mesmo e criar um
objeto para manipular o arquivo a partir do endereço lógico do mesmo no disco rígido;

• Antes de ler os dados, era necessário armazenar os bytes que diziam qual o tipo do
arquivo: se binário ou texto. Era preciso guardar o número de bytes no momento da
leitura para que fosse assegurado ao ler que o tamanho dos dados estava correto;

• Se o arquivo estivesse dentro de um sistema UNIX, vários atributos precisavam ser


tratados. Estes atributos diziam se se tratava de um arquivo regular, um diretório, um
dispositivo de sistema ou ainda um link simbólico para outro arquivo;

• Também seria necessário verificar se o usuário atualmente carregado no sistema tinha


as permissões para ler o arquivo, para executar e gravar. Geralmente, nestes sistemas,
nem sempre todas as permissões eram garantidas.

Ou seja, era um trabalho bastante árduo.

Com a chegada dos sistemas operacionais mais modernos, estes passaram a oferecer um
conjunto de objetos para executar estas tarefas. Geralmente chamados de API
(Application Programming Interfaces), estas bibliotecas cobriam boa parte das tarefas,
embora, com alguma complexidade.

A partir do surgimento dos ambientes gerenciados como o framework .Net as tarefas


ficaram muito mais simplificadas pois, quase tudo o que se desejar fazer com os
arquivos nos programas possuirá uma classe que irá resolver o problema para o
programador. Não é possível cobrir todas as classes em um artigo como este, mas,
partindo das tarefas mais básicas, vamos descobrindo como fazer as tarefas mais
elementares.

Para que você consiga entender bem o artigo e poder executar os exemplos eu espero
que você tenha algum conhecimento dos seguintes assuntos:
• Criação de programa console e Windows Forms usando o Visual Studio;

• Uso do modo de debug do Visual Studio para corrigir erros no código;

• Criação de classes.

• Programação orientada a objetos com C#.

[nota]Nota: Apesar do artigo demonstrar as operações de maneira mais elementar


deixando de apresentar alguns pontos importantes como criptografia, tabela de
caracteres a ser usada (encoding pages) e gerenciamento de permissões, todas estas
questões são extensamente cobertas pelo framework .Net e a linguagem C#. Para evitar
estender demais o artigo, vamos tratar apenas das questões iniciais. Procure se informar
mais sobre o assunto posteriormente. [/nota]

[subtitulo]Classes para ler e gravar arquivos [/subtitulo]

Basicamente todas as classes usadas para o trabalho com arquivos estão dentro da
biblioteca System.IO. Assim, para poder usar estas em seu projeto ou sua classe você
precisa adicionar a biblioteca da seguinte forma:

using System.IO;

A Tabela 1 mostra as principais classes e as funcionalidades que ela provê. Além disto,
demonstra também o contexto mais comum onde este tipo de classe será usada.

"

Criando, copiando,
movendo e excluindo
arquivos em .NET
Facebook Twitter
(2) (0)

Veja neste artigo como manipular (criar, copiar, mover e excluir) arquivos no
Windows utilizando os recursos do .NET Framework.
Criar, mover, copiar ou excluir um arquivo utilizando o .NET pode ser tão fácil quanto
escrever uma única linha de código. Veremos neste artigo, duas formas de fazer isso,
utilizando as classes File e FileInfo em C# e VB.NET.

No exemplo das Listagens 1 e 2, é mostrado como criar, copiar, mover e excluir um


arquivo utilizando a classe File.

Listagem 1: utilizando a classe File em C#

//Código em C#

File.Create(“C:\\arquivo.txt”);//Cria o arquivo “arquivo.txt” na


unidade C:

File.Copy(“C:\\arquivo.txt”, “D:\\arquivo.txt”)//Copia o arquivo


“arquivo.txt” da unidade C: para a D:

File.Move(“D:\\arquivo.txt”, “E:\\arquivo.txt”)//Move o arquivo


“arquivo.txt” da unidade D: para a E:

File.Delete(“C:\arquivo.txt”)//Exclui o arquivo “arquivo.txt” da


unidade C:. Agora deve restar apenas o da unidade E:

Listagem 2: utilizando a classe File em VB.NET

//Código em VB.NET

File.Create(“C:\\arquivo.txt”);//Cria o arquivo “arquivo.txt” na


unidade C:

File.Copy(“C:\\arquivo.txt”, “D:\\arquivo.txt”)//Copia o arquivo


“arquivo.txt” da unidade C: para a D:

File.Move(“D:\\arquivo.txt”, “E:\\arquivo.txt”)//Move o arquivo


“arquivo.txt” da unidade D: para a E:

File.Delete(“C:\arquivo.txt”)//Exclui o arquivo “arquivo.txt” da


unidade C:. Agora deve restar apenas o da unidade E:

Notem que os métodos da classe File utilizados são estáticos, ou seja, não precisamos
criar uma instância da classe para usar suas funções.
Agora, vejamos uma forma alternativa de fazer o mesmo, dessa vez utilizando a classe
FileInfo. Em termos de funcionalidade, não há diferença. O que vale considerar para
decidir qual das duas formas usar é saber que outras ações serão executadas com os
arquivos em questão. As duas classes possuem propriedades e métodos diferentes, mas
em boa parte dos casos, são usadas da mesma forma. A classe FileInfo, porém, requer
que seja criada uma instância para que os métodos sejam chamados. Vejamos a seguir
como fazer isso:

Listagem 3: utilizando a classe FileInfo em C#

FileInfo fi = new FileInfo(“C:\\arquivo.txt”);

fi.Create();//Cria o arquivo “arquivo.txt” na unidade C:

fi.CopyTo(“D:\\arquivo.txt”);//Copia o arquivo “arquivo.txt” da


unidade C: para a D:

fi.MoveTo(“E:\arquivo.txt”);//Move o arquivo “arquivo.txt” da unidade


C: para a E:

fi.Delete(); // Exclui o arquivo “arquivo.txt” da unidade C:. Agora


devem restar os arquivos nas unidades D: e E:

Listagem 4: utilizando a classe FileInfo em VB.NET

Dim fi As new FileInfo(“C:\\arquivo.txt”);

fi.Create();//Cria o arquivo “arquivo.txt” na unidade C:

fi.CopyTo(“D:\\arquivo.txt”);//Copia o arquivo “arquivo.txt” da


unidade C: para a D:

fi.MoveTo(“E:\arquivo.txt”);//Move o arquivo “arquivo.txt” da unidade


C: para a E:

fi.Delete(); // Exclui o arquivo “arquivo.txt” da unidade C:. Agora


devem restar os arquivos nas unidades D: e E:

É importante observar que a instância da classe FileInfo mantém a referência a um


arquivo. Diferente da classe file, onde referenciamos um arquivo qualquer para copiar,
mover ou excluir, uma vez criado o objeto FileInfo, este será relativo sempre a um
mesmo arquivo. Para que o exemplo das listagens 3 e 4 ficassem iguais ao das listagens
1 e 2, seria necessário excluir o arquivo da unidade D:. Para isso, precisaríamos criar
outra instância do FileInfo, referenciando o arquivo “D:\arquivo.txt” e depois chamar o
método Delete().

Uma última observação é válida com relação ao método Move da classe File e MoveTo
da classe FileInfo. Nos exemplos, o arquivo foi movido para outro diretório, porém,
caso movêssemos o arquivo para o mesmo diretório de origem, seria o mesmo que
renomear este arquivo.

Bem, por enquanto é só. Foram dicas simples, mas que podem ser muito úteis no dia-a-
dia, afinal, quem nunca precisou manipular arquivos externos a sua aplicação?

A maior parte das classes para manipulação de arquivos está contida no namespaces
System.IO (de Input/Output), que é explorado em detalhes nos artigos abaixo:

Primeiros passos com


o namespace
System.IO - Revista
easy .Net Magazine 25
Facebook Twitter
(0) (0)

O artigo apresenta as classes que o .NET framework dispõe para realizarmos o


tratamento de arquivos, diretórios do sistema e drives instalados no sistema
operacional, utilizando práticas que podem melhorar o desempenho e manter a
segurança nas atividades relacionadas a operações IO (entradas e saídas) em
aplicações .NET.

Demais posts desta série:


Primeiros passos com o namespace System.IO - Parte 2
De que se trata o artigo

O artigo apresenta as classes que o .NET framework dispõe para realizarmos o


tratamento de arquivos, diretórios do sistema e drives instalados no sistema operacional,
utilizando práticas que podem melhorar o desempenho e manter a segurança nas
atividades relacionadas a operações IO (entradas e saídas) em aplicações .NET.

Em que situação o tema é útil

Na grande maioria dos sistemas desenvolvidos, existe a necessidade de trabalharmos


com arquivos organizados em diretórios no disco rígido da máquina ou leitura de drives
e localização, manipulação e armazenamento de informações. Quando necessitamos
manter alguma informação que não seja em bancos de dados relacionais, precisamos
manter estas informações organizadas de uma forma que permita a leitura pela
aplicação.

Resumo Devman

Neste artigo veremos quais as classes disponíveis no .NET framework que possibilitam
a manipulação/edição e exclusão de arquivos físicos e diretórios de sistema em conjunto
com as funcionalidades disponíveis pelo sistema operacional. Vamos entender a leitura
das informações importantes de arquivos, diretórios e drives como, por exemplo,
localização, verificação de propriedades, tamanho, espaço disponível no sistema
operacional e monitoramento de alterações.

Trabalhar com arquivos, diretórios e operações de IO é uma tarefa considerada trivial


para desenvolvedores. Certamente a grande maioria das aplicações ainda necessita
realizar o tratamento de arquivos, diretórios, leitura de imagens, vídeos e outros dados
que estão armazenados tanto no disco rígido como em bancos de dados relacionais. A
má utilização de comandos para estas operações podem comprometer a estrutura de uma
aplicação inteira. Desta forma, para facilitar as operações I/O em aplicações .NET, o
.NET framework dispõe de um namespace específico para estas tarefas. Trata-se do
namespace System.IO. Com as classes disponibilizadas neste namespace, o
desenvolvedor pode manipular diretórios, arquivos e drivers do sistema operacional,
além de manipular leitura e reprodução de áudio, vídeo, imagens etc.

Este conjunto de classes são derivadas do tipo FileSystemClass, que nada mais é que um
conjunto de classes especializadas em manipulação de informação para arquivos,
drivers e diretórios. Além deste conjunto de classes, este namespace disponibiliza uma
série de outras classes, que tratam das mais variadas operações de I/O possíveis, como
escrita e leitura em arquivos.

A Classe FileSystemInfo
No namespace System.IO temos uma série de classes distintas, sendo cada uma aplicada
a um tipo de objeto diferente no sistema operacional.

A classe FileSystemInfo, a princípio, serve apenas como classe base, conforme os


princípios da Orientação a Objetos, onde temos uma classe mais generalizada para dar
origem à classes mais específicas. Ela foi implementada justamente para que suas
classes filhas (especializadas como FileInfo) possuam um conjunto de propriedades e
métodos em comum, não sendo necessário reescrever os mesmos métodos em cada
classe filha.

Para conhecermos melhor as propriedades e métodos da classe FileSystemInfo, vamos


analisar a Tabela 1, onde são listadas as propriedades e a Tabela 2, onde são
apresentados os seus métodos.

Propriedade Descrição

Attributes permite manipular informações dos atributos do arquivo ou diretório que está se
analisado. Atributos são um conjunto de informações utilizadas tanto pelo sistem
operacional, aplicações ou até mesmo pelo usuário, para definir comportamento
arquivo ou diretório. Um exemplo de atributo de um arquivo é o famoso “Somen
Leitura”. Para verificar todos os atributos possíveis de um arquivo ou diretório,
analisar o enumerador contido no mesmo namespace da
classe FileSystemInfo, denominado FileAttributes.

CreationTime permite informar ou ler informação da data em que o arquivo ou diretório foi cri

Exists tem como único objetivo retornar se o arquivo ou diretório analisado através do
informado existe ou não no sistema operacional. Neste caso a propriedade some
retorna valor e não permite ser alimentada (modificada).

Extension retorna a extensão de um arquivo, por exemplo, em arquivo com o nome “File.tx
retornará a extensão txt.

FullName retorna o caminho completo do arquivo ou diretório no sistema operacional, des


drive inicial até o nome do arquivo ou diretório no sistema.

LastAccessTime informa a data e hora do último acesso de um arquivo ou diretório.

LastWriteTime informa a data e hora da última manipulação de escrita realizada em um arquivo


diretório no sistema operacional.

Name diferente da FullName, esta propriedade retorna apenas o nome do arquivo ou di


sem o caminho ou extensão do mesmo.

Tabela 1. Propriedades da classe FileSystemInfo


Método Descrição

Delete responsável por excluir o arquivo ou diretório do sistema operacional.

Refresh atualiza as informações (referente ao armazenamento) do arquivo ou diretório no


operacional.

Tabela 2. Métodos da classe FileSystemInfo

A Classe FileInfo
A classe destinada a fornecer informações de armazenamento de arquivos no sistema
operacional é a classe FileInfo"

Primeiros passos com


o namespace
System.IO – Revista
easy .Net Magazine -
Parte II
Facebook Twitter
(0) (0)

O artigo apresenta as classes que o .NET framework possui para realizarmos


manipulação de arquivos gravados no sistema operacional ou
arquivos/informações alocados na memória do computador.
Demais posts desta série:
Primeiros passos com o namespace System.IO - Parte 1

De que se trata o artigo

O artigo apresenta as classes que o .NET framework possui para realizarmos


manipulação de arquivos gravados no sistema operacional ou arquivos/informações
alocados na memória do computador, utilizando práticas que podem melhorar o
desempenho e manter a segurança nas atividades relacionadas a operações IO (entradas
e saídas) em aplicações .NET.

Em que situação o tema é útil

Sempre que for preciso editar, criar ou excluir algum arquivo hospedado no sistema
operacional, ou manter dados gravados temporariamente na memória do computador,
obtendo o máximo de performance no trabalho com dados de diversos formatos.

Primeiros passos com o namespace System.IO – Parte II

Neste artigo veremos as principais classes derivadas de Stream e que são usadas para
manipulação de arquivos. Apresentaremos as classes File, Directory, StreamReader,
StreamWriter e BufferedStream, explicando seus principais métodos e exemplificando o
uso das mesmas.

Conforme já comentamos no primeiro artigo, as tarefas de manipulação de arquivos são


tarefas muito comuns no dia-a-dia do desenvolvedor, porém é fundamental o
conhecimento dos recursos nativos do framework para tornar estas tarefas mais
produtivas e seguras.

Neste artigo vamos estudar como uma aplicação pode utilizar o .NET framework para
ler e escrever arquivos,

Para podermos realizar a manipulação de arquivos, criando, adicionando ou removendo


conteúdo, ou até mesmo apenas lendo informações contidas nestes, precisamos primeiro
entender como funciona o mecanismo de transferência de dados gravados para a
aplicação que está solicitando os mesmos, e vice versa.

Para que esta transferência de dados seja possível o framework possui uma série de
classes que derivam de uma classe base, chamada Stream. Um Stream pode ser
classificado como o objeto que realiza a conexão entre a aplicação que está necessitando
manipular os dados e a fonte onde estes dados estão armazenados, podendo ser o disco
rígido, a memória, a internet, o próprio teclado etc. Streams não se limitam apenas a
trabalhar com arquivos, mas também são aplicados nas transferências de dados como
vídeos, imagens, voz e demais dados que necessitem de transmissão de dados. A
principal função dos Streams é permitir a interação da aplicação com elementos
externos, sejam eles quais forem.

Agora que conhecemos um pouco mais sobre o conceito de Stream, vamos conhecer
quais as principais propriedades e métodos implementados pela classe
abstrata Stream do .NET. Assim como as demais classes de manipulação I/O, a
classe Stream também está localizada no namespace System.IO e possui as propriedades
apresentadas na Tabela 1 e os métodos apresentados na Tabela 2 a seguir:

Nota do DevMan

I/O: Esta sigla é muito utilizada na computação e


significa Input/Output (Entrada/Saída). Refere-se à comunicação de entrada e saída de
dados entre softwares ou hardwares. Operações de entrada e saída de dados
armazenados em um disco rígido são consideradas uma operação de I/O, assim como
operações de entrada e saída entre um computador e uma impressora, mouse, teclado,
scanner etc.

Propriedade Descrição

CanRead Esta propriedade determina quando o Stream suporta leitura de dados.

CanSeek Esta propriedade determina quando o Stream suporta seeking. Seeking é uma bu
uma posição no Stream. Através do Seek podemos posicionar o cursor de leitura
gravação em um determinado local e ler ou gravar a partir deste local.

CanTimeOut Determina se o Stream possui um Timeout, ou seja, se a operação atual com o S


pode expirar depois de determinado tempo.

CanWrite Determina se o Stream permite gravação de dados (escrita).

Length Esta propriedade retorna o tamanho do Stream, em bytes.

Position Retorna e permite informar qual a posição do cursor no Stream. Esta posição não
ser maior que o tamanho retornado pela propriedade Length.

ReadTimeout Informação de qual é o tempo limite para operações de leitura do Stream.

WriteTimeout Informação de qual é o tempo limite para operações de gravação (escrita) do Str

Tabela 1. Propriedades da classe Stream

Método Descrição

Close Fecha o Stream e libera todos os recursos associados a ele.


Flush Limpa qualquer buffer existente para o Stream e força as alterações para que sej
gravadas na fonte de dados.

Read Este método executa uma leitura sequencial no Stream, conforme o número de b
definido e a partir da posição atual do cursor no Stream, atualizando esta mesma
após a leitura.

ReadByte Executa a leitura de apenas um byte no Stream, partindo da posição atual e atual
esta posição após a leitura. É o mesmo que executar o método Read passando co
parâmetros apenas um byte.

Seek Método que permite definir a posição do cursor no Stream sem a necessidade de
realizar a leitura até esta posição.

SetLength Define o tamanho do Stream. Caso o tamanho informado seja menor que o tama
dados contido no Stream este será truncado. Se for maior, apenas será expandido
novo tamanho.

Write Método que permite a gravação de informação no Stream, informando o número


desta informação e atualizando o mesmo para a nova posição após a gravação.

WriteByte Realiza o mesmo que no método Write, porém grava apenas um byte no Stream.

Tabela 2. Métodos da classe Stream

Nota do DevMan

Buffer: Termo utilizado para denominar uma região da memória do computador que
está alocada para o armazenamento temporário de dados, sendo possível realizar leituras
e gravações neste espaço.

A Figura 1 ilustra como os diferentes tipos de Streams, contidos no .net framework, se


relacionam para fornecer uma estrutura organizada de leitura e gravação de dados em
diversas fontes de dados e também em algumas classes de leitura (reader) e escrita
(writer) que servem de apoio para o trabalho com Streams. Durante este artigo veremos
os principais e mais utilizados Streams e suas classes de apoio.
Figura 1. Streams do .net framework e classes de apoio para leitura e escrita. (Fonte:
http://www.cnblogs.com/erebus/articles/2176646.html)

Classes de Apoio para Streams


As classes de Streams podem ser utilizadas sozinhas ou em conjunto com classes de
apoio, que são classes que nos fornecem serviços que facilitam a interação com os
Streams.

Com a classe File podemos realizar uma série de operações com arquivos, como a
leitura e escrita do conteúdo de um arquivo, a criação ou a abertura de um arquivo com
permissões de somente leitura, a criação ou escrita de um arquivo com permissões de
escrita, além de operações básicas com arquivos como a verificação se o arquivo
pesquisado existe, exclusão de arquivos, dentre outras.

A classe File auxilia o trabalho com os Streams pelo fato de possuir alguns métodos
específicos que retornam instâncias de classes do tipo Stream já abertas e prontas para a
utilização. O objeto do tipo Stream mais básico retornado pela classe File é o
objeto FileStream, que vamos estudar profundamente mais tarde, mas que tem como
objetivo representar um arquivo no sistema de arquivos e nos possibilita interagir com o
mesmo. Além deste objeto mais genérico, FileStream, a classe File também possui
métodos que retornam Streams, mais específicos como o objeto StreamReader, que é
um objeto que também representa um arquivo, porém com permissões de operações
relacionadas somente à leitura dos dados deste arquivo, ou seja, somente será possível
ler dados com este tipo de Stream.
Veremos a seguir as classes de apoio para Streams disponibilizadas pelo .net
framework.

Classe File
Como comentamos anteriormente, a classe File possui uma série de propriedades e
métodos que possibilitam o trabalho com Streams de uma forma mais simplificada. Para
entender melhor como esta classe pode nos auxiliar, podemos verificar na Tabela 3 os
seus principais métodos.

Método Descrição

AppendAllText Adiciona um texto ao final do conteúdo de um arquivo existente, ou, no caso do


não existir, cria o mesmo com o texto como conteúdo.

AppentText Abre um arquivo, ou cria um novo quando não existir, e retorna uma instância d
classe StreamWriter a qual é preparada para a escrita de conteúdo no arquivo.

Copy Cria uma cópia do arquivo.

Create

"

Caso você precise lidar especificamente com arquivos ZIP também há classes
específicas para isso. Se for esse o caso, sugerimos a leitura do artigo abaixo:
Manipulação de
arquivos .zip no .NET
Framework 4.5
Facebook Twitter
(0) (0)

Veja neste artigo alguns exemplos de utilização das classes ZipFile e


ZipArchive. Esses novos tipos foram disponibilizados com o lançamento do
.NET Framework 4.5, tendo por objetivo simplificar operações de compactação
e descompressão de arquivos.

O uso de mecanismos para a compactação de arquivos é, sem sombra de dúvidas, um


recurso bastante difundido no meio empresarial. Devido ao grande volume de
informações processadas, diversas técnicas são empregadas com intuito de reduzir a
quantidade de dados, tentando com isto maximizar o potencial de utilização dos meios
de armazenamento e/ou transmissão.

Cópias de segurança (backups) são um exemplo notório de aplicação de meios para a


compactação de informações. A realização de backups é um recurso vital em qualquer
tipo de negócio, uma vez que possibilita a rápida recuperação de dados essenciais,
viabilizando assim a retomada das operações rotineiras tão logo ocorram situações
consideradas como desastrosas. A importância das cópias de segurança é atestada pelo
enfoque dado a este tipo de questão por muitas auditorias corporativas, com uma série
de procedimentos determinando que informações se prestam à realização de backups,
além da periodicidade a ser levada em conta neste último caso.

Um dos formatos de compactação mais conhecidos é o ZIP. Este padrão aberto surgiu
ainda em 1989, tendo passado por uma série de evoluções desde então. Sobre estruturas
baseadas nesta especificação (extensão .zip), é comum que as mesmas possuam várias
pastas e arquivos como parte de seus respectivos conteúdos, sendo possível inclusive
configurar a taxa com a qual tais itens serão comprimidos (maior compactação significa,
normalmente, um maior tempo em operações que envolvam a descompressão de
arquivos).

O processo de descompactação de arquivos .zip também oferece uma flexibilidade


considerável. Existe a alternativa de se descompactar um arquivo específico, sem que
isto se traduza na necessidade de efetuar tal ação sobre todo o conteúdo que se está
manipulando. A este comportamento que permite a execução de operações sobre um
arquivo específico dá-se o nome de acesso randômico.

Todas essas características aqui mencionadas estão entre as razões que contribuíram
para a popularização do padrão ZIP. Este formato é a base utilizada na geração de
arquivos JAR (bibliotecas contendo classes e outros recursos Java compactados), no
novo padrão para documentos, planilhas e apresentações do pacote Office (Open XML),
além de ser suportado nativamente pelo próprio Windows (existem inclusive
funcionalidades do sistema operacional que envolvem o uso de recursos de
compactação).

A plataforma .NET conta desde a versão 2.0 com mecanismos que permitem a
compactação de arquivos: trata-se da classe GZipStream (namespace
System.IO.Compression). Contudo este tipo apresentava algumas limitações, como a
impossibilidade de se comprimir vários arquivos numa mesma estrutura. Isso levou ao
surgimento de alternativas que procuravam suprir esta demanda, sendo um bom
exemplo disto a biblioteca SharpLibZip (http://sharpziplib.com/).

Já com o .NET Framework 3.5 seriam disponibilizadas as classes Package e


PackagePart (namespace System.IO.Packaging). A partir de então, a plataforma .NET
passou a contar com a capacidade de manipulação de arquivos compactados com uma
estrutura mais complexa (considerando inclusive pastas com arquivos vinculados às
mesmas).

Com o lançamento do .NET 4.5 outros melhoramentos foram introduzidos no que se


refere ao suporte do formato ZIP. A finalidade deste artigo é descrever o funcionamento
das classes ZipFile (http://msdn.microsoft.com/en-
us/library/system.io.compression.zipfile.aspx) e ZipArchive
(http://msdn.microsoft.com/en-us/library/system.io.compression.ziparchive.aspx), as
quais foram disponibilizadas neste nova versão do Framework. Para isto, serão
apresentados a seguir alguns exemplos de trechos de código que utilizam esses tipos.

Utilizando as classes ZipFile e ZipArchive


em aplicações .NET
Antes de iniciar a discussão sobre as características das classes ZipFile e ZipArchive, é
necessário ressaltar que referências a duas bibliotecas precisarão ser adicionadas a
projetos que venham a empregar esses tipos.

Este procedimento pode ser realizado, dentro do Solution Explorer do Visual Studio
2012, clicando com o botão direito sobre o item "References" de um projeto e
selecionando na sequência a opção "Add Reference...".

Aparecerá então a janela “Reference Manager”. Em “Assemblies > Framework”


localizar as bibliotecas “System.IO.Compression” e
“System.IO.Compression.FileSystem”, selecionando as mesmas (Figura 1). Acionar
finalmente o botão “OK”, de maneira que a referências seja incluídas no projeto que se
estiver criando.

Figura 1: Adicionando referências à bibliotecas de compressão a um projeto

ZipFile será a primeira das classes abordada por este artigo. Trata-se de um tipo
estático, o qual disponibiliza métodos que podem ser usados em operação de criação,
extração ou leitura de arquivos gerado segundo o padrão ZIP.

Supondo que seja preciso compactar toda uma estrutura de diretórios e arquivos de um
projeto ASP.NET, como aquela que consta na Figura 2.
Figura 2: Exemplo de estrutura de diretórios e arquivos a ser compactada

A Listagem 1 apresenta um exemplo de como isto pode ser feito através da classe
ZipFile. No caso, foi utilizado o método CreateFromDirectory, o qual cria um novo
arquivo .zip a partir de um diretório específico; devem ser fornecidos como parâmetros
a esta operação:

 O caminho do diretório que estará sendo comprimido;


 O nome do arquivo no formato .zip que será gerado como resultado do
processamento;
 O nível de compressão do arquivo a ser criado. Para este parâmetro é necessário
utilizar um dos valores disponíveis para o enumeration CompressionLevel (namespace
System.IO.Compression): Optimal (utiliza-se o maior grau de compactação possível,
por mais que esta tarefa possa demandar um tempo maior), Fastest (prioriza-se a
velocidade do processo de compactação, mesmo que com isto não se atinja o melhor
índice possível), NoCompression (os diferentes itens adicionados a um arquivo .zip não
estarão comprimidos);
 Um valor booleano que, em caso afirmativo, indica que o nome do diretório-base será
incluido na hierarquia de pastas a serem compactadas (neste exemplo específico,
definiu-se o valor “false”, de forma que não exista dentro do arquivo .zip uma pasta de
nome ArquivosACompactar como diretório inicial).

Listagem 1: Exemplo de utilização do método CreateFromDirectory da classe ZipFile

...
ZipFile.CreateFromDirectory(

@"C:\Temp\TesteCompactacao\ArquivosACompactar\",

@"C:\Temp\TesteCompactacao\Exemplo01.zip",

CompressionLevel.Optimal,

false);

...

Após a execução do trecho de código da Listagem 1, um arquivo de nome


“Exemplo01.zip” terá sido criado conforme especificado na chamada ao método
CreateFromDirectory (Figura 3).

Visualizando o conteúdo deste arquivo, será possível confirmar que o mesmo foi gerado
seguindo a mesma estrutura de arquivos e pastas do diretório informado originalmente
(Figura 4).

Figura 3: Arquivo .zip que foi gerado a partir do método CreateFromDirectory


Figura 4: Visualizando o conteúdo do arquivo .zip gerado via método
CreateFromDirectory

Já na Listagem 2 está um exemplo de uso da operação ExtractToDirectory. Esse método


estático recebe como parâmetros o nome de um arquivo .zip, além do diretório de
destino em que será descompactado o conteúdo deste último.

Listagem 2: Exemplo de utilização do método ExtractToDirectory da classe ZipFile

...

ZipFile.ExtractToDirectory(

@"C:\Temp\TesteCompactacao\Exemplo01.zip",

@"C:\Temp\TesteCompactacao\ArquivosDescompactados\");

...

O processamento da instrução que consta na Listagem 2 irá descompactar todo o


conteúdo do arquivo “Exemplo01.zip” no diretório que foi definido como destino
(Figura 5).
Figura 5: Conteúdo de arquivo .zip descompactado via método ExtractToDirectory

Além do tipo estático ZipFile, a classe ZipArchive também oferece uma série de
funcionalidades que visam facilitar a implementação de funcionalidades baseadas na
manipulação de arquivos compactados.

ZipArchive é um tipo que corresponde a uma representação de um arquivo .zip. Em


termos práticos, isto significa que por meio dessa estrutura poderão ser executadas
operações individuais sobre os itens de um arquivo .zip.

Cada elemento compactado dentro de um arquivo .zip equivale a uma instância do tipo
ZipArchiveEntry (namespace System.IO.Compression), a qual pode ser obtida através
de uma chamada ao método GetEntry com uma referência da classe ZipArchive. A
identificação de um objeto ZipArchiveEntry corresponde ao caminho de um elemento
dentro de um arquivo .zip (possíveis pastas + nome do arquivo que está compactado).

Dentre as ações possíveis empregando os tipos ZipArchive e ZipArchiveEntry estão: a


inclusão de novos arquivos, atualizações no conteúdo de tais elementos, exclusões de
itens ou ainda, a leitura individual destes.

O exemplo que consta na Listagem 3 faz uso das classes ZipFile, ZipArchive e
ZipArchiveEntry simultaneamente, a fim de com isto extrair dois itens que constam no
arquivo .zip criado anteriormente.

O método Open de ZipFile permite a abertura de um arquivo compactado, recebendo


como parâmetros o caminho deste, além de que modo isto ocorrerá. Para este último
parâmetro, deverá ser utilizado um dos valores do enumeration ZipArchiveMode
(namespace System.IO.Compression):

 Read: carregamento de um arquivo .zip para a realização somente de operações de


leitura;
 Create: permite apenas a inclusão de novos itens a um arquivo .zip;
 Update: permite tanto a realização de operações de leitura, quanto escrita em um
arquivo zipado.

No trecho de código apresentado na Listagem 3, definiu-se que o arquivo


“Exemplo01.zip” será aberto para a leitura, para que se consiga com isso proceder com
a extração de alguns dos elementos armazenados no mesmo (valor de enumeration
ZipArchiveMode.Read). A partir de instâncias da classe ZipArchiveEntry é acionado o
método ExtractToFile, de maneira que a descompactação de um item gere um novo
arquivo (com base no caminho que foi passado como parâmetro).

A execução desse conjunto de instruções será responsável pela geração de dois arquivos
em um diretório de testes, conforme indicado na Figura 6.

Listagem 3: Exemplo de utilização das classes ZipFile e ZipArchive

...

using (ZipArchive archive = ZipFile.Open(

@"C:\Temp\TesteCompactacao\Exemplo01.zip",

ZipArchiveMode.Read))

string caminhoBaseExtracao =

@"C:\Temp\TesteCompactacao\ExtracaoZipArchive\";

ZipArchiveEntry entry1 =

archive.GetEntry(@"ProjetoExemplo.sln");

entry1.ExtractToFile(

caminhoBaseExtracao + "ProjetoExemplo.sln");
ZipArchiveEntry entry2 =

archive.GetEntry(@"ProjetoExemplo\Default.aspx");

entry2.ExtractToFile(

caminhoBaseExtracao + "Default.aspx");

...

Figura 6: Arquivos descompactados por meio das classes ZipFile, ZipArchive e


ZipArchiveEntry

Por fim, a Listagem 4 apresenta um exemplo de criação de um arquivo .zip.

Neste último caso, está sendo invocado o método Open de ZipFile, informando-se ao
mesmo o valor de enumeration ZipArchiveMode.Create. Esta ação resultará na criação
de um novo arquivo .zip e, consequentemente, na geração de uma instância do tipo
ZipArchive (já habilitada para a inclusão de itens que ficarão compactados).

Chamadas à operação CreateEntryFromFile são então efetuadas utilizando a referência


da classe ZipArchive, de maneira a se incluírem itens ao arquivo .zip que se está
manipulando. Este método recebe como parâmetros:

 O caminho do elemento que será adicionado ao arquivo .zip;


 O nome de tal elemento dentro do arquivo .zip considerado;
 O grau de compactação do arquivo que se está comprimindo (a partir de um dos
valores possíveis para o enumeration ZipArchiveMode).

Listagem 4: Criação de arquivo .zip com as classes ZipFile e ZipArchive

...

using (ZipArchive archive = ZipFile.Open(

@"C:\Temp\TesteCompactacao\Exemplo02.zip",

ZipArchiveMode.Create))

string caminhoBaseOrigem =

@"C:\Temp\TesteCompactacao\ArquivosACompactar\";

archive.CreateEntryFromFile(

caminhoBaseOrigem + "ProjetoExemplo.sln",

"ProjetoExemplo.sln",

CompressionLevel.Optimal);

archive.CreateEntryFromFile(

caminhoBaseOrigem + @"ProjetoExemplo\Default.aspx",

@"ProjetoExemplo\Default.aspx",

CompressionLevel.Optimal);
}

...

A execução do código que está na Listagem 4 produzirá como resultado um arquivo


com conteúdo similar àquele que consta na Figura 7.

Figura 7: Arquivos criado através das classes ZipFile e ZipArchive

Conclusão
Conforme detalhado no transcorrer deste artigo, as novas classes do .NET 4.5 para
manipulação de arquivos .zip (ZipFile e ZipArchive) simplificam consideravelmente a
implementação de funcionalidades que envolvam o uso de técnicas de compactação. Os
diferentes recursos oferecidos por esses tipos permitem a realização de operações
sofisticadas, sem que isso implique em grandes esforços de codificação.

LINQ
LINQ (Language Integrated Query) é uma parte da linguagem C# que permite

consultar coleções de dados com uma sintaxe semelhante à da linguagem


SQL, com cláusulas para filtros e junções entre resultados, por exemplo. Para
conhecer esse recurso, confira o artigo abaixo:

Introdução a LINQ -
Revista easy .net
Magazine 30
Facebook Twitter
(4) (0)

Neste artigo veremos o conceito de LINQ e alguns exemplos de


implementações com o mesmo que podem ser utilizados no nosso dia a dia.

Artigo do tipo Exemplos Práticos


Recursos especiais neste artigo:
Conteúdo sobre boas práticas.

Introdução a LINQ
LINQ (Language Integrated Query) é uma funcionalidade muito importante do .NET
framework, trazendo uma sintaxe próxima do SQL, porém simples para ser utilizada em
qualquer fonte de dados, de arrays e listas em memória a implementações para o uso com
banco de dados.

Neste artigo veremos seu conceito e alguns exemplos de implementações com o mesmo
que podem ser utilizados no nosso dia a dia.

Em que situação o tema é útil


Este tema é útil na recuperação de dados de forma prática, eficiente e produtiva,
fornecendo ao desenvolvedor um modo elegante de acessar seus dados estejam eles em
memória, arquivos XML ou bases de dados.

Lidar com coleções de dados é algo que desenvolvedores necessitam muitas vezes,
sejam dados vindos de um banco de dados, dados de XML, dados em listas de objetos
dentre outros veículos, porém o caminho mais comum é ter para cada fonte de dados
uma forma diferente de trabalhar.

Eis então que surge no .NET 3.5 a Language-Integrated Query, conhecida como LINQ,
tornando a manipulação de itens de dados, sejam objetos, arquivos XML ou retornos de
banco de dados, em objetos tendo praticamente a mesma forma para todos eles. Assim
se tornou mais fácil trabalhar com os dados de forma unificada.

Tendo surgido no .NET 3.5 ela é suportada a partir do C# 3.0 em diante e também por
outras linguagens da plataforma, como por exemplo, o VB.Net.

A LINQ é então uma sub linguagem de paradigma declarativo que roda por cima da
CLR podendo ser usada misturada no contexto de outras linguagens, por exemplo, seu
projeto pode ser Vb.Net ou C#, entre outras, e usar trechos de código LINQ no meio dos
seus códigos.

Temos então a Figura 1 que mostra onde a LINQ está na arquitetura do .NET
Framework:

Figura 1. A LINQ dentro do .Net Framework

Como pode ver a LINQ é disponível para qualquer linguagem do .NET como um
recurso adicional. Os chamados LINQ-Enabled Data Sources seriam implementações da
LINQ para diversos cenários, como a LINQ to SQL, primeiro esboço de ORM da
Microsoft, substituído depois pelo Entity Framework que utiliza a LINQ to Entities,
além da LINQ to XML em que podemos usar para consultar nós no XML como itens de
dados em uma estrutura OO.

Nota: A LINQ consegue trabalhar com qualquer estrutura de objetos que implemente a
interface IEnumerable<T>.
As principais vantagens apontadas pela Microsoft para o uso da LINQ são a validação
de sintaxe em tempo de execução, suporte a IntelliSense e tipagem estática.

Sintaxe
A linguagem LINQ possui duas formas de pesquisa: sintaxe de consulta (muito próxima
da linguagem SQL) e a sintaxe de método.

Sintaxe de consulta
A sintaxe de consulta é chamada de Comprehension Queries e utiliza de palavras chaves
em comum com a SQL como from, where e select, porém em uma outra ordem.
Segundo o time da LINQ eles fizeram de uma forma mais lógica do que a SQL:
primeiro o quê, depois de onde, depois a clausula where e por fim o select. Sempre
começando com from e terminando com select, como podemos notar no exemplo
da Listagem 1.

Listagem 1. Selecionando uma string dentro de um array

"

Uma possibilidade muito interessante do LINQ é a de utilizar Lambda

Expressions para realizar as consultas, ao invés da sintaxe padrão de queries.

Caso você ainda não saiba o que são Lambda Expressions, você pode ver o
curso abaixo e logo em seguida ver como aplicá-las no LINQ:

Usando Expressões
Lambda com LINQ
Facebook Twitter
(8) (0)
Neste artigo vamos falar um pouco sobre Expressões lambda, veja o que são e
como funcionam. Esse artigo abrange também a utilização de lambda com
LINQ.

Uma expressão Lambda é uma função anônima que você pode usar para criar Delegates
ou tipos de árvores de Expressão. São particularmente muito úteis para escrever
consultas LINQ.

Para facilitar o entendimento, a expressão lambda é uma espécie de função, porém sem
nome. Ela realiza cálculos, filtros e retorna valores ou coleções de valores.

O símbolo “=>” é o operador Lambda, também conhecido como vai para (goes to), e
toda expressão precisa dele. Ao criar uma você, devem-se colocar os parâmetros a
esquerda do operador (caso haja parâmetros) e do outro lado do bloco coloca-se a
expressão ou instrução do. Esse tipo de expressão pode ser atribuído a Delegates.

Abaixo veremos alguns exemplos de expressões lambda para facilitar o entendimento.

Na Listagem 1 um array numérico e deveremos saber quantos números pares tem nesse
array.

Listagem 1. Exemplo Expressão lambda

static void Main(string[] args)

//Criamos uma array de numeros

int[] numeros = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

//utilizamos a função cout com uma expressão lambda como


parametro

int qtdPares = numeros.Count(x => x % 2 == 0); // qtdPares = 5

Console.WriteLine("Quantidades de Numeros Pares: {0}",

qtdPares.ToString());

Console.ReadKey();
}

Agora veremos um exemplo criando um delegate na Listagem 2. Imagine que você tem
uma situação onde precise passa um valor e receber o quadrado dele.

Listagem 2. Exemplo Expressão lambda com Delegate

delegate float deleg(float i);

static void Main(string[] args)

//Criamos uma variavel para receber um valor digitado pelo


usuário

float I = float.Parse(Console.ReadLine());

//Criamos uma variavel do tipo delegate que recebe

//a expressão lambda

deleg _Delegate = x => x * x;

float j = _Delegate(I); //j = 25

Console.WriteLine("O Quadrado de {0} é {1}",

I.ToString(), j.ToString());

Console.ReadKey();
}

Porque usar Expressões lambda?


Devemos usar as Expressões lambdas para deixar seu código muito mais simples e
legível, tornando-o mais agradável e elegante, principalmente na forma como embarca
métodos anônimos ao código.

Expressões lambda com LINQ


Bom agora que já obtivemos uma breve explicação sobre expressões lambda, vamos
mostrar sua utilização com LINQ.

LINQ é uma linguagem de consulta que foi introduzida a partir do .Net Framewok 3.5 e
é utilizada na manipulação de coleções, sejam elas vindas de um banco de dados,
documentos XML, coleções de objetos, etc. Assim, podemos definir o LINQ como uma
consulta que não tem dependência da fonte de dados.

Agora vamos criar um cenário para que possamos demonstrar a utilização do LINQ e a
melhora quando utilizamos lambda junto a ele. Vamos criar uma lista do tipo pessoa;
pessoa é um tipo de dado com as propriedades Nome, Idade, Cidade e Sexo.

Para montarmos esse exemplo, Abra o Visual Studio e crie um novo projeto clicando
em File -> New -> Project, conforme a Figura 1.
Figura 1. Criando o projeto.

Na janela seguinte digite “empty” na caixa de pesquisa e selecione BlankSolution.


Altere a propriedade Name para Lambda ou para o nome que preferir, conforme mostra
a Figura 2.

Figura 2. Novo projeto em Branco.


Se olharmos no Solution Explorer veremos que foi criada uma solução vazia, então
agora clique com o botão direito do mouse e escolha Add... e em seguida New Project.
Crie um projeto do Tipo Console Application e altere a propriedade Name para
Lambda, conforme demonstrado nas Figuras 3 e 4.

Figura 3. Adicionando um novo projeto a Solution.

Figura 4. Criando o projeto Console Application.

Agora vá até o Solution explorer e clique com o botão direito do mouse sobre o projeto
Console Application. Escolha a opção Add -> New Item e na janela que irá se abrir
escolha a opção Class, alterando a propriedade Name para Pessoa.cs. Agora vamos
implementar a classe pessoa, onde a mesma deve ter as propriedades Nome, Idade,
Cidade e Sexo e um construtor que recebe essas propriedades, conforme a Listagem 3.

Listagem 3. Criando Classe Pessoa

class Pessoa

public String Nome { get; set; }

public int Idade { get; set; }

public String Cidade { get; set; }

public String Sexo { get; set; }

public Pessoa(String _Nome, int _Idade,

String _Cidade, String _Sexo)

Nome = _Nome;

Idade = _Idade;

Cidade = _Cidade;

Sexo = _Sexo;

public override String ToString()

{
return "Nome: " + Nome + " Idade: " +

Idade.ToString() + " Cidade: " + Cidade + " Sexo: " +


Sexo;

No mesmo arquivo Pessoa.cs vamos também implementar uma classe que é uma lista
de pessoa. Essa classe conterá apenas um construtor sem parâmetros que servirá para
alimentarmos nossa lista de pessoas, ou seja, ao instanciarmos essa lista ela já estará
populada. Observe a Listagem 4.

Listagem 4. Alimentando a Lista de pessoas

class lPessoa : List<Pessoa>

public lPessoa()

this.Add(new Pessoa("JOSÉ SILVA", 21, "PIRACICABA", "M"));

this.Add(new Pessoa("ADRIANA GOMES", 18, "CURITIBA",


"F"));

this.Add(new Pessoa("JOSÉ NOVAES", 17, "CAMPINAS", "M"));

this.Add(new Pessoa("ANDRÉ NEVES", 50, "PIRACICABA",


"M"));

this.Add(new Pessoa("JONATHAN SANTOS", 45, "CAMPINAS",


"M"));

this.Add(new Pessoa("IVANILDA RIBEIRO", 29, "AMERICANA",


"F"));
this.Add(new Pessoa("BRUNA STEVAN", 28, "AMERICANA",
"M"));

this.Add(new Pessoa("ANDREIA MARTINS", 22,

"RIO DE JANEIRO", "F"));

this.Add(new Pessoa("ANTONIO DA SILVA", 30,

"POÇOS DE CALDAS", "M"));

this.Add(new Pessoa("JOSEFINO DANTAS", 48,

"CAMPOS DO JORDÃO", "M"));

this.Add(new Pessoa("HELENA MARIA GOMES", 25,

"SÃO PAULO", "F"));

this.Add(new Pessoa("DJONATHAN MASTRODI", 29,

"PIRACICABA", "M"));

this.Add(new Pessoa("BRUNO MARTINO", 55, "AMERICANA",


"M"));

this.Add(new Pessoa("ANA MARIA GONÇALVES", 60,

"PRAIA GRANDE", "F"));

this.Add(new Pessoa("MARCELA MARIA RIBEIRO", 85,

"JOAO PESSOA", "F"));

this.Add(new Pessoa("ISABELLA MENDES", 32, "PIRACICABA",


"F"));

this.Add(new Pessoa("IGOR MENDES", 22, "CAMPINAS", "M"));


this.Add(new Pessoa("ANTONIO JOSÉ FARIAS", 21,

"RIO DAS PEDRAS", "M"));

this.Add(new Pessoa("JULIO VIEIRA", 19, "ANDRADINA",


"M"));

this.Add(new Pessoa("MARIO RIBEIRO ", 13, "LIMEIRA",


"M"));

Agora que já criamos as classes necessárias, veremos a utilização de lambda com LINQ.
A cada exemplo mostraremos a forma com e sem lambda para que você possa ver as
diferenças entre os dois tipos.

Primeiro vamos utilizar a nossa lista de pessoas, porém deveremos retornar na tela
somente as pessoas que a primeira letra do Nome é “A”. Para isso devemos
implementar a rotina utilizando Expressões Lambda. Na Listagem 5 você confere o
exemplo com lamba e na Listagem 6 sem lambda.

Listagem 5. Exemplo de lambda com LINQ

lPessoa _lPessoa = new lPessoa();

foreach (var item in _lPessoa.Where

(p=>p.Nome.StartsWith("A")))

Console.WriteLine(item.ToString());

}
Listagem 6. Exemplo com LINQ

lPessoa _lPessoa = new lPessoa();

var pessoa= from p in _lPessoa

where p.Nome.StartsWith("A")

select p

foreach (var item in pessoa)

Console.WriteLine(item.ToString());

Agora neste novo exemplo desejamos exibir na tela somente as pessoas que tenham
idade entre 20 (vinte) e 30 (trinta) anos. Na Listagem 7 você confere o exemplo com
lambda e na Listagem 8 sem lambda.

Listagem 7. Segundo exemplo de lambda com LINQ

lPessoa _lPessoa = new lPessoa();

foreach (var item in _lPessoa.Where(p=>p.Idade>=20

&& p.Idade<=30))

{
Console.WriteLine(item.ToString());

Listagem 8. Segundo exemplo de LINQ

lPessoa _lPessoa = new lPessoa();

var pessoa= from p in _lPessoa

where p.Idade>=20 && p.Idade<=30

select p

foreach (var item in pessoa)

Console.WriteLine(item.ToString());

Utilizando o lambda com o LINQ o código fica bem mais curto, economizando tempo.
Com as expressões lambda você conseguirá ser bem mais produtivo.

Como você já deve ter visto até aqui, o LINQ é bastante flexível e pode ser usado em
diferentes contextos. Por exemplo, nos links abaixo você pode ver exemplos de
utilização desse recurso na leitura de arquivos XML e na persistência de dados:
Utilizando LINQ e
Extensions Methods
para a leitura de
arquivos XML
Facebook Twitter
(2) (0)

Veja neste artigo como efetuar a leitura de arquivos XML de uma forma mais
produtiva, empregando para isto recursos de LINQ to XML e de construções de
código conhecidas como Extension Methods.

O formato XML (sigla em inglês para “Extensible Markup Language”) surgiu ainda no
final dos anos 1990, sendo um padrão aberto voltado à geração de documentos
hierárquicos e que conta com uma ampla adesão por parte de sistemas computacionais
dos mais variados tipos.

Do ponto de vista estrutural, o padrão XML possui vários pontos em comum com a
linguagem HTML, estando baseados em marcações (tags). Contudo, diferente de
HTML em que há um conjunto pré-definido destes elementos, o formato XML pode ser
definido como uma metalinguagem: em termos práticos, isso significa que não existem
tags pré-definidas, com esses itens podendo ser definidos conforme necessidades
específicas.

Um documento XML é formado por um conjunto de elementos (nodes), com as


relações de dependência entre estes elementos sendo estabelecidas por meio de níveis
hierárquicos. Todo elemento é constituído por uma tag e seu respectivo conteúdo.
Quanto àquilo que consta em um elemento, o mesmo pode estar vazio, possuir atributos
ou ainda, conter um agrupamento de outros elementos. Já um atributo nada mais é do
que um par formado por um nome identificador e um valor correspondente (este último
entre aspas), com estes dois itens separados por um sinal de “=” (“igual”), além de
terem sido declarados dentro da tag que define um elemento.

A estrutura flexível oferecida pelo padrão XML é utilizada em larga escala em


processos de integração entre sistemas via Web Services, no conteúdo de sites da
Internet, em dispositivos móveis etc. Atualmente, este formato é suportado pelas
principais linguagens de programação e sistemas operacionais, existindo diversas
técnicas para a manipulação de dados neste padrão.

Considerando o caso específico da plataforma .NET, diversos métodos permitem a


implementação de funcionalidades baseadas na leitura e/ou escrita de informações no
padrão XML. LINQ to XML é uma dessas tecnologias, oferecendo vários meios que
viabilizam a realização de operações sobre dados hierárquicos. Além daquilo que é
oferecido nativamente pela extensão LINQ to XML, outras funcionalidades podem
ainda ser implementadas com base neste mecanismo, sem que isto implique
necessariamente em herdar classes pré-existentes a fim de incluir ou, até mesmo,
redefinir comportamentos: isto é possível graças ao uso de construções de código
conhecidas como "Extension Methods".

O objetivo deste artigo é discutir como Extension Methods podem ser combinados a
expressões que envolvam o uso de LINQ to XML, simplificando assim o processo de
leitura de informações contidas em documentos XML.

Conteúdo do arquivo XML utilizado nos


exemplos
Na Listagem 1 é apresentado o conteúdo do arquivo XML (Prestadores.xml) em que se
baseiam os exemplos apresentados mais adiante.

No arquivo “Prestadores.xml” constam dados de prestadores de serviço contratados


junto a uma hipotética Consultoria de Tecnologia. É possível constatar neste documento
XML a presença dos seguintes campos:

 ID: Número inteiro que identifica um prestador de serviços;


 CPF: CPF do profissional;
 NomeProfissional: nome do prestador de serviços;
 Empresa: empresa da qual o prestador é proprietário;
 CNPJ: CNPJ da empresa prestadora de serviços;
 Cidade: cidade em que está aberta a empresa prestadora;
 Estado: estado da empresa prestadora;
 InscricaoEstadual: inscrição estadual da empresa prestadora de serviços (o conteúdo
deste campo é opcional);
 ValorHora: valor recebido por hora trabalhada (preenchido quando o prestador de
serviços ser pago de acordo como o número de horas trabalhadas);
 ValorMensal: valor recebido mensalmente (preenchido quando o prestador de
serviços for pago de acordo com um valor fixo mensal);
 DataCadastro: data em que foi efetuado o cadastro do prestador de serviços;
 InicioAtividades: data em que o prestador de serviços iniciou suas atividades (o
conteúdo deste campo é opcional).

Listagem 1: Arquivo Prestadores.xml


<?xml version="1.0" encoding="utf-8"?>

<Prestadores>

<Prestador>

<ID>131</ID>

<CPF>111.111.111-11</CPF>

<NomeProfissional>JOÃO DA SILVA</NomeProfissional>

<Empresa>SILVA CONSULTORIA EM INFORMÁTICA LTDA</Empresa>

<CNPJ>11.111.111/1111-11</CNPJ>

<Cidade>São Paulo</Cidade>

<Estado>SP</Estado>

<InscricaoEstadual>1111-1</InscricaoEstadual>

<ValorHora>50,00</ValorHora>

<ValorMensal></ValorMensal>

<DataCadastro>03/01/2013</DataCadastro>

<InicioAtividades>03/01/2013</InicioAtividades>

</Prestador>

<Prestador>

<ID>132</ID>

<CPF>222.222.222-22</CPF>

<NomeProfissional>JOAQUIM DE OLIVEIRA</NomeProfissional>
<Empresa>SERVIÇOS DE TECNOLOGIA OLIVEIRA ME</Empresa>

<CNPJ>22.222.222/2222-22</CNPJ>

<Cidade>Belo Horizonte</Cidade>

<Estado>MG</Estado>

<InscricaoEstadual></InscricaoEstadual>

<ValorHora></ValorHora>

<ValorMensal>10527,35</ValorMensal>

<DataCadastro>03/01/2013</DataCadastro>

<InicioAtividades></InicioAtividades>

</Prestador>

<Prestador>

<ID>133</ID>

<CPF>333.333.333-33</CPF>

<NomeProfissional>MARIA MARTINS</NomeProfissional>

<Empresa>MARTINS TECNOLOGIA LTDA</Empresa>

<CNPJ>33.333.333/3333-33</CNPJ>

<Cidade>Rio de Janeiro</Cidade>

<Estado>RJ</Estado>

<InscricaoEstadual>33333</InscricaoEstadual>

<ValorHora>65,00</ValorHora>
<ValorMensal></ValorMensal>

<DataCadastro>04/01/2013</DataCadastro>

<InicioAtividades>10/01/2013</InicioAtividades>

</Prestador>

<Prestador>

<ID>134</ID>

<CPF>444.444.444-44</CPF>

<NomeProfissional>JOANA SANTOS</NomeProfissional>

<Empresa>CONSULTORIA SANTOS LTDA</Empresa>

<CNPJ>44.444.444/4444-44</CNPJ>

<Cidade>São Paulo</Cidade>

<Estado>SP</Estado>

<InscricaoEstadual></InscricaoEstadual>

<ValorHora></ValorHora>

<ValorMensal>8474,77</ValorMensal>

<DataCadastro>04/01/2013</DataCadastro>

<InicioAtividades></InicioAtividades>

</Prestador>

</Prestadores>
Definindo a classe que corresponde ao
conteúdo do arquivo XML de Prestadores
A Listagem 2 apresenta a implementação da classe Prestador. Instâncias deste tipo
equivalem, basicamente, aos dados contidos em cada elemento “Prestador” do
documento XML de exemplo.

É possível notar ainda que campos opcionais dos tipos decimal e DateTime estão com
um sinal de interrogação (“?”). Este recurso é conhecido como Nullable, permitindo a
propriedades e variáveis que o utilizam armazenar também o valor null, por mais que
estas tenham sido definidas a partir de um tipo primitivo (decimal) ou estruturado
(DateTime). Sem o uso de uma Nullable tais referências assumiriam, se nenhum valor
fosse associado às mesmas, o valor default para seus respectivos tipos (o número zero
no caso de um decimal).

Listagem 2: Classe Prestador

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace TesteExtensionMethodsXML

public class Prestador

public int ID { get; set; }

public string CPF { get; set; }

public string NomeProfissional { get; set; }


public string Empresa { get; set; }

public string CNPJ { get; set; }

public string Cidade { get; set; }

public string Estado { get; set; }

public string InscricaoEstadual { get; set; }

public decimal? ValorHora { get; set; }

public decimal? ValorMensal { get; set; }

public DateTime DataCadastro { get; set; }

public DateTime? InicioAtividades { get; set; }

Implementando a leitura do documento XML


via LINQ to XML
A classe estática ArquivoPrestadores (Listagem 3) representa a estrutura responsável
por efetuar a leitura do arquivo XML, a fim de com isto gerar uma coleção de objetos
do tipo Prestador.

O processamento do documento XML e a posterior geração de instâncias da classe


Prestador acontece através do método CarregarInformacoes:

 Inicialmente é gerada uma nova instância de XDocument (namespace


System.Xml.Linq), a partir de uma chamada ao método Load, passando-se como
parâmetro a este o nome do arquivo XML a ser carregado (parâmetro “arquivo”);
 Prossegue-se agora com a geração das instâncias baseadas na classe Prestador. Isso é
feito através de um loop foreach, acionando-se para isto o método Element a partir da
instância do tipo XDocument; esta primeira ação irá selecionar o node “Prestadores”.
Em seguida, invoca-se o método Elements, o qual irá retornar como resultado uma
coleção de referências da classe XElement (namespace System.Xml.Linq) e que
correspondem aos elementos de nome “Prestador” (subordinados ao node principal
“Prestadores”);
 A cada iteração do loop foreach é criada uma nova referência do tipo Prestador,
atribuídos valores às propriedades deste objeto a partir de elementos presentes no
node “Prestador” e, por fim, adicionada a instância em questão à coleção
“prestadores” (a qual é retornada posteriormente como resultado da execução do
método CarregarInformacoes);
 A leitura de cada elemento pertencente ao node “Prestador” se faz por meio do
método Element, a partir da instância de XElement (variável “dadosPrestador”)
declarada no início do loop foreach. Para isto, são invocados o método Element de
“dadosPrestador” e, na sequência, a propriedade Value. Conforme é possível observar,
o retorno da propriedade Value é sempre uma string, o que força a realização de
procedimentos de conversão de um tipo para outro; além disso, verificações adicionais
são realizadas quando se estiver manipulando um campo cujo conteúdo é opcional
(caso das propriedades InscricaoEstadual, ValorHora, ValorMensal, DataCadastro e
InicioAtividades).

Listagem 3: Classe ArquivoPrestadores

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Xml.Linq;

using System.IO;

namespace TesteExtensionMethodsXML

public static class ArquivoPrestadores

public static List<Prestador> CarregarInformacoes(


string arquivo)

List<Prestador> prestadores = new List<Prestador>();

Prestador prestador;

string strInscrEstadual;

string strValorHora;

decimal valorHora;

string strValorMensal;

decimal valorMensal;

string strDataCadastro;

DateTime dataCadastro;

string strInicioAtividades;

DateTime inicioAtividades;

XDocument xml = XDocument.Load(arquivo);

foreach (XElement dadosPrestador in

xml.Element("Prestadores").Elements("Prestador"))

prestador = new Prestador();


prestador.ID = Convert.ToInt32(dadosPrestador

.Element("ID").Value);

prestador.CPF = dadosPrestador

.Element("CPF").Value;

prestador.Empresa = dadosPrestador

.Element("Empresa").Value;

prestador.NomeProfissional = dadosPrestador

.Element("NomeProfissional").Value;

prestador.CNPJ = dadosPrestador

.Element("CNPJ").Value;

prestador.Cidade = dadosPrestador

.Element("Cidade").Value;

prestador.Estado = dadosPrestador

.Element("Estado").Value;

strInscrEstadual = dadosPrestador

.Element("InscricaoEstadual").Value;

if (!String.IsNullOrWhiteSpace(strInscrEstadual))

prestador.InscricaoEstadual =
strInscrEstadual;

strValorHora = dadosPrestador.

Element("ValorHora").Value;

if (!String.IsNullOrWhiteSpace(strValorHora) &&

decimal.TryParse(strValorHora,

out valorHora))

prestador.ValorHora = valorHora;

strValorMensal =

dadosPrestador.Element("ValorMensal").Value;

if (!String.IsNullOrWhiteSpace(strValorMensal) &&

decimal.TryParse(strValorMensal,

out valorMensal))

prestador.ValorMensal = valorMensal;

strDataCadastro = dadosPrestador

.Element("DataCadastro").Value;

if (!String.IsNullOrWhiteSpace(strDataCadastro) &&

DateTime.TryParse(strDataCadastro,
out dataCadastro))

prestador.DataCadastro = dataCadastro;

strInicioAtividades = dadosPrestador

.Element("InicioAtividades").Value;

if (!String.IsNullOrWhiteSpace(

strInicioAtividades) &&

DateTime.TryParse(strInicioAtividades,

out inicioAtividades))

prestador.InicioAtividades = inicioAtividades;

prestadores.Add(prestador);

return prestadores;

Já na Listagem 4 está um exemplo de trecho de código que emprega a classe


ArquivoPrestadores, fornecendo para isto o caminho em que se encontra o arquivo
“Prestadores.xml”.
Listagem 4: Exemplo de uso da classe ArquivoPrestadores

...

List<Prestador> prestadores = ArquivoPrestadores

.CarregarInformacoes(@"C:\Devmedia\Prestadores.xml");

...

Por mais que a implementação da classe ArquivoPrestadores seja simples e não exija
grandes esforços, a leitura e a conversão de valores originários de um arquivo XML
pode gerar trechos extensos de código, bem como se repetir ao longo de uma aplicação.
É neste ponto que o recurso conhecido como Extension Methods pode se revelar como
extremamente útil, conforme será demonstrado na próxima seção. Este mecanismo
possibilita não apenas um código mais enxuto, como também facilitando o uso de
funcionalidades básicas para tratamento de dados no formato XML.

Utilizando Extension Methods


O recurso conhecido como Extension Methods foi disponibilizado a partir da versão 3.5
do .NET Framework. Em termos práticos, um Extension Method nada mais é do que um
método estático, muito embora um desenvolvedor possa enxergar o mesmo como se
fosse uma operação acessível a partir da instância de uma determinada classe.

Este tipo de construção evita a necessidade de se gerar uma nova classe, servindo como
um meio para “adicionar” novas funcionalidades a um recurso já existente. O próprio
.NET Framework emprega de forma extensiva essa prática: LINQ é um bom
exemplo de mecanismo que faz uso de Extension Methods, de maneira a facilitar com
isto a manipulação de coleções de objetos através de métodos como Where e OrderBy.

Na Listagem 5 encontra-se a implementação da classe estática LinqToXmlExtensions.


Este tipo foi estruturado da seguinte forma:

 O método estático GetStringValue recebe como parâmetros uma instância do tipo


XElement, além do nome de um node que pertença a este elemento. Caso tal node
exista e possua um valor associado ao mesmo, será retornada a string correspondente;
do contrário, o método GetStringValue devolverá como resultado de sua execução o
valor null;
 As operações estáticas StringValue, IntValue, DecimalValue e DateTimeValue
representam exemplos de Extension Methods, adicionando novas funcionalidades ao
tipo XElement, sem porém exigir a necessidade de se gerar uma nova classe a partir do
mesmo. Todos esses métodos recebem como parâmetro uma instância da classe
XElement precedida pela palavra-chave “this” (isto é uma característica típica de um
Extension Method), além de um segundo parâmetro com o nome do node a ser lido;
 Os métodos StringValue, IntValue, DecimalValue e DateTimeValue acionam a operação
GetStringValue, manipulando os valores fornecidos por esta última e devolvendo
resultados baseados em seus respectivos tipos de retorno. No caso específico de
IntValue, DecimalValue e DateTimeValue isto envolve a utilização de valores Nullable e
da classe Convert em transformações de dados.

Listagem 5: Classe LinqToXmlExtensions

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Xml.Linq;

namespace TesteExtensionMethodsXML

public static class LinqToXmlExtensions

private static string GetStringValue(

XElement element, string node)

string valor = null;


XElement childElement = element.Element(node);

if (childElement != null)

valor = childElement.Value;

if (!String.IsNullOrWhiteSpace(valor))

valor = valor.Trim();

else

valor = null;

return valor;

public static string StringValue(

this XElement element, string node)

return GetStringValue(element, node);

}
public static int? IntValue(

this XElement element, string node)

string valor = GetStringValue(element, node);

if (valor != null)

return Convert.ToInt32(valor);

else

return null;

public static decimal? DecimalValue(

this XElement element, string node)

string valor = GetStringValue(element, node);

if (valor != null)

return Convert.ToDecimal(valor);

else

return null;

}
public static DateTime? DateTimeValue(

this XElement element, string node)

string valor = GetStringValue(element, node);

if (valor != null)

return Convert.ToDateTime(valor);

else

return null;

A simples definição da classe LinqToXmlExtensions em um projeto de testes fará com


que o próprio IntelliSense do Visual Studio liste os métodos que estendem uma classe.
É o que pode ser observado na Figura 1 com o método IntValue ou ainda, com a
operação StringValue conforme demonstrado na Figura 2.
Figura 1: Selecionando o método IntValue através do IntelliSense

Figura 2: Selecionando o método StringValue através do IntelliSense

Na Listagem 6 são apresentadas algumas instruções da classe ArquivoPrestador já


modificadas. Nota-se que o uso dos métodos IntValue, StringValue, DecimalValue e
DateTimeValue simplificou consideravelmente a implementação do método
CarregarInformacoes.

Listagem 6: Utilizando Extension Methods em instruções da classe ArquivoPrestador


...

prestador.ID =

dadosPrestador.IntValue("ID").Value;

prestador.CPF =

dadosPrestador.StringValue("CPF");

prestador.Empresa =

dadosPrestador.StringValue("Empresa");

...

prestador.InscricaoEstadual =

dadosPrestador.StringValue("InscricaoEstadual");

prestador.ValorHora =

dadosPrestador.DecimalValue("ValorHora");

prestador.ValorMensal =

dadosPrestador.DecimalValue("ValorMensal");

prestador.DataCadastro =

dadosPrestador.DateTimeValue("DataCadastro").Value;
prestador.InicioAtividades =

dadosPrestador.DateTimeValue("InicioAtividades");

...

Já a Listagem 7 demonstra outra implementação possível para a classe


ArquivoPrestadores, combinando para isto o uso de uma consulta LINQ em conjunto
com as classes XDocument e XElement, além dos Extension Methods definidos
anteriormente através da classe LinqToXmlExtensions.

Esta nova versão da classe ArquivoPrestadores constitui um bom exemplo de como o


uso de Extension Methods pode contribuir para uma codificação menos extensa. Por
meio deste recurso conseguiu-se, basicamente, evitar a escrita de longos trechos de
código visando a leitura e o tratamento de dados no formato XML. Importante frisar que
os métodos definidos em LinqToXmlExtensions também poderiam ser facilmente
reutilizados em outros pontos de uma aplicação hipotética.

Listagem 7: Classe ArquivoPrestadores modificada

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Xml.Linq;

using System.IO;

namespace TesteExtensionMethodsXML

public static class ArquivoPrestadores


{

public static List<Prestador> CarregarInformacoes(

string arquivo)

XDocument xml = XDocument.Load(arquivo);

return (from XElement dadosPrestador in xml

.Element("Prestadores").Elements("Prestador")

select new Prestador()

ID = dadosPrestador

.IntValue("ID").Value,

CPF = dadosPrestador

.StringValue("CPF"),

Empresa = dadosPrestador

.StringValue("Empresa"),

NomeProfissional = dadosPrestador

.StringValue("NomeProfissional"),

CNPJ = dadosPrestador

.StringValue("CNPJ"),

Cidade = dadosPrestador
.StringValue("Cidade"),

Estado = dadosPrestador

.StringValue("Estado"),

InscricaoEstadual = dadosPrestador

.StringValue("InscricaoEstadual"),

ValorHora = dadosPrestador

.DecimalValue("ValorHora"),

ValorMensal = dadosPrestador

.DecimalValue("ValorMensal"),

DataCadastro = dadosPrestador

.DateTimeValue("DataCadastro").Value,

InicioAtividades = dadosPrestador

.DateTimeValue("InicioAtividades")

}).ToList();

Conclusão
Procurei com este artigo demonstrar como LINQ e o recurso conhecido como Extension
Methods pode simplificar bastante a manipulação de arquivos XML. A definição de
métodos que estendam as capacidades de tipos como XElement evita não apenas a
criação de diversas subclasses (algo que provavelmente aumentaria a complexidade de
um projeto), como também possibilita o reuso de determinadas funcionalidades por toda
uma aplicação (ou mesmo em diversas soluções de software).

Usando Expressões
Lambda com LINQ
Facebook Twitter
(8) (0)

Neste artigo vamos falar um pouco sobre Expressões lambda, veja o que são e
como funcionam. Esse artigo abrange também a utilização de lambda com
LINQ.

Uma expressão Lambda é uma função anônima que você pode usar para criar Delegates
ou tipos de árvores de Expressão. São particularmente muito úteis para escrever
consultas LINQ.

Para facilitar o entendimento, a expressão lambda é uma espécie de função, porém sem
nome. Ela realiza cálculos, filtros e retorna valores ou coleções de valores.

O símbolo “=>” é o operador Lambda, também conhecido como vai para (goes to), e
toda expressão precisa dele. Ao criar uma você, devem-se colocar os parâmetros a
esquerda do operador (caso haja parâmetros) e do outro lado do bloco coloca-se a
expressão ou instrução do. Esse tipo de expressão pode ser atribuído a Delegates.

Abaixo veremos alguns exemplos de expressões lambda para facilitar o entendimento.

Na Listagem 1 um array numérico e deveremos saber quantos números pares tem nesse
array.

Listagem 1. Exemplo Expressão lambda

static void Main(string[] args)

{
//Criamos uma array de numeros

int[] numeros = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

//utilizamos a função cout com uma expressão lambda como


parametro

int qtdPares = numeros.Count(x => x % 2 == 0); // qtdPares = 5

Console.WriteLine("Quantidades de Numeros Pares: {0}",

qtdPares.ToString());

Console.ReadKey();

Agora veremos um exemplo criando um delegate na Listagem 2. Imagine que você tem
uma situação onde precise passa um valor e receber o quadrado dele.

Listagem 2. Exemplo Expressão lambda com Delegate

delegate float deleg(float i);

static void Main(string[] args)

//Criamos uma variavel para receber um valor digitado pelo


usuário

float I = float.Parse(Console.ReadLine());

//Criamos uma variavel do tipo delegate que recebe

//a expressão lambda


deleg _Delegate = x => x * x;

float j = _Delegate(I); //j = 25

Console.WriteLine("O Quadrado de {0} é {1}",

I.ToString(), j.ToString());

Console.ReadKey();

Porque usar Expressões lambda?


Devemos usar as Expressões lambdas para deixar seu código muito mais simples e
legível, tornando-o mais agradável e elegante, principalmente na forma como embarca
métodos anônimos ao código.

Expressões lambda com LINQ


Bom agora que já obtivemos uma breve explicação sobre expressões lambda, vamos
mostrar sua utilização com LINQ.

LINQ é uma linguagem de consulta que foi introduzida a partir do .Net Framewok 3.5 e
é utilizada na manipulação de coleções, sejam elas vindas de um banco de dados,
documentos XML, coleções de objetos, etc. Assim, podemos definir o LINQ como uma
consulta que não tem dependência da fonte de dados.

Agora vamos criar um cenário para que possamos demonstrar a utilização do LINQ e a
melhora quando utilizamos lambda junto a ele. Vamos criar uma lista do tipo pessoa;
pessoa é um tipo de dado com as propriedades Nome, Idade, Cidade e Sexo.
Para montarmos esse exemplo, Abra o Visual Studio e crie um novo projeto clicando
em File -> New -> Project, conforme a Figura 1.

Figura 1. Criando o projeto.

Na janela seguinte digite “empty” na caixa de pesquisa e selecione BlankSolution.


Altere a propriedade Name para Lambda ou para o nome que preferir, conforme mostra
a Figura 2.
Figura 2. Novo projeto em Branco.

Se olharmos no Solution Explorer veremos que foi criada uma solução vazia, então
agora clique com o botão direito do mouse e escolha Add... e em seguida New Project.
Crie um projeto do Tipo Console Application e altere a propriedade Name para
Lambda, conforme demonstrado nas Figuras 3 e 4.

Figura 3. Adicionando um novo projeto a Solution.

Figura 4. Criando o projeto Console Application.


Agora vá até o Solution explorer e clique com o botão direito do mouse sobre o projeto
Console Application. Escolha a opção Add -> New Item e na janela que irá se abrir
escolha a opção Class, alterando a propriedade Name para Pessoa.cs. Agora vamos
implementar a classe pessoa, onde a mesma deve ter as propriedades Nome, Idade,
Cidade e Sexo e um construtor que recebe essas propriedades, conforme a Listagem 3.

Listagem 3. Criando Classe Pessoa

class Pessoa

public String Nome { get; set; }

public int Idade { get; set; }

public String Cidade { get; set; }

public String Sexo { get; set; }

public Pessoa(String _Nome, int _Idade,

String _Cidade, String _Sexo)

Nome = _Nome;

Idade = _Idade;

Cidade = _Cidade;

Sexo = _Sexo;

}
public override String ToString()

return "Nome: " + Nome + " Idade: " +

Idade.ToString() + " Cidade: " + Cidade + " Sexo: " +


Sexo;

No mesmo arquivo Pessoa.cs vamos também implementar uma classe que é uma lista
de pessoa. Essa classe conterá apenas um construtor sem parâmetros que servirá para
alimentarmos nossa lista de pessoas, ou seja, ao instanciarmos essa lista ela já estará
populada. Observe a Listagem 4.

Listagem 4. Alimentando a Lista de pessoas

class lPessoa : List<Pessoa>

public lPessoa()

this.Add(new Pessoa("JOSÉ SILVA", 21, "PIRACICABA", "M"));

this.Add(new Pessoa("ADRIANA GOMES", 18, "CURITIBA",


"F"));

this.Add(new Pessoa("JOSÉ NOVAES", 17, "CAMPINAS", "M"));

this.Add(new Pessoa("ANDRÉ NEVES", 50, "PIRACICABA",


"M"));
this.Add(new Pessoa("JONATHAN SANTOS", 45, "CAMPINAS",
"M"));

this.Add(new Pessoa("IVANILDA RIBEIRO", 29, "AMERICANA",


"F"));

this.Add(new Pessoa("BRUNA STEVAN", 28, "AMERICANA",


"M"));

this.Add(new Pessoa("ANDREIA MARTINS", 22,

"RIO DE JANEIRO", "F"));

this.Add(new Pessoa("ANTONIO DA SILVA", 30,

"POÇOS DE CALDAS", "M"));

this.Add(new Pessoa("JOSEFINO DANTAS", 48,

"CAMPOS DO JORDÃO", "M"));

this.Add(new Pessoa("HELENA MARIA GOMES", 25,

"SÃO PAULO", "F"));

this.Add(new Pessoa("DJONATHAN MASTRODI", 29,

"PIRACICABA", "M"));

this.Add(new Pessoa("BRUNO MARTINO", 55, "AMERICANA",


"M"));

this.Add(new Pessoa("ANA MARIA GONÇALVES", 60,

"PRAIA GRANDE", "F"));

this.Add(new Pessoa("MARCELA MARIA RIBEIRO", 85,

"JOAO PESSOA", "F"));


this.Add(new Pessoa("ISABELLA MENDES", 32, "PIRACICABA",
"F"));

this.Add(new Pessoa("IGOR MENDES", 22, "CAMPINAS", "M"));

this.Add(new Pessoa("ANTONIO JOSÉ FARIAS", 21,

"RIO DAS PEDRAS", "M"));

this.Add(new Pessoa("JULIO VIEIRA", 19, "ANDRADINA",


"M"));

this.Add(new Pessoa("MARIO RIBEIRO ", 13, "LIMEIRA",


"M"));

Agora que já criamos as classes necessárias, veremos a utilização de lambda com LINQ.
A cada exemplo mostraremos a forma com e sem lambda para que você possa ver as
diferenças entre os dois tipos.

Primeiro vamos utilizar a nossa lista de pessoas, porém deveremos retornar na tela
somente as pessoas que a primeira letra do Nome é “A”. Para isso devemos
implementar a rotina utilizando Expressões Lambda. Na Listagem 5 você confere o
exemplo com lamba e na Listagem 6 sem lambda.

Listagem 5. Exemplo de lambda com LINQ

lPessoa _lPessoa = new lPessoa();

foreach (var item in _lPessoa.Where

(p=>p.Nome.StartsWith("A")))

{
Console.WriteLine(item.ToString());

Listagem 6. Exemplo com LINQ

lPessoa _lPessoa = new lPessoa();

var pessoa= from p in _lPessoa

where p.Nome.StartsWith("A")

select p

foreach (var item in pessoa)

Console.WriteLine(item.ToString());

Agora neste novo exemplo desejamos exibir na tela somente as pessoas que tenham
idade entre 20 (vinte) e 30 (trinta) anos. Na Listagem 7 você confere o exemplo com
lambda e na Listagem 8 sem lambda.

Listagem 7. Segundo exemplo de lambda com LINQ

lPessoa _lPessoa = new lPessoa();

foreach (var item in _lPessoa.Where(p=>p.Idade>=20


&& p.Idade<=30))

Console.WriteLine(item.ToString());

Listagem 8. Segundo exemplo de LINQ

lPessoa _lPessoa = new lPessoa();

var pessoa= from p in _lPessoa

where p.Idade>=20 && p.Idade<=30

select p

foreach (var item in pessoa)

Console.WriteLine(item.ToString());

Utilizando o lambda com o LINQ o código fica bem mais curto, economizando tempo.
Com as expressões lambda você conseguirá ser bem mais produtivo.
Threads
Por meio de threads uma aplicação pode executar diferentes tarefas de forma

concorrente, evitando o sobrecarregamento do fluxo principal do programa. Por

exemplo, por meio de threads podemos executar um procedimento pesado em


segundo plano, deixando a interface com o usuário livre para interações.

Para dar os primeiros passos com threads em C# você pode ver o seguinte
artigo:

Iniciando com Threads


no C# - Revista easy
.Net Magazine 25
Facebook Twitter
(0) (0)

O artigo trata de como utilizar threads no C#, tornando possível que sua
aplicação execute “N” tarefas ao mesmo tempo. Desta forma, um processo não
irá depender de outro para ser executado, por exemplo.

Do que se trata o artigo

O artigo trata de como utilizar threads no C#, tornando possível que sua aplicação
execute “N” tarefas ao mesmo tempo. Desta forma, um processo não irá depender de
outro para ser executado, por exemplo.

Em que situação é útil


A conexão com um banco de dados pode ser um processo demorado e em alguns casos
este processo é realizado na inicialização de uma aplicação. Esta demora pode causar ao
usuário a impressão de que a aplicação esteja “travada”. Neste contexto, uma forma de
evitar esta situação é utilizar uma thread para realizar este processo de conexão,
permitindo que a aplicação trabalhe demais processos enquanto a conexão não é
concluída, por exemplo, gerar e exibir formulários da aplicação. Esse é um dos
exemplos em que as threads podem ser utilizadas.

Resumo do DevMan

O suporte a threads no contexto de aplicações (softwares) é um recurso fornecido pelo


próprio Sistema Operacional. Uma thread é responsável por executar tarefas dentro de
uma aplicação. O conceito de multithreading é aplicado quando estas tarefas são
divididas em duas ou mais threads, buscando diminuir o tempo de processamento destas
tarefas. O .NET oferece um componente que manipula threads dentro de uma aplicação,
chamado BackgroundWorker. Este artigo introduz o conceito de threads e apresenta
uma implementação para conexão com o banco de dados, utilizando uma thread, por
meio do componente BackgroundWorker.,br>Autores: Everton Coimbra e Giuvane
Conti

Threads refere-se à técnica em que a execução de um determinado processo (tarefa) é


dividido em dois ou mais processos, permitindo a execução de diversas tarefas de
maneira concorrente.

O conceito de multithreading apresenta um modelo de programação que permite


executar múltiplas tarefas, compartilhando recursos de um processo e executando de
forma independente. Logo, o programador deve evitar que duas ou mais threads operem
em um mesmo recurso (arquivo, endereço de memória ou conexão de rede) Um sistema
que utiliza multithreading possui melhor desempenho em computadores com múltiplos
núcleos de processamento, pois esses núcleos permitem que as tarefas de
cada thread sejam executadas de forma simultânea. Este escalonamento de tarefas
(multithreads) permite que threads trabalhem de maneira simultânea e independente,
logo, o programador deve evitar que duas ou mais threads operem em um mesmo
recurso (arquivo, endereço de memória ou conexão de rede). Caso aconteça a aplicação
apresentará um erro conhecido como deadlock. Theads devem ser programadas para
cooperar e não competir. O escalonamento (Nota do DevMan 1) entre threads segue a
mesma lógica do escalonamento entre processos. Um método que busca evitar situações
de deadlock é conhecido como Thread safety. Este conceito será abordado no decorrer
do artigo.

Nota do DevMan 1

Escalonamento de processos é uma atividade organizacional feita pelo escalonador da


CPU. O escalonador escolhe o processo que tem mais prioridade e menos tempo e
coloca-o na memória principal, desta maneira o processador evita ficar ocioso.

Para que o multithreading seja manipulado corretamente, as linhas de execução devem


ser sincronizadas para que os dados sejam processados na ordem correta. Desta maneira
trabalhar com este conceito requer muito cuidado na implementação, pois todas
as threads que estão sendo manipuladas dentro devem funcionar muito bem
sincronizadas, principalmente se estiverem trabalhando com variáveis dependentes.
Sendo assim, deve ser respeitada a ordem em que foram escalonadas, para evitar um
possível deadlock.

Deadlock é caracterizado por um impasse entre dois ou mais processos, sendo assim
impedidos de continuar suas execuções, ou seja, bloqueados.

Para exemplificar threads, ao realizar um Ctrl + Shift + Del em seu computador e


acessando a opção “Iniciar o Gerenciador de tarefas” é possível notar que o sistema
operacional Windows trabalha com várias tarefas ao mesmo tempo, como pode ser visto
na Figura 1.

Figura 1. Gerenciador de tarefa do MS Windows.

Em uma situação onde um determinado processamento seja realizado por um


processador de cinco núcleos, em teoria, o processador seria capaz de realizar cinco
aplicações ao mesmo tempo, porém, existem outros diversos aplicativos em execução.
Isso é possível porque o processador consegue trabalhar com todos os aplicativos e
apresentar resultados satisfatórios devidos à velocidade de seu processamento. Sendo
assim os aplicativos parecem estar em execução simultaneamente.

Assim como os processos, as threads possuem estados em seu ciclo de vida, dentro de
um sistema as ações e ordens de uma thread são controladas por uma tabela
de threads que armazena informações de seu fluxo de execução, o nome desta tabela
é Thread TCB (Task Control Block) e a mesma contêm:

• O endereço da pilha;

• O contador de programa;

• Registrador de instruções

• Registradores de dados, endereços, flags;

• Endereços das threads filhas;

• Estado de execução.

A comunicação entre threads pode ser realizada por meio de variáveis globais do
processo que as criou. Existem técnicas que podem ser utilizadas para realizar o
sincronismo das mesmas, como monitores e semáforos, que possuem recursos capazes
de indicar a ordem de execução dos processos e são utilizadas para evitar condições
concorrência. A "

Como você deve ter visto, com threads conseguimos implementar processamento
assíncrono e paralelo, o que pode nos garantir ganho de desempenho e manter a
aplicação funcional durante processamentos pesados. Para saber mais sobre
programação assíncrona e paralela em C#, confira os conteúdos abaixo:

Programação
Assíncrona
(Multithreading) em
.NET com C#
Facebook Twitter
(9) (0)
Veja neste artigo o que é, quando e como utilizar multithread. Thread é uma
forma de um processo dividir a si mesmo em duas ou mais tarefas, podendo
executar elas concorrentemente. O suporte a threads é oferecido pelos
Sistemas Operacionais, ou por bibliotecas de algumas linguagens de
programação.

1. Overview sobre Multithreads

1.1 O que são Threads?

Thread é uma forma de um processo dividir a si mesmo em duas ou mais tarefas,


podendo executar elas concorrentemente. O suporte a threads é oferecido pelos
Sistemas Operacionais, ou por bibliotecas de algumas linguagens de programação

1.2 O que são Aplicações Multithreads?

Um programa single - thread (thread única) inicia na etapa 1 e continua


seqüencialmente (etapa 2, etapa 3, o passo 4) até atingir a etapa final. Aplicações
multithread permitem que você execute várias threads ao mesmo tempo, cada uma
executando um passo por exemplo.

Cada thread é executada em seu próprio processo, então, teoricamente, você pode
executar o passo 1 em uma thread e, ao mesmo tempo executar o passo 2 em outra
thread e assim por diante. Isso significa que a etapa 1, etapa 2, etapa 3 e etapa 4
podem ser executadas simultaneamente.

Na teoria, se todos os quatro passos durassem o mesmo tempo para executar, você
poderia terminar o seu programa em um quarto do tempo que levaria para executar os
quatro passos sequencialmente.

Então, por que todos os programas não são multithread? Uma das razões nos diz que
junto com a velocidade vem a complexidade. Controlar um grande número de threads
requer bastante trabalho e conhecimento. Imagine se um passo de alguma forma
dependesse das informações no passo 2. O programa pode não funcionar
corretamente se o passo 1 termina cálculo antes do passo 2 ou vice-versa.

Para ajudar entender essa complexidade, vamos fazer uma analogia comparando um
programa multithread com o corpo humano. Cada um dos órgãos do corpo (coração,
pulmões, fígado, cérebro) são processos e cada processo é executado
simultaneamente. Assim, o corpo humano seria como uma grande aplicação
multithread. Os órgãos são os processos em execução simultânea, eles dependem
uns dos outros, sendo que todos os processos se comunicam através de sinais
nervosos e fluxo de sangue, desencadeando uma série de reações químicas. Como
em todos os aplicativos multithread, o corpo humano é muito complexo. Se algum
processo não obtiver informações de outros processos, ou um processo retardar ou
acelerar, vamos acabar com um problema médico. É por isso que, esses processos
precisam ser sincronizados corretamente para funcionar normalmente.
1.3 Quando Utilizar Multithread?

Quando precisamos de desempenho e eficiência. Por exemplo, vamos pensar em um


jogo de vídeo game. Imagine um jogo possuindo uma única thread, tanto para o
processamento de imagens quanto para o processamento de áudio. Seria possível?
Sim, seria. Mas esse é o cenário ideal para garantir a performance? Não. O ideal seria
criar threads para o processamento das rotinas de imagens e outra para rotinas de
áudio.

Outro cenário comum onde você precisaria de Multithread seria um sistema de


tratamento de mensagens. Imagine um aplicativo que captura milhares de mensagens
simultaneamente. Você não pode capturar eficientemente uma série de mensagens ao
mesmo tempo em que você está fazendo algum outro processamento pesado, porque
senão você pode perder mensagens. Então cabe aqui dividir o processamento de
captura e processamentos paralelos em threads diferentes.

Cada um destes cenários são usos comuns para multithread e se utilizado de maneira
correta, essa técnica vai melhorar significativamente o desempenho de aplicações
similares.

Em linhas gerais, os benefícios do uso das threads advém do fato do processo poder
ser dividido em várias threads; quando uma thread está à espera de determinado
dispositivo de entrada/saída ou qualquer outro recurso do sistema, o processo como
um todo não fica parado, pois quando uma thread entra no estado de 'bloqueio', uma
outra thread aguarda na fila.

1.4 Quando não utilizar Multithread?

Geralmente quando aprendemos os recursos de multithread, ficamos fascinados com


todas as possibilidades que essa técnica nos oferece e saímos usando – a em todos
os tipos de programas que vemos pela frente.

É preciso tomar muito cuidado com isso, pois ao contrário de um programa single
thread, você está lidando com muitos processos ao mesmo tempo, com múltiplas
variáveis ??dependentes. Isso pode se tornar muito complicado de se controlar.
Pensemos em multithread, um malabarismo. Malabarismos com uma única bola em
sua mão (embora meio chato) é bastante simples. No entanto, se você é desafiado a
colocar duas dessas bolas no ar, a tarefa é um pouco mais difícil. Imagine três, quatro,
cinco bolas então, e as coisas vão se tornar cada vez mais difíceis. Como a contagem
de bola aumenta você tem chances cada vez maiores de deixar cair alguma bola.

Trabalhar com multithreads requer muito trabalho e concentração. É preciso ressaltar


que o uso dessa técnica em sistemas simples, geralmente não nos leva a um cenário
muito proveitoso. Mesmo que o programador seja um cara muito bom e desenhe
direitinho o sistema utilizando threads, fatalmente o ganho em performance não irá
compensar caso seja necessário dar manutenções futuras no sistema.

2. Como Utilizar Threads

2.1 Criando uma Multithread


Dentro do .Net, para criar uma thread devemos utilizar o Namespace
System.Threading. Nesta Namespace se encontra a classe Thread, que é a que
usamos para instanciar uma thread.

No exemplo abaixo, veremos como instanciar uma thread e como rodá – la. Antes de
tudo, uma thread sempre realiza determinada tarefa codificada em um método. Sendo
assim, primeiramente vamos definir um método estático que realizará alguns prints em
uma aplicação de Console:

Lembrando que todos os exemplos citados aqui foram feitos em C#, no Visual Studio
2008, utilizando um projeto do tipo Console Application.

public static void run()

for (int i = 0; i < 5; i++)

Console.WriteLine("Thread Atual - " + Thread.CurrentThread.Name +


i);

Thread.Sleep(1000);

Este método apenas escreve na tela o nome da thread atual dentro de um loop for.

Repare que no código abaixo este método será utilizado para instanciar uma thread,
pois para criar threads, precisamos apontar o método que ela executará, passando
como parâmetro ao construtor da thread um objeto ThreadStart, que recebe o nome
do método a ser executado pela thread.

Agora no método Main do seu Console Application, digite as seguintes linhas de


código:

static void Main(string[] args)

{
Console.WriteLine("Thread principal iniciada");

Thread.CurrentThread.Name = "Principal - ";

Thread t1 = new Thread(new ThreadStart(run));

t1.Name = "Secundária - ";

t1.Start();

for (int i = 0; i < 5; i++)

Console.WriteLine("Thread atual - " + Thread.CurrentThread.Name


+ i);

Thread.Sleep(1000);

Console.WriteLine("Thread Principal terminada");

Console.Read();

No código acima vemos como instanciar uma thread e como iniciá-la:

Thread t1 = new Thread(new ThreadStart(run));

t1.Name = "Secundária - ";

t1.Start();
Depois fazemos o mesmo loop do método run, dentro do método main para que os
dois métodos rodem em concorrência.

Observe a chamada do método Sleep da classe Thread. Esse método suspende a


execução da thread no período especificado como parâmetro em milissegundos.

Ao executar nosso Console Application temos o seguinte output:

2.2 Sincronização de Threads

O maior problema ao usar múltiplas threads é organizar o código de forma a dividir


racionalmente o uso de recursos que elas venham a compartilhar. Esses recursos
podem ser variáveis, conexões com banco de dados, dispositivos etc. Essa forma de
organizar o acesso aos recursos compartilhados se chama Sincronização de Threads.
Caso nós não sincronizarmos corretamente as threads, fatalmente cairemos nos
famigerados deadlocks.

Para realizarmos uma boa sincronização de threads, devemos conhecer alguns


métodos e propriedades de uma thread:

Métodos:

 Suspend(): Suspende a execução de uma Thread, até o método Resume() seja


chamado.
 Resume(): Reinicia uma thread suspensa. Pode disparar exceções por causa de
possíveis status não esperados das threads.
 Sleep(): Uma thread pode suspender a si mesma utilizando esse método que
espera um valor em Milisegundos para especificar esse tempo de pausa.
 Join(): Chamado por uma thread, faz com que outras threads espere por ela até
que ela acabe sua execução.
 CurrentThread(): Método estático que retorna uma referência à thread em
execução

Estados de uma Thread:

Os estados podem ser obtidos usando a enumeration ThreadState, que é uma


propriedade da classe Thread. Abaixo seguem os valores dessa enumeration:

 Aborted: Thread já abortada;


 AbortRequested: Quando uma thread chama o método Abort();
 Background: Rodando em bnackground;
 Running: Rodando depois que outra thread chama o método start();
 Stopped: Depois de terminar o método run() ou Abort();
 Suspended: Thread Suspendida depois de chamar o método Suspend();
 Unstarted: Thread criada mas não iniciada.
 WaitSleepJoin: Quando uma thread chama Sleep() ou Join(), ou quando uma
outra thread chama join();

Propriedades:

 Name: Retorna o nome da thread;


 ThreadState: Retorna o etado de uma thread;
 Priority: Retorna a prioridade de uma thread. As prioridades podem ser:
o Highest
o AboveNormal
o Normal
o BelowNormal
o Lowest

 IsAlive: Retorna um valor booleano indicando se uma thread esta “viva” ou não;
 IsBAckground: Retorna uma valor booleano indicando se a thread está rodando
em Background ou Foreground;

2.2.1 Sincrinizando Threads

Quando temos várias threads que compartilham recursos, precisamos fornecer acesso
sincronizado a esses recursos. Temos de lidar com problemas de sincronização
relacionados com o acesso simultâneo a variáveis ??e objetos acessíveis por múltiplas
threads ao mesmo tempo. Para ajudar nesta sincronização, no .Net, podemos fazer
com que uma thread bloqueie um recurso compartilhado enquanto o utiliza. Podemos
pensar nisso como uma caixa onde o objeto está disponível e apenas um segmento
pode entrar e outra thread só terá acesso ao recurso quando a outra que esta o
utilizando saia da caixa.

Vamos exemplificar o uso de múltiplas threads. No mesmo Console Application,


vamos primeiramente criar uma classe que conterá um método, que por sua vez será
utilizado por algumas threads concorrentemente:
class Printer

public void PrintNumbers()

for (int i = 0; i < 5; i++)

Thread.Sleep(100);

Console.Write(i + ",");

Console.WriteLine();

No método Main da classe program, vamos instanciar um objeto da classe Printer,


criar três threads passando o método PrintNumbers para cada thread criada, ou seja,
as três threads tentarão utilizar o mesmo recurso:

static void Main(string[] args)

Console.WriteLine("======MultiThreads======");

Printer p = new Printer();

Thread[] Threads = new Thread[3];

for (int i = 0; i < 3; i++)


{

Threads[i] = new Thread(new ThreadStart(p.PrintNumbers));

Threads[i].Name = "threadFilha " + i;

foreach (Thread t in Threads)

t.Start();

Console.ReadLine();

Ao executar o programa temos o seguinte resultado:


Podemos ver como o Thread Scheduler nativo está chamando o objeto Printer para
imprimir os valores numéricos. Como podemos ver temos um resultado inconsistente
pois não temos qualquer tipo de controle de sincronização das threads, sendo que
elas estão acessando o método PrintNumbers() de maneira aleatória.

Existem diversos meios de sincronização que podemos utilizar para garantir o


processamento correto de nossos programas multithread.

A) Usando o Lock

Poderíamos por exemplo utilizar a pajavra-chave Lock dentro do método


PrintNumbers():

public void PrintNumbers()

lock (this)

for (int i = 0; i < 5; i++)

Thread.Sleep(100);

Console.Write(i + ",");

Console.WriteLine();

A palavra-chave lock obriga-nos a especificar um token (uma referência de objeto) que


deve ser adquirido por uma thread para entrar no escopo do lock (bloqueio). Quando
estamos tentando bloquear um método em nível de instância, podemos simplesmente
passar a referência a essa instância. (Nós podemos usar esta palavra-chave para
bloquear o objeto atual) Uma vez que a thread entra em um escopo de bloqueio, o
sinal de trava (objeto de referência) é inacessível por outros segmentos, até que o
bloqueio seja liberado ou o escopo do bloqueio seja encerrado.

Execute o programa e você terá a seguinte tela:

B) Utilizando Monitor Type

Método Monitor.Enter () é o destinatário final do token da thread. Precisamos escrever


todo o código do escopo de bloqueio dentro de um bloco try. A cláusula finally
assegura que o token de thread seja liberada (usando o método Monitor.Exit () ,
independentemente de qualquer exceção de runtime.

Na classe Printer, modifique o método PrintNumbers como a seguir:

public void PrintNumbers()

Monitor.Enter(this);

try

for (int i = 0; i < 5; i++)

{
Thread.Sleep(100);

Console.Write(i + ",");

Console.WriteLine();

catch (Exception ex)

Console.WriteLine(ex.Message);

finally

Monitor.Exit(this);

Ao rodar o programa temos o seguinte resultado:


Bom, chegamos ao fim de nosso artigo.

Programação
Assíncrona com C#
Facebook Twitter
(3) (0)

Na aplicação de exemplo deste artigo uma das principais classes para


execução assíncrona de tarefas –
System.ComponentModel.BackgroundWorker – é usado para criar um conjunto
de tarefas sendo executadas independentemente.

Artigo do tipo Tutorial


Recursos especiais neste artigo:
Conteúdo sobre boas práticas.

Programação Assíncrona com C# - Parte 1


É lugar comum no meio de desenvolvimento de software dizer que enquanto houve um
progresso muito grande no hardware os programas não vêm conseguindo aproveitar todo este
progresso.

Alguns dizem que o hardware é sub utilizado e que os programas são até mesmo mal
feitos, a ponto de não usarem todos os recursos de processamento e memória colocados
à disposição.

Discussões à parte, um fato é que realmente os softwares estão subutilizando o hardware


existente que também é bem potente. Por outro lado, aproveitar todo este potencial é
muito complexo.

A “evolução” dos computadores atuais se deu mais em aumentar o número de


processadores disponíveis num mesmo dispositivo do que aumentar o número de
operações por segundo que são executadas. Resumindo: se tem mais processadores para
executar os programas, mas, a frequência de velocidade destes aumentou pouco se é que
aumentou.

Para utilizar efetivamente este potencial é necessário aproveitar a existência de vários


processadores e criar rotinas paralelas, que sejam executadas juntamente com outras e
que o sistema operacional, ao delegar que se tratam de rotinas paralelas faça a
distribuição destas no processador.

Sistemas operacionais mais modernos – Windows 7 e acima, Mac OS e o Linux – lidam


bem com paralelismo e distribuição das tarefas nos processadores, mas, boa parte dos
programas só agora começa a ser preparada para executar eficientemente rotinas
paralelas.

O Framework .NET permite criar estas rotinas e controla-las de uma maneira mais fácil,
não que será tão fácil como a programação tradicional, mas, com um pouco de trabalho
e organização se consegue bons resultados.

Para melhores resultados é preciso entender diferenças sutis entre rotinas paralelas e
assíncronas. A primeira modalidade diz respeito em tarefas sendo executadas de forma
simultânea podendo os seus resultados serem ou não esperados ao mesmo tempo. Já
tarefas assíncronas são sempre executadas em paralelo, mas não é necessário aguardar a
sua finalização, ou, o término de uma rotina não depende do término de outra.

Como é de se esperar o maior problema a ser resolvido é controlar o comportamento de


cada tarefa sendo executada assincronamente. Assim, recursos do Framework .NET vão
fazer uma boa diferença dando ferramentas bem flexíveis e mais simples para
compreensão.

Na aplicação de exemplo deste artigo uma das principais classes para execução
assíncrona de tarefas – System.ComponentModel.BackgroundWorker – é usado para
criar um conjunto de tarefas sendo executadas independentemente sendo que cada uma
possui seus eventos e pode ser interrompida sem afetar a execução da outra.

Em que situação o tema é útil


As técnicas para controlar rotinas paralelas e assíncronas – sim, existem diferenças –
apresentadas no artigo ajudarão aos desenvolvedores a iniciar a utilização dos recursos
do Framework .NET e da linguagem C# voltados à execução de várias rotinas
aproveitando assim os recursos de processamento dos computadores modernos sem
perder o controle, possibilitando ao usuário além de iniciar várias rotinas paralelas,
saber como está o seu andamento e também cancelar a rotina quando achar necessário.

Hardware e paralelismo
Vamos começar de um jeito diferente. Se puder (e estiver usando o Windows no seu
computador, claro), abra o gerenciador de tarefas. Vá para a aba Desempenho e dê uma
conferida como está o trabalho dos processadores do seu PC (ou notebook, ou, seja lá o
que estiver usando...). Se você for um cara sortudo vai ter quatro ou até oito núcleos de
processamento. Se for uma pessoa mais modesta, terá seus meros dois processadores, o
que já é um bom começo. Guarde estes dados que já falarei novamente sobre estes.

Há pouco mais de dez anos, o máximo que um computador pessoal poderia ter era um
processador com um núcleo simples e quando muito, coprocessadores aritméticos que,
segundo especificações técnicas daquela época, eram usados para auxiliar no cálculo de
tarefas matemáticas mais complexas. O problema é que nunca ficou muito claro para
desenvolvedores de aplicações comerciais quando e como estes processadores auxiliares
podiam ser usados. Desde os tempos do Delphi eu nunca cheguei a ver uma rotina que
fosse feita para rodar no processador aritmético ou algo assim e realmente não pude
pesquisar sobre este assunto.

Mas, segundo a lei te Moore, os processadores dobrariam de capacidade a cada dezoito


meses, e, então, alcançariam um limite físico para o número de operações que podem
ser feitas por segundo. Bem, parece que estamos perto deste limite. Há um bom tempo a
velocidade dos processadores não tem grandes saltos em desempenho e computadores
mais comuns, dificilmente ultrapassam os 2.8 GHZ de frequência. O maior problema
nem é tanto aumentar a velocidade e sim, dissipar o calor produzido pelo processador.
Problemas com hardware e limitações físicas à parte, estas limitações vem sendo muito
bem resolvidas através da montagem de processadores em paralelo gerando os
processadores multicore que temos e que você deve ter percebido ao usar o gerenciador
de tarefas.

Assim, resolve-se o problema de velocidade aumentando o número de processadores.


Em teoria se com um processador com a velocidade de 2.2 GHZ se tem um número
determinado de tarefas executadas por segundo, ao termos dois processadores da mesma
frequência se terá o dobro desta velocidade. Em teoria, mas não é o que ocorre na
prática.

Acontece que aumentando o número de núcleos de processamento aumenta-se a


complexidade em coordenar o seu funcionamento. É semelhante à gestão de projetos. Se
você tem um projeto que precisa ser entregue em três semanas e tem quatro pessoas, se
dobrar o número de pessoas não significa necessariamente que o projeto vai ser
entregue na metade do tempo. Isto porque você vai precisar coordenar esta equipe e
distribuir as tarefas para que uma pessoa da equipe não fique esperando outra terminar
uma tarefa da qual a sua depende terminar o trabalho para começar o dela.
Algo análogo a isto ocorre nas CPUs multiprocessadas. É preciso coordenar para que os
processadores não fiquem ociosos esperando outro terminar uma tarefa da qual a sua
depende. Isto é bem complicado e por causa disto, muitos softwares são feitos quase
todos sequencialmente – isto é sem tarefas paralelas – ou, quando muito, existem rotinas
paralelas que são mal gerenciadas e acabam causando congelamento da aplicação que
fica aguardando uma tarefa terminar para poder continuar com seu trabalho.

Por causa disto ainda há alguma frustração entre os usuários de programas,


principalmente aplicações corporativas e comerciais, que muitas vezes investem em
hardware, mas quando colocam seus programas para serem executados nestas máquinas
não tem o desempenho esperado.

Parte deste problema ocorre principalmente porque o software não é criado para
aproveitar os recursos de multiprocessamento. Por outro lado, é muito comum que
nestas máquinas multicore, a frequência dos processadores fique muito baixa, em torno
dos 1.8 GHZ.

Como solução para isto os desenvolvedores precisam aprender a domesticar as rotinas


paralelas. Existe uma brincadeira na minha equipe de trabalho que diz que “ninguém
controla uma thread selvagem”.

A boa notícia com o Framework .NET isto pode ser feito mais facilmente. É possível
começar bem simples, executando processamento paralelo até criar um verdadeiro
thread pool e controlar múltiplas tarefas sendo executadas paralelamente sem permitir
que uma interfira na outra.

Thread pool é o termo usado para uma das formas de se controlar várias threads dentro
de um programa estabelecendo um ID para esta e podendo interferir no seu
funcionamento. Como controle pode se citar a sua interrupção, pausa e também
monitoramento do progresso do seu andamento. Uma das maneiras que isto pode ser
implementado em C# e no Framework .NET é através do uso de collections. Na
aplicação de exemplo é criado um controle análogo a este.

Paralelismo x Rotinas Assíncronas


Rotinas paralelas são executadas em segundo plano, permitindo que outras tarefas sejam
executadas ao mesmo tempo. Sua utilização mais comum é quando o software deve
fazer uma operação longa como uma consulta no sistema de arquivos ou em um banco
de dados, um cálculo mais complexo, uma renderização de imagens, resumindo,
qualquer operação que vai levar algum tempo e que se deseja que o programa continue
respondendo aos comandos do usuário.

Geralmente são executadas enquanto a interface com o usuário mostra o andamento da


tarefa para o usuário que pode ter ou não a opção de cancelar a operação. Existe outra
forma de usar tarefas paralelas que é distribuindo seu trabalho em sub-rotinas sendo que
cada uma faz uma parte da tarefa principal. Isto já é mais difícil de fazer porque neste
caso, mesmo que o trabalho seja executado mais rápido, deve se ter um bom
planejamento para fazer a separação das partes e, pode ser que em alguns casos, uma
tarefa deva esperar o término de outra para continuar seu trabalho. Neste caso, mesmo
executando as tarefas de forma paralela o processamento é síncrono que significa que
uma tarefa precisa esperar o término de outra para ser executado.

Quando se fala em rotinas assíncronas está se tratando de uma forma de usar o


processamento paralelo onde não é necessário aguardar o término de uma rotina para
executar outra. Este tipo de rotina é usada quando não é necessário aguardar o seu
término ou quando o resultado final não é necessário para que se possa continuar
usando o software. Exemplos bem práticos de rotinas assíncronas são o envio de e-mail,
um envio de solicitação de processamento para um banco de dados (como uma consulta,
um relatório, um cálculo que possa ser executado enquanto o usuário esteja fazendo
outras tarefas).

Ao desenvolver rotinas assíncronas o programador precisa ter um controle do seu


andamento, meios para seu cancelamento e alguma forma de conhecer quando esta foi
concluída para poder mostrar o resultado para o usuário ou fazer algo com o valor que
foi devolvido. Sempre que possível deve se optar por este tipo de rotina, principalmente
quando se deseja criar programas que precisam estar constantemente disponíveis para o
usuário e não congelar a qualquer custo.

Recursos do Framework .NET


Para poder desenvolver rotinas assíncronas que atendam aos requisitos colocados antes:
controle do andamento, término e cancelamento, os desenvolvedores que usam a
plataforma .NET contam com vários recursos. O principal é a classe
System.Threading.Thread que é usada para fazer com que o código seja executado de
forma paralela. Esta é a classe base para o processamento em paralelo e o seu
funcionamento bem simples. Considere um método qualquer, como o presente
na Listagem 1.

Listagem 1. Estrutura de um método

public void MeuMetodo()

// faz alguma coisa...

var obj = "Teste";

Console.WriteLine(obj);

// Faz mais alguma coisa...

}
Os detalhes da implementação não são importantes por isso foram omitidos, considere
que este método faça qualquer tipo de processamento. No corpo do método não é
necessário nenhum comando ou classe especial, pode ser uma atribuição de valores,
uma consulta a um banco de dados ou qualquer coisa. O ponto principal é que para que
este método seja executado em uma thread separada e ficando assim em segundo plano,
enquanto outros métodos possam ser executados em paralelo basta usar o código a
seguir:

var thread = new System.Threading.Thread(MeuMetodo);thread.Start();

Note que este método não retorna nada e nem requer parâmetros, porém, podemos criar
métodos que usam parâmetros e tenham algum retorno. A classe
System.Threading.Thread é muito versátil e possui métodos para cancelar, suspender e
continuar sua execução. Se você puder, verifique a documentação sobre a classe e o seu
namespace para conhecer melhor os recursos que podem ser usados, embora, mais à
frente, veremos outra forma de controlar execução de threads que permite mais controle
com menos complexidade.

Dificuldades ao utilizar Threading


O maior problema ao se usar threads e a classe Thread do Framework .NET é a
dificuldade em controlar o seu andamento e também de oferecer ao usuário maneiras
realmente eficazes de cancelamento. Isto ocorre porque o desenvolvedor é quem fica
responsável por realizar estas tarefas e implementar da maneira que achar melhor. De
uma maneira bem direta, é preciso começar tudo do zero porque apesar de toda a
flexibilidade oferecida, o programador é quem decide como fazer o controle.

Dois pontos fundamentais para o desenvolvimento assíncrono são reportar o andamento


da tarefa e permitir o seu cancelamento. Para que isto ocorra, deve se criar uma forma
de comunicação entre o processo que chamou a thread e ela. Uma das formas de fazer a
comunicação do andamento e passar para a thread um objeto que possa ser notificado
sempre que necessário como um delegate usado em conjunto com um EventHandler.

Delegates funcionam como “ponteiros” para métodos e normalmente são usados


juntamente com ponteiros para que uma rotina dentro de uma classe possa executar uma
ação quando algo acontecer como, por exemplo, for necessário reportar o progresso do
trabalho.

Assim, vamos considerar uma classe, como a Listagem 2 demonstra.

Listagem 2. Demonstrando uso de delegates

1 public class foo

2 {

3 public delegate void FazAlgo(int valor);


4 public event FazAlgo Executa;

6 public void Faz()

7 {

8 for (int i = 0; i < 1000; i++)

9 if (i % 100 == 0 && Executa != null)

10 Executa(i);

11 }

12 }

Nesta classe a linha 3 cria o delegate que é a assinatura para o método que será acoplado
ao evento. O assessor deve sempre ser public seguido da palavra-chave delegate. O tipo
de retorno pode ser qualquer um, porém, o mais comum é void. O delegate pode ou não
ter parâmetros. Novamente o usual é que se tenham estes já que seu uso só é justificável
caso se deseje passar valores entre rotinas diferentes.

Um delegate precisa estar acompanhado de um evento que será inspecionado e usado


para fazer a chamada ao método que foi anexado ao delegate. Neste exemplo, a linha 4
cria um evento chamado Executa. Novamente o assessor deve ser público seguido da
palavra event, o tipo é o do delegate criado anteriormente seguido de um nome.

Seguindo em frente a classe tem um método público chamado Faz() que se inicia na
linha 6 que teoricamente será executado em segundo plano. Este método executa um
loop de zero a mil e a cada cem números passa o valor para um método que vai fazer
alguma coisa. Observe na linha 9 um passo importante ao trabalhar com eventos que é a
verificação do evento estar nulo. Se o evento não estiver, a classe faz uma chamada ao
evento (linha 10).

Para consumir este código, teríamos alguma coisa parecida com o código da Listagem
3.

Listagem 3. Consumindo eventos e delegates

1 using System;

2
3 public class program

4 {

5 public static void Main()

6 {

7 var obj = new foo();

8 obj.Executa += DisparaFazAlgo;

9 obj.Faz();

10 }

11

12 public void DisparaFazAlgo(int valor)

13 {

14 Console.WriteLine(valor);

15 }

16 }

Trata-se de um programa que se inicia criando uma instância da classe (linha 7) e na


linha 8 acopla o método DisparaFazAlgo() o evento Executa da classe. Em seguida é
feita uma chamada ao método Faz() para executar o código. O método
DisparaFazAlgo() está descrito a partir da linha 12. Note que o cabeçalho segue o
modelo definido pela classe.

Assim, de uma forma bem básica, são estes os passos para que métodos dentro de uma
classe possa executar código externo, geralmente fazendo parte da classe um nível
acima.

Espero que tenha ficado claro que a principal desvantagem de se fazer tudo do zero é a
dificuldade e o número de passos necessários, sem falar da necessidade de estar atendo
a muitos detalhes. É importante também notar que, existem situações que somente esta
abordagem poderá ser usada em casos de desenvolver rotinas para bibliotecas de
funções que serão usadas em múltiplos projetos. Além disto, desenvolver tudo a partir
do zero dá a possibilidade de se controlar cada aspecto do código.

Background Worker
Como quase todos os recursos para o Framework .NET existe uma opção mais acessível
e que oferece blocos prontos para manipulação de threads. Esta opção está na classe
System.ComponentModel.BackgroundWorker que é na verdade uma classe que
encapsula o funcionamento de threads com propriedades, eventos e métodos prontos
para serem usados em seus projetos de qualquer natureza. Como “projetos de qualquer
natureza”, estão incluídas as class libraries (dlls), projetos Windows Forms, WPF e
Web. Esta classe está presente nos projetos Windows Forms através de um componente
da ToolBox do Visual Studio, mas, pode ser criado diretamente no código.

O seu grande diferencial são recursos mais eficientes (e também mais fáceis de usar)
para controle de execução, progresso e cancelamento além de um melhor tratamento do
final da thread. Para se ter uma ideia de como o trabalho é realizado são duas as
principais propriedades que precisam ser configuradas ao se criar um objeto desta
classe:

1. WorkerReportProgress: indica se as rotinas que estão vinculadas com o


componente (ou objeto se preferir) vão dar informações sobre o andamento das tarefas
vinculadas. Neste caso, ao executar uma chamada para o método ReportProgress do
componente, passando o percentual concluído do trabalho e opcionalmente, algum outro
dado que for necessário.

2. WorkerSupportsCancellation: dá suporte para que a rotina que está sendo


executada seja cancelada de forma assíncrona. Neste caso, ao finalizar o trabalho deve
se criar um EventHandler para o evento RunWorkerCompleted que verifique se a rotina
foi cancelada.

Como se pode perceber, com poucas propriedades já se tem boa parte do trabalho
executado. Como foi citado o acoplamento de eventos, são três os principais eventos
que devem ser tratados ao se trabalhar com este componente. O primeiro é o DoWork.
Este evento é chamado pelo método RunWorkAsync(). Ao fazer isto é necessário
escrever um método com a assinatura para este evento que é a seguinte:

private void worker_DoWork(object sender, DoWorkEventArgs e)

O primeiro parâmetro é usado para capturar de qual objeto partiu a chamada para a
rotina assíncrona e o segundo, armazena dois parâmetros que podem ser passados para a
rotina:

1. Argument: valor do tipo object podendo armazenar qualquer dado.

2. Result: outra propriedade do tipo object que armazena o resultado da rotina e é


enviada para o método executado ao término da tarefa. Use esta propriedade para tratar
o retorno de métodos que foram executados em background.
Durante a execução da tarefa, pode ser que seja importante mostrar o andamento do
trabalho usando, por exemplo, uma barra de progressos. Um aspecto importante da
utilização de rotinas assíncronas ou threads é que estas não podem acessar os
componentes da interface diretamente. Isto porque só códigos dentro da thread que
criou o controle visual podem interagir com os mesmos. Porém, com o
BackgroundWorker isto é diferente, basta escrever um método para o evento
ProgressChanged, que deve ter a seguinte assinatura:

private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)

Mais uma vez, o primeiro parâmetro controla o remetente da chamada do método e o


segundo, contém dois argumentos para serem usados:

1. ProgressPercentage: armazena um número inteiro com o percentual do trabalho


concluído.

2. UserState: instância do tipo object que pode armazenar qualquer informação que seja
importante durante a comunicação do andamento.

Por fim, quando a rotina acoplada com o BackgroundWorker termina, é feita uma
chamada ao evento RunWorkerCompleted que precisa ser manipulado por um método
como o do modelo:

private void worker_RunWorkerCompleted(object sender,


RunWorkerCompletedEventArgs e)

Como nos anteriores, sender identifica qual objeto fez a chamada à rotina e o segundo
parâmetro, passa dois argumentos, Result e UserState, ambos do tipo objeto. O primeiro
elemento pode ser armazenado durante o evento DoWork para armazenamento dos
resultados da operação.

A classe System.ComponentModel.BackgroundWorker vai ser muito usada na


aplicação de exemplo que foi criada para demonstrar rotinas assíncronas neste artigo.

Aplicação de exemplo
O objetivo é criar uma aplicação Windows que percorra as subpastas de uma pasta ou
drive informado e armazene os dados dos arquivos que forem encontrados em um banco
de dados SQL Server. A janela principal (Figura 1) deve disponibilizar o campo para o
usuário informar a pasta inicial. Para a seleção ficar mais exata, um botão deve ser
usado para mostrar uma caixa de diálogo para selecionar a pasta inicial. A janela de
busca está exibida na Figura 2.
Figura 1. Tela inicial da aplicação

Após a seleção da pasta, o seu nome é preenchido no campo de texto (que na interface
vai estar como somente leitura).

Figura 2. Caixa de diálogo para escolher pasta inicial

Para iniciar o trabalho, o usuário deve clicar no botão executar. O progresso do


andamento é feito em um painel que mostra o andamento das diversas tarefas. Um
detalhe importante é que após iniciar uma rotina, o usuário pode rodar outra
imediatamente, para outra pasta e assim, ter várias rotinas rodando simultaneamente
como demonstrado na "
Programação Paralela
no .NET Framework
Facebook Twitter
(9) (0)

Neste artigo entenderemos como funciona a programação paralela e


assíncrona no .NET Framework, aplicando técnicas para otimizar o
desempenho e responsividade das aplicações.

Fique por dentro


O desenvolvimento de aplicações mais robustas, responsivas e que realizam diversos cálculos
por segundo tem se tornado corriqueiro principalmente devido à evolução e o barateamento
dos processadores, que passaram a possuir vários núcleos de processamento.

Cada núcleo de processamento é também conhecido como core que na verdade é um


processador à parte, podendo haver dois, quatro, seis e até oito dessas cores em um
único chip de CPU. Este avanço incentivou o desenvolvimento da tecnologia
multithread que torna o fluxo de execução de uma aplicação mais eficiente e dinâmico.

O objetivo deste artigo é apresentar o ambiente multithreading, suas qualidades, os


problemas que podem advir através do seu uso e as ferramentas que o .NET fornece,
juntamente com a linguagem C#, para a criação de aplicações que possam proporcionar
uma nova experiência ao usuário.

Para que você possa entender o que é multithread, imagine uma montadora de
automóveis. Agora imagine que nesta montadora haja somente um funcionário que seja
responsável por planejar os custos, projetar o automóvel, organizar as peças, montar as
peças, pintar o automóvel, instalar o motor, realizar a bateria de testes do automóvel e
enviar o automóvel para a concessionária.

Quantos automóveis você acha que seriam produzidos por dia? Melhor, por ano? Com
certeza não seriam muitos. Através desse cenário de produção surreal é que pode se
fazer uma analogia com os processos de software que todos os dias são processados
pela CPU.
Mas o que são processos e como são formados? Para que você possa entender melhor
como funciona as threads, será importante abordar alguns conceitos básicos de
processos e threads.

Processos e threads
Processo é um dos conceitos mais importantes de qualquer sistema operacional, pois
representam programas em execução. Eles possuem um código executável, pilha de
execução, estado de processo e prioridade do processo que é definida pelo sistema
operacional.

Possuem também um valor no registrador Program Counter (PC) e um valor do


apontador de pilha Stack Pointer (SP). Com estes recursos, o sistema operacional
realiza operações concorrentes entre os processos realizando uma espécie de
pseudoparalelismo, onde alterna entre os processos ativos, executando partes de cada
um em um intervalo de tempo definido pelas prioridades de cada processo, sendo esta
troca denominada troca de contexto ou context switching.

O sistema operacional não se perde porque possui endereços de cada intervalo de


execução e dos próximos intervalos que serão executados, armazenados pelo registrador
da CPU Program Counter. Os processos também podem ser classificados de acordo
com o tipo de tarefa que realizam:

· Processos CPU-bound: Processos CPU-bound passam a maior parte do tempo


utilizando recursos do processador para a execução de suas tarefas. Aplicações que
realizam muitos cálculos por segundo como jogos ou softwares de informação
geográfica precisam de maior poder de processamento e consequentemente possuem
maiores prioridades de processamento;

· Processos I/O-bound: Processos I/O-bound não precisam de tanta atenção do


processador como os processos CPU-bound, porque utilizam mais o disco em suas
tarefas de gravação e leitura de dados.

Geralmente estes processos são bem mais lentos que os processos CPU-bound porque
não dependem exclusivamente da CPU, tendo seu gargalo de desempenho nos discos. A
tendência é que quanto maior for a velocidade do processador, mais gargalos de
processos I/O irão surgir, pois o processador precisará aguardar o disco terminar a maior
parte do trabalho para que o processo continue.

Essa diferença de desempenho é mostrada na Figura 1.


Figura 1. Períodos de uso da CPU alternando entre períodos de espera por operações de I/O.
a) Processo CPU-bound b) Processo I/O-bound

As threads são bem parecidas com os processos, mas possuem entre elas diferenças
fundamentais. Cada processo serial (que não possui paralelismo) possui seu próprio
espaço de endereçamento definido pelo S.O, tendo somente uma thread principal.

Porém há ocasiões em que um processo é custoso e encarregar somente uma thread de


todo o trabalho acarreta em percas significantes de desempenho e eficiência do
processador, portanto com a criação de múltiplas threads no mesmo processo
(possuindo o mesmo espaço de endereçamento) é possível distribuir responsabilidades e
partes de processamento do processo a cada thread e a mesma irá executar em paralelo
sua parte.

A Figura 2 ilustra as diferenças entre aplicações seriais e multithread.

Figura 2. Diferenças entre aplicações seriais (uma thread) e aplicações multithread.


Ao final todo o trabalho realizado é reunido por cada thread e entregue como um só
processo, sendo este procedimento chamado de fork-join, como mostrado na Figura 3.

Figura 3. Representação gráfica do processo fork-join.

Se voltarmos ao primeiro exemplo dado de um cenário de produção, fica evidente que


seria necessária mais mão de obra em uma linha de produção para que mais carros
fossem produzidos e entregues. Se transformarmos este cenário de produção em um
processo de software, podemos afirmar que precisaríamos de mais threads para que o
processo fosse feito mais rapidamente, por exemplo cinco pessoas pintariam os carros e
10 soldariam as peças, criando um ambiente multithread (fork). Ao final um resultado
único seria entregue que é o automóvel pronto em si (join).

Multithreading em .NET
A partir da versão 4.0 do .NET, novos recursos de programação paralela
e multithreading como a TPL (Task Parallel Library) representada principalmente pela
classe Task e as cláusulas async e await (inseridas na versão 4.5), tornaram a
programação paralela bem mais fácil e rápida. Neste artigo serão abordadas as
principais classes de programação em threads e suas funcionalidades.

Serão mostradas as classes Thread, ThreadPool e Task para programação em threads.


Também serão tratadas as funcionalidades PLINQ (Parallel Language Query), as
cláusulas async e await para responsividade de aplicações.

Por último alguns recursos avançados de programação paralela devem ser abordados
também, como semáforos, mutexes, monitors e coleções concorrentes.

Primeiro exemplo – A classe Thread


Para o nosso primeiro exemplo utilizando a linguagem C#, será mostrado um código
que realiza uma tarefa entre três threads.

A thread main é a thread onde é executado o método de entrada Main, padrão na


maioria das linguagens descendentes do C. Após são criadas duas threads; a primeira
thread ficará responsável por apenas realizar exibir a mensagem: “Thread rodando”,
onde será mostrado o seu identificador, porém esta mensagem será exibida em um loop
infinito.

Para cancelá-la, uma segunda thread ficará responsável por interromper sua execução se
o usuário digitar ‘C’. O código desse exemplo pode ser visto na Listagem 1.

Listagem 1. Primeiro exemplo utilizando a classe Thread

01 bool stop = false;

02

03 Thread thread = new Thread(() =>

04 {

05 while (!stop)

06 {

07 Console.WriteLine("Thread {0} rodando...",


Thread.CurrentThread.ManagedThreadId);

08 Thread.Sleep(500);

09 }

10 Console.WriteLine("Thread {0} cancelada.",


Thread.CurrentThread.ManagedThreadId);

11 });

12

13 var thread2 = new Thread((id) =>

14 {

15 var key = Console.ReadKey(true);

16 if (key.Key == ConsoleKey.C)
17 {

18 stop = true;

19 Console.WriteLine("Thread {0} cancelou a execucao


da thread {1}",

20 Thread.CurrentThread.ManagedThreadId, (int) id);

21 }

22 });

23

24 thread.Start();

25 thread2.Start(thread.ManagedThreadId);

26

27 for (int i = 0; i< 100; i++)

28 {

29 Console.WriteLine("Main thread rodando...");

30 Thread.Sleep(500);

31 }

32

33 thread.Join();

34 thread2.Join();

Na linha 1 é declarada uma variável booleana stop que ficará responsável por terminar a
execução do código executado pela thread1. A thread1 é declarada nas linhas 3 a 11. A
classe Thread recebe como parâmetro do construtor, um delegate do tipo ThreadStart.
O delegate ThreadStart é uma referência de um método que não recebe valores como
parâmetro e retorna um tipo void. A notação lambda é utilizada para escrever um
método anônimo, que é representada pelo símbolo => (goes to), que indica ao
compilador que o corpo do método está sendo iniciado.

Ao lado esquerdo são declarados os parâmetros do método anônimo e a direita é escrito


o código do método. O método anônimo é como se fosse um método comum, porém
não possui nome.

Na linha 5, o bloco while é controlado pela variável stop. Enquanto a mesma for falsa,
este código será executado.

Mas onde que o valor de stop é alterado? Nas linhas 13 a 22 é declarada uma segunda
thread, a thread2 que será responsável por cancelar a thread1.

Ela irá captar as teclas digitadas pelo usuário e irá testar se é a tecla C (linhas 15 e 16).
Se for a tecla correta, a thread1 é cancelada, juntamente com a thread2, exibindo uma
mensagem que mostra os ID’s da thread cancelada e da thread que cancelou.

Nas linhas 24 e 25 são iniciadas as threads. A thread2 recebe como argumento do


método Start o ID da thread1 que será cancelada ao usuário pressionar C; este
argumento é recebido pelo parâmetro id declarado na lista de argumentos do método
anônimo da linha 13, que exibirá na mensagem o ID da thread cancelada.

Nas linhas 33 e 34 são invocados os métodos Join das respectivas threads, mas qual é a
sua finalidade? Lembra-se da Figura 3? É aqui que o fork das execuções das threads
são unidos à thread main. Se estas linhas fossem comentadas, havia a possibilidade de a
thread main não esperar o fim das execuções das outras threads, sendo que a aplicação
poderia terminar precocemente.

Aproveitando este código, pode-se mencionar algumas outras características do


ambiente multithreading:

· Cada thread criada neste exemplo recebe um identificador e uma prioridade. Tal
prioridade pode ser alterada pela propriedade Priority e o identificador recuperado pela
propriedade ManagedThreadId;

· A thread não é iniciada automaticamente, mas cabe ao desenvolvedor iniciar sua


execução;

· Ao ser criada, a thread é colocada em uma fila de execução de threads;

· Ao terminar o tempo de execução, o S.O. suspende a execução da thread, a coloca no


final da fila de execuções e executa a próxima thread da fila (context switching);

· Quando uma thread deve esperar alguma tarefa de I/O, também é realizado o " [...]
Serialização
Com muita frequência precisamos persistir os objetos das nossas aplicações

em arquivos ou enviá-los, por exemplo, através da internet para outras

aplicações. Nesses casos é preciso obter uma representação desses objetos

em um formato adequado, como XML, JSON ou binário. Para isso usamos a

técnica de serialização, que você poderá conhecer em maiores detalhes nos


links a seguir:

Serialização de
objetos em C#
Facebook Twitter
(1) (0)

O artigo mostra os primeiros passos, classes, métodos e práticas a serem


tomadas para realizar a serialização de objetos usando os recursos do
framework .NET e a linguagem C#.

Fique por dentro


A serialização permite que aplicações de diferentes plataformas possam trocar informações
sem que tenham qualquer biblioteca compartilhada, apenas usando protocolos e padrões de
formatação e armazenamento dos dados. O artigo mostra os primeiros passos, classes,
métodos e práticas a serem tomadas para realizar a serialização de objetos usando os recursos
do framework .NET e a linguagem C#.

Um problema muito comum enfrentado por quem precisa utilizar programas que
exportam ou importam informações a partir de arquivos gerados por outrem, é a
incompatibilidade entre versões. Às vezes, um documento de texto ou um arquivo de
imagem criado com uma versão de um software não pode ser aberto ou utilizado
corretamente por outra versão do mesmo software, ou ainda por outro software de
função equivalente.
Atualmente é grande o número de programas, formatos de arquivo e plataformas que
precisam ser interconectadas. Por exemplo, é bem provável que os dados que sejam
inseridos em uma aplicação desktop, sejam vistos posteriormente em uma aplicação na
web ou enviados para algum dispositivo móvel.

No início havia pouco problema, porque também o número de plataformas era reduzido.
Basicamente havia um ou dois sistemas operacionais e o formato usado era o texto puro,
seguido de um arquivo indicando o layout, indicando onde um campo terminava e outro
começava, basicamente indicando a coluna de início e o comprimento esperado dos
dados.

Também não era problema o idioma usado, pois no início da programação o inglês
imperava e coisas como acentos e caracteres especiais – que complicam a
interoperabilidade quando projetos rodam em idiomas diferentes – ainda não eram
suportadas.

Com a evolução, surgiram questões que precisaram ser resolvidas e a troca de dados
entre os programas foi o ponto primeiro a ser atacado. Ao ler isto, pode-se ter a
impressão de que a interoperabilidade é uma questão menor, porque os arquivos de
texto são suficientemente eficazes para armazenar dados e facilmente de serem
manipulados. Mas vejamos alguns dados:

1. Textos precisam ser representados de forma linear e contínua. Como fazer para
representar objetos complexos que possuem objetos aninhados e listas de outros
objetos?

2. Cada idioma tem o seu conjunto de caracteres específico. Considere a simples tarefa
de enviar um texto em português para um programa sendo executado no idioma inglês
(que como sabemos, não tem acentos nem os mesmos símbolos do nosso idioma).

3. Com o surgimento de linguagens orientadas a objeto, os dados passaram a ser


estruturados de uma forma mais rica e complexa, para que pudesse se representar o
mundo real através de analogias como modelos (classes) e objetos (instâncias).

Logo, para que uma aplicação possa usar os dados, é necessário saber como estão
estruturados, conhecendo não só os nomes usados, mas também os tipos de dados.

Uma solução abrangente


Não demorou e começaram a surgir formas de se representar estes dados de uma forma
eficaz e que pudesse ser adotada pelo maior número de aplicações: a serialização.

Em linhas gerais, ela consiste em descobrir a estrutura de um determinado objeto ou


dado e prepará-lo para ser armazenado ou transmitido pela rede de uma maneira
portável, que possa ser lida por outra aplicação que conheça o padrão. Neste processo
são transmitidos:

1. A estrutura dos dados – nomes de propriedades, seus tipos e hierarquia;


2. Os dados propriamente dito;

3. Como fazer para restaurar os dados.

Usando uma analogia, a serialização é o teletransporte do mundo do desenvolvimento


de software. Você coleta todas as informações sobre o dado a ser serializado, ordena
esses metadados (dados sobre dados) de alguma forma e transmite.

Na outra ponta, o programa que for ler, já conhecendo o protocolo e o funcionamento


deste processo, materializa o objeto usando esses metadados.

Assim, o fluxo de serialização pode ser simplificado da seguinte forma;

1. Descobrir a estrutura do objeto: quais os nomes dos seus elementos, objetos,


propriedades e quais os seus tipos;

2. Fazer o mesmo para cada objeto aninhado até que não haja mais nenhum a ser
descoberto;

3. Associar os valores aos objetos;

4. Transformar os dados para envio usando um formato pré-estabelecido.

Embora a colocação destes passos possa parecer simples, considere como fazer para
descobrir quais são as propriedades e elementos de um objeto, sendo que você não tem
acesso ao código do mesmo, apenas uma amostra de uma instância deste objeto. Isto é
mandatório para que a serialização possa ser largamente empregada.

Qualquer desenvolvedor pode serializar um objeto se conhecer sua estrutura, mas como
fazer sem acesso ao seu código?

Neste ponto entra uma técnica muito difundida entre as linguagens de programação de
várias plataformas chamada Reflection. Ela consiste em recursos para ler quais são as
propriedades, tipos, métodos e valores de um objeto a partir de sua instância. No
Framework .NET você irá encontrar este recurso no namespace classe
System.Reflection.

Nota

Usar Reflection é, por si só, uma tarefa longa e cheia de detalhes que não cabem neste
artigo, e por isso serão apenas citados. Para obter mais detalhes, verifique a
sessão Links deste artigo.

Formas de serializar
Basicamente, são utilizadas duas formas de serialização:
· Binária: os dados são transformados em uma sequência de bytes. É mais complexa
para enviar dados através da rede, porque pode implicar em problemas de segurança e
os dados podem ser barrados por antivírus e firewalls.

· Texto: os dados são estruturados usando fluxos de texto puro em uma determinada
codificação. É a forma preferível quando se quer trocar dados entre plataformas
diferentes e também quando estes precisam trafegar por uma rede, pois não representam
ameaças à segurança.

Sobre a serialização binária não há maiores considerações a serem feitas, pois como foi
colocado, trata-se apenas de fluxo de bytes. Já realizar serialização no formato texto
implica em escolher um jeito de estruturar as informações.

Aqui entra um formato já bem conhecido e utilizado há bastante tempo, o XML, e um


mais recente, mas que já tem um espaço importante na representação e transporte de
informações, o JSON.

Arquivos XML consistem de marcações ou tags, que podem ser aninhadas


hierarquicamente, e por ser um formato de texto, podem ser lidos por qualquer editor de
texto. A Figura 1 mostra um exemplo de arquivo XML aberto no navegador de
internet.

abrir imagem em nova janela

Figura 1. Exemplo de documento XML visualiza" [...]


Serialização de
objetos no formato
JSON
Facebook Twitter
(11) (0)

Neste artigo serão apresentadas algumas formas para se realizar a


serialização e deserialização de objetos em JSON (JavaScript Object Notation)
no .NET Framework, com exemplos na linguagem C#.

Artigo do tipo Exemplos Práticos

Serializando objetos em JSON


Neste artigo serão apresentadas algumas formas para se realizar a serialização e deserialização
de objetos em JSON (JavaScript Object Notation) no .NET Framework, com exemplos na
linguagem C#. Serão apresentadas as classes nativas DataContractJsonSerializer,
JavaScriptSerlializer e em seguida a biblioteca externa Json.NET, possibilitando a comparação
entre estas e facilitando a escolha daquela que melhor atende a cada necessidade.

Em que situação este tema é útil


O tema abordado neste artigo é útil no dia a dia de todo desenvolvedor que necessita
armazenar ou transferir dados entre aplicações utilizando um dos formatos que mais tem
ganhado espaço nos últimos anos, o JSON. Este conteúdo também é útil para aqueles
que desejam conhecer as opções oferecidas pelo .NET Framework para fazer a
serialização de objetos nesse formato de dados, de forma a estar pronto para escolher
entre elas e utilizá-las quando for necessário.

O rápido avanço dos softwares e o aumento da sua importância nos processos diários de
diversos setores da sociedade têm feito ocorrer grandes modificações no perfil das
aplicações em geral. Programas que antes se resumiam a editores de texto que mal
exibiam na tela o conteúdo digitado e o enviavam para uma impressora matricial, agora
precisam atender a exigências e seguir tendências que mudam quase que diariamente.
As aplicações atuais precisam estar aptas a trabalhar com grandes volumes de dados e,
além disso, importar e exportar informações de diversos tipos e fontes. Os sistemas
devem ser capazes de interagir uns com os outros e realizar a troca de informações, bem
como armazená-las de forma segura e adequada à finalidade a que se destinam. Porém,
não existe uma ferramenta única para desenvolvimento de software, pelo contrário, são
várias as plataformas e linguagens já existentes e que não param de surgir e se renovar.
Também não há uma regra universal que dite como as informações devem ser tratadas e
armazenadas, o que por muito tempo fez com que cada aplicação trabalhasse de maneira
totalmente individual e manipulasse seus dados de uma forma que dependia apenas dos
desenvolvedores.

Com o passar do tempo, a necessidade de que as informações geradas por um software


pudessem ser aproveitadas por outro fez com que novas estratégias fossem
desenvolvidas para atender as exigências do mercado. Os grandes sistemas
gerenciadores de bancos de dados têm muito em comum entre si, como a forma de
armazenar os dados em tabelas, então uma solução seria que todo software,
independente da linguagem utilizada em seu desenvolvimento, pudesse acessar todo
tipo de banco de dados. Hoje isso quase é possível, pois a maioria dos SGBDs
comerciais disponibilizam provedores para serem acessados a partir das principais
linguagens de programação. Mas é fácil perceber que essa alternativa não é tão viável,
pois cada aplicação possui seu banco de dados, com um modelo totalmente planejado
para atender às necessidades que motivaram sua criação. Logo, não é possível (ou
viável) que um sistema, para obter informações provenientes de outro, precise acessar
diretamente a base de dados principal deste segundo.

Para resolver este conflito, formatos de representação de dados foram propostos e


passaram a ser utilizados como padrões, seguidos por todos aqueles que os desejassem
aderir a eles. Dentre estas soluções, destaca-se a XML (Extensible Markup Language,
ou Linguagem de Marcação Estendida), uma linguagem reconhecida pela W3C (BOX
1) que tem como objetivo representar e organizar dados de forma hierárquica. Essa
linguagem se difundiu rapidamente e passou a ser usada como principal forma de
intercâmbio de informações entre aplicações, principalmente através da internet. Com
sua popularização, as diversas linguagens de programação passaram a suportar
diretamente a manipulação de dados nesse formato, o que permitiu que um arquivo
XML gerado por uma aplicação pudesse ser facilmente lido por outra, desenvolvida em
plataforma diferente, pois o que prevalecia eram as regras de estruturação dos dados
definidos pelo padrão XML.

A XML pode ser utilizada para representar desde dados simples, como textos e valores
numéricos, até coleções de objetos complexos, com várias propriedades. Na Listagem
1 vemos um exemplo de um objeto Venda, com suas propriedades e itens, representado
em formato XML.

Listagem 1. Objeto complexo representado em formato XML

01 <?xml version="1.0" encoding="utf-8"?>

02 <Venda>
03 <Data>24/07/2013</Data>

04 <Valor>100,00</Valor>

05 <Cupom>102030</Cupom>

06 <Itens>

07 <Item>

08 <Codigo>123</Codigo>

09 <Quantidade>1</Quantidade>

10 <Subtotal>30,00</Subtotal>

11 </Item>

12 <Item>

13 <Codigo>456</Codigo>

14 <Quantidade>2</Quantidade>

15 <Subtotal>70,00</Subtotal>

16 </Item>

17 </Itens>

18 </Venda>

Notamos que os dados são organizados de forma hierárquica e utilizando os sinais < e
>. Maiores detalhes sobre a linguagem XML fogem do objetivo deste artigo, mas é
importante conhecer, ainda que de forma básica, o formato que atualmente é o mais
utilizado para a troca de informações entre aplicações. Tendo esse entendimento, será
mais fácil compreender o formato concorrente e que é foco deste artigo, o JSON.

BOX 1. W3C
O W3C (World Wide Web Consortium) é uma comunidade fundada por Tim Berners-
Lee, o criador da Web, e formada por diversos membros como universidades e
empresas privadas, que cooperam para o desenvolvimento de padrões utilizados na web,
como as linguagens HTML e CSS.

O formato JSON
JSON (em inglês JavaScript Object Notation) é um formato para troca de dados baseado
na sintaxe de representação de objetos da linguagem de script JavaScript. Apesar do
nome e de ser derivado dessa linguagem, o uso do formato JSON não requer
necessariamente a linguagem JavaScript, pelo contrário, a maioria das linguagens de
programação atuais oferecem meios para se trabalhar com esse formato.

A sintaxe JSON é bastante simples e muito conhecida por programadores que utilizam
linguagens cuja sintaxe deriva de C, como C++, C# e Java. Isso faz com que este
formato seja de fácil leitura por humanos e de fácil interpretação pelas máquinas,
tornando-o bastante adequado a operações de troca de dados entre aplicações,
principalmente na internet. Por este motivo, JSON se apresenta como um concorrente
da linguagem XML, com maior expressividade no meio web, devido à facilidade de
manipulação através de JavaScript e melhor desempenho.

"

Reflection
Reflection é uma técnica que tem por objetivo obter informações sobre objetos,

classes e assemblies em tempo de execução. Nos artigos abaixo você poderá

aprender a trabalhar com reflexão em C# e algumas aplicações práticas dessa


técnica:

Trabalhando com
Reflection em C#
Facebook Twitter
(5) (0)
Veja neste artigo como utilizar a técnica reflection para ler os metadados de um
objeto e manipulá-lo na linguagem C#.

Figura 1: Trabalhando com Reflection em C#

A reflexão é algo interessante que o .Net fornece, com ela podemos escrever código o
qual lê as informações do metadado dos objeto em tempo de execução. Essas
informações são toda a estrutura existente na classe, portanto métodos, propriedades e
até mesmo atributos de classes e métodos são visualizadas.

Para iniciar a captura de todas as informações da classe, devemos utilizar o


namespace System.Reflection. Para visualizar as informações devemos primeiro
capturar o Type do objeto, ele é responsável por encapsular todas as informações
citadas e a fornece a possibilidade de manipula-las.

Vamos a um caso simples de reflexão de um objeto.

Listagem 1: Reflexão de ValeuType

static void Main(string[] args)

int inteiro = 10;

string texto = "DevMedia";

float Flutuante = 10.2f;


System.Type Tipo = null;

Tipo = inteiro.GetType();

Console.WriteLine(Tipo.Name);

Console.WriteLine(texto.GetType().Name);

Console.WriteLine(Flutuante.GetType().Name);

Console.Read();

O resultado é:

Figura 2: Resultado da Reflexão

Note que retornou os tipos e não o nome ou valores da propriedade.

Usar reflexão não é só uma tarefa para descobrir tipos, existe a possibilidade de refletir
métodos encapsulados em uma DLL e utilizar os métodos e propriedades em tempo de
execução.

Antes de efetuar o load de uma DLL, a reflexão permite também a geração de novas
instâncias de um tipo, utilizando a classe Activator. Isso é só um exemplo das muitas
possibilidades do reflection.

Vamos refletir uma classe Humano para que possamos exemplificar todos os principais
pontos da Reflection.

Listagem 2: Classe Humano


public class Humano

private string TipoSanguineo { get; set; }

public int Idade { get; set; }

public int Altura { get; set; }

public Double Peso { get; set; }

public string Nome { get; set; }

public void Piscar()

Console.WriteLine("Piscar os olhos agora.");

public void Respirar()

Console.WriteLine("Repirar 1...2...3");

public void PensarAlgo(string pensamentos, DateTime quando)

Console.WriteLine("Estou pensando em : " + pensamentos + "


pensei nisso agora : " + quando.ToShortTimeString());

}
public void SentirFome()

Console.WriteLine("Estou ficando com fome. Hora do


Lanche.");

private void CantarNoBanheiro()

Console.WriteLine("Bla ... Bla ... Bla ...");

Para conhecer o assembly da classe Humano:

Listagem 3: Carregar o Assembly

var Assembly = typeof (Humano).Assembly;

Veja que o seguinte valor irá aparecer no objeto:

Terra, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

Onde Terra é o nome do namespace que comporta a classe Humano, a versão 1.0 é a
versão do assembly no momento da compilação.

O próximo passo é capturar o Type da classe, para ter acesso a todos os atributos
relacionados a ela e poder manipulá-los.

Listagem 4: Capturar o Type da classe

Type humanoType = typeof(Humano);

Como é o do nosso conhecimento, a classe Humano pertence ao namespace Terra e


fomos criteriosos ao escolher a classe Humano. Isto deve ser afirmado, pois podemos
também definir uma classe especifica dentro de um namespace a ser refletida. Por
exemplo:

Listagem 5: Capturar uma classe dentro do namespace

var animaisType = Type.GetType("Terra.Animais");

Porém vamos seguir com o primeiro exemplo.

Após ter o Type armazenado, vamos navegar por entre os métodos e propriedades da
classe e capturar alguns valores da mesma.

Primeiramente vamos criar uma nova instância para que seja possível a manipulação
dos dados do objeto.

Listagem 6: Activator auxiliando na geração de novas instâncias

object newHuman = Activator.CreateInstance(humanoType);

Esta linha equivale ao operador New.

Agora caso não queira trabalhar com novas instâncias de objetos e sim com um objeto
já instanciado e passado para o método de reflexão, simplesmente informe o mesmo no
método, assim o GetValue e o SetValue serão realizados dentro de um objeto do
escopo.

Para capturar todas as propriedades públicas existentes utilizamos o


método GetProperties.

Listagem 7: Usando o método GetProperties

PropertyInfo[] properties = humanoType.GetProperties();

foreach (var propertyInfo in properties)

Console.WriteLine(propertyInfo.Name);

Console.Read();
Figura 3: Propriedades públicas da classe

Para capturar uma propriedade pública existente na classe, vamos utilizar o


método GetProperty.

Listagem 8: Capturando uma propriedade com GetProperty

PropertyInfo property = humanoType.GetProperty("Idade");

Console.WriteLine(property.GetValue(newHuman, null));

Console.Read();

Sabemos que o resultado é zero, pois estamos capturando valores de uma nova
instância. Então para informar valores para o objeto utilizamos o SetValue.

Listagem 9: Atribuindo valores usando o SetValue

PropertyInfo propertySet =
humanoType.GetProperty("Idade");

propertySet.SetValue(newHuman, 23, null);

Console.WriteLine(propertySet.GetValue(newHuman, null));

Console.Read();

Pronto, nosso Humano agora tem 23 anos de idade. Repare que devemos informar em
qual objeto desejamos informar o valor, porém precisamos capturar a propriedade em
que queremos que isso aconteça. Adendo, como tudo isso ocorre em tempo de
execução, se preocupe em cada letra digitada, verifique se os tipos de valores são
corretos, pois toda a exceção só será dada em tempo de execução.

A classe Humano possui diversos métodos que são essenciais para ela, como respirar,
portanto vamos invocar todas as necessidades existente.
Listagem 10: Invocar um método público

humanoType.InvokeMember("Respirar", BindingFlags.InvokeMethod |
BindingFlags.Public | BindingFlags.Instance, null, newHuman, null);

humanoType.InvokeMember("Piscar", BindingFlags.InvokeMethod |
BindingFlags.Public | BindingFlags.Instance, null, newHuman, null);

humanoType.InvokeMember("SentirFome", BindingFlags.InvokeMethod |
BindingFlags.Public | BindingFlags.Instance, null, newHuman, null);

O resultado é:

Figura 4: Os métodos públicos executados

Assim como acontece com as propriedades, métodos privados não são visualizados de
imediato pela reflexão, entretanto é possível executá-los, utilizando
os BindingFlags apropriados para isso.

BindingFlags
É um enumerador com diversas opções que servem como parâmetros para o momento
da reflexão. Não é o foco do artigo, mas é importante conhecê-lo para usufruir melhor
da reflexão, o link de documentação do mesmo está em Referências.

Listagem 11: Acessando métodos privados

humanoType.InvokeMember("CantarNoBanheiro",
BindingFlags.InvokeMethod | BindingFlags.Instance |
BindingFlags.NonPublic, null,
newHuman, null);

Repare que foi utilizado o BindingFlags.NonPublic.


Vale ressaltar que é possível aceder métodos que possuem parâmetros de entrada, como
é o caso do PensarAlgo, veja:

Listagem 12: Invocar o pensamento

humanoType.InvokeMember("PensarAlgo", BindingFlags.InvokeMethod |
BindingFlags.Instance | BindingFlags.Public, null, newHuman, new
object[] { "em viajar.", DateTime.Now });

Conclusão
A reflexão de modo bem cru é tudo o que fazemos, criamos instâncias usando o New,
atribuímos valores pelo Set e recuperamos pelo Get. Chamamos os métodos apenas
encontrando o nome, porém não acessamos de fora seus métodos privados.

Porém a reflexão não para por aqui, isso é só o começo do entendimento.

Podemos gerar, por exemplo, métodos com reflexão para realizar conversão de tipos, se
tornando um método reutilizável. Assim como, a reflexão é utilizada para trabalhar com
classes que possuem atributos, como o [Serializable] e com isso o poder na
automatização aumenta.

Trabalhando com
atributos de classe e
Reflection em C#
Facebook Twitter
(2) (1)

Veja neste artigo como trabalhar com atributos de classe em C#, criando
atributos customizados e acessando-os através da técnica Reflection.
Figura 1: Atributos com reflection

Para aqueles que ainda não sabem ou até sabem, mas não tinham se dado conta,
começamos este artigo com exemplos de atributos de classe.

Listagem 1: Um Atributo de Classe

[Obsolete]

public void Metodo();

[Serializable]

public class Classe();

[WebMethod]

public static void CallAjax();

Esses nomes entre chaves acima de cada nome de método ou declaração de classe são
os atributos e servem para identificar e classificar classes e seus campos segundo alguns
critérios, atribuindo-lhes propriedades específicas para utilização no contexto em que
são utilizadas.

Por exemplo, o atributo Obsolete permite definir uma mensagem indicando que o
método está obsoleto e indicando qual utilizar em seu lugar.

Então, assumindo que possuímos o conhecimento de reflexão, vamos partir para o


entendimento de como criar atributos de classes.
Listagem 2: Criando um atributo simples

[AttributeUsage(System.AttributeTargets.Method)]

public class MethodAttribute : Attribute

private int _Variavel1 = 0;

public string _Variavel2 = "";

public MethodAttribute()

public MethodAttribute(string metodo, string metodo1)

public void Metodo()

public void Metodo(string entrada)

}
AttributeUsage()
Responsável pelas regras de como o atributo irá funcionar. Nele indicamos que o
atributo servirá de assinaturas, para classes, métodos ou algumas outras opções
encontradas na listagem 3.

Listagem 3: AttributeTargets Enum

public enum AttributeTargets

Assembly ,

Module,

ClasS,

Struct,

Enum,

Constructor,

Method,

Property,

Field,

Event,

Interface,

Parameter,

Delegate,

ReturnValue,
GenericParameter,

All = GenericParameter | ReturnValue | Delegate | Parameter |


Interface | Event | Field | Property | Method | Constructor | Enum |
Struct | Class | Module | Assembly

Cada opção dessas habilita uma possibilidade diferente para o atributo gerado. Além
do AttributeTargets, existem outros dois parâmetros dentro de AttributeUsage, são
eles:

 AllowMultiple: Habilita que o atributo seja assinado mais de uma vez, dentro do
escopo definido para o mesmo, o valor padrão é false.
 Inherited: Habilita que as classes derivadas herdem o atributo também, o valor padrão
é true.

Muito rápido e simples, porém terá um grande ganho de automatização quando tudo
estiver programado.

A Estrutura
A estrutura proposta para esse sistema é feita em 3 camadas e uma camada de reflexão.
A ideia da estrutura não está no foco.
Figura 2: Estrutura da aplicação

Repare que o Reflection é o ultimo passo, pois ele será o responsável (neste caso) por
efetuar o envio das informações, por isso foi dito que a estrutura não é o foco, pois a
DAL e até mesmo a Bussines, neste caso, simplesmente passam as informação para a
próxima camada, não realizam tarefas.

Codificando os Atributos
Os atributos é a parte mais importante daqui e fácil de desenvolver. Os atributos são
basicamente classes com construtores (não obrigatoriamente) e propriedades.

Existem atributos mais complexos que realizam tarefas de classes robustas, mas na
grande maioria os atributos serão simples.

Toda classe de atributo herda de Attribute e a mesma é assinada


com AttributeUsage com as definições que foram explicadas acima.

Para este exemplo, foram criados dois atributos, um se


chama StoredProcedureAttributes e o outro FieldsAttributes.

Listagem 4: Codificando os atributos

public enum ProcedureTypeCommand

Insert,

Delete,

Update

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false,


Inherited = false)]

public class StoredProcedureAttributes : Attribute


{

public string ProcedureName { get; set; }

public ProcedureTypeCommand ProcedureType { get; set; }

public String[] ProcParams { get; set; }

public StoredProcedureAttributes(string procName,


ProcedureTypeCommand procType, params String[] param)

ProcedureName = procName;

ProcedureType = procType;

ProcParams = param;

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false,


Inherited = false)]

public class FieldsAttributes : Attribute

public string FieldName { get; set; }

public SqlDbType FieldType { get; set; }

public int Length { get; set; }


public object Value { get; set; }

public FieldsAttributes()

public FieldsAttributes(string FieldName, SqlDbType FieldType)

this.FieldName = FieldName;

this.FieldType = FieldType;

public FieldsAttributes(string FieldName, SqlDbType FieldType,


int Length)

this.FieldName = FieldName;

this.FieldType = FieldType;

this.Length = Length;

Simples assim foram definidos dois atributos, um é assinatura de método e o outro


como assinatura de propriedades. Caso tente colocar um atributo definido para
propriedade em uma classe, por exemplo, o interpretador e o compilador Visual Studio
irá exibir um erro.
O atributo StoredProcedureAttributes, recebe os dados do Procedure do banco de
dados, o tipo de evento DML que ele realiza e os parâmetros existentes no procedure
(obrigatórios ou não).

O atributo FieldsAttributes recebe o nome do Parâmetro correspondente na entrada do


procedure, o tipo de dado que a coluna foi definida no banco de dados e o length (para
Varchar, Char e etc) para definir o tamanho da informação máxima que o parâmetro
pode conter.

Codificando as Camadas
As camadas não tem mistério, com exceção da camada de Entity. Nesta camada estão
todas as assinaturas dos atributos e é por ela que o Reflection consegue definir o tipo de
ação a ser realizada e os parâmetros passados.

Entity
A Entity é um espelho da tabela de dados e possui as regras de “DML” amarradas a ela.
Nesta camada estão as definições das assinaturas.

Listagem 5: Entidade e os atributos

public class Titulos

#region Propriedades

private int _id;

private string _nome;

private int _idCategoria;

private string _categoria;

private string _duracaoFilme;


[FieldsAttributes("@ID", System.Data.SqlDbType.Int)]

public int ID

get { return _id; }

set { _id = value; }

[FieldsAttributes("@NOME", System.Data.SqlDbType.VarChar, 30)]

public string Nome

get { return _nome; }

set { _nome = value; }

[FieldsAttributes("@IDCATEGORIA", System.Data.SqlDbType.Int)]

public int IdCategoria

get { return _idCategoria; }

set { _idCategoria = value; }

}
[FieldsAttributes("@CATEGORIA", System.Data.SqlDbType.VarChar,
30)]

public string Categoria

get { return _categoria; }

set { _categoria = value; }

[FieldsAttributes("@DURACAOFILME",
System.Data.SqlDbType.VarChar, 20)]

public string DuracaoFilme

get { return _duracaoFilme; }

set { _duracaoFilme = value; }

#endregion

private TitulosBLL titulosBLL;

public Titulos()

titulosBLL = new TitulosBLL();

}
#region Metodos Assinados

[StoredProcedureAttributes("STP_INS_TITULOS",
ProcedureTypeCommand.Insert, "@NOME", "@IDCATEGORIA", "@CATEGORIA",
"@DURACAOFILME")]

public void Salvar()

titulosBLL.Salvar(this);

[StoredProcedureAttributes("STP_DEL_TITULOS",
ProcedureTypeCommand.Delete, "@ID")]

public void Deletar()

titulosBLL.Deletar(this);

#endregion

Veja como ficaram os atributos criados, em cima das propriedades apenas o atributo
para propriedade e o mesmo para os métodos. Toda a declaração que seria feita na
camada de DAL foi transportado para a entidade, isso é um exemplo para a
funcionalidade do Reflection.

Observação: Muitos devotos por patterns e outros, com certeza não aprovariam um
modelo desses.

Camada de Reflection
A última camada do escopo realiza a reflexão do objeto passado pela entidade e captura
as demais informações para realizar a ação em questão (salvar, deletar).

Primeiramente vamos entender alguns métodos de reflexão dessa classe.

O GetFieldsAttributes é um método responsável por capturar do objeto passado para a


camada todas as propriedades as quais levam a assinatura definida anteriormente.

Listagem 6: Código do GetFieldsAttributes

private void GetFieldsAttributes(System.Type type, object obj)

ListFields.Clear();

foreach (var propertyInfo in type.GetProperties())

foreach (Attributes.FieldsAttributes customAttribute


in
propertyInfo.GetCustomAttributes(typeof(Attributes.FieldsAttributes),
false))

customAttribute.Value = propertyInfo.GetValue(obj,
null);

ListFields.Add(customAttribute);

O GetStoredProcedureAttributes, por sua vez, é o método responsável por capturar


do objeto passado para a camada todas as propriedades as quais levam a assinatura
de StoredProcedureAttributes.
Listagem 7: GetStoredProcedureAttributes

private void GetStoredProcedureAttributes(Type type)

listProcMethods.Clear();

foreach (var methodInfo in type.GetMethods())

foreach (Attributes.StoredProcedureAttributes
storedProcedureAttributes in
methodInfo.GetCustomAttributes(typeof(Attributes.StoredProcedureAttrib
utes), false))

listProcMethods.Add(storedProcedureAttributes);

Eles juntam as informações em listas distintas definidas na classe como static. Essas
listas de informações são necessárias para a filtragem dos parâmetros corretos na hora
do envio das informações.

O evento Salvar recebe uma variável genérica para permitir receber qualquer objeto
dentro do escopo definido.

Sem mais delongas, segue:

Listagem 8: Método Salvar

public void Salvar<T>(T obj)

{
var type = typeof(T);

GetFieldsAttributes(type, obj);

GetStoredProcedureAttributes(type);

Attributes.StoredProcedureAttributes StoredProcedure =
listProcMethods.First(p => p.ProcedureType ==
Attributes.ProcedureTypeCommand.Insert);

Salvar(StoredProcedure);

private void Salvar(Attributes.StoredProcedureAttributes


storedProcSalvar)

SqlCommand cmd = new SqlCommand();

try

cmd.CommandType =
System.Data.CommandType.StoredProcedure;

cmd.CommandText = storedProcSalvar.ProcedureName;

cmd.Connection = GetConnection();

cmd.Parameters.Clear();

foreach (string procParam in


storedProcSalvar.ProcParams)
{

var field = ListFields.First(whe => whe.FieldName


== procParam);

cmd.Parameters.Add(new
SqlParameter(field.FieldName, field.FieldType, field.Length) { Value =
field.Value });

cmd.ExecuteNonQuery();

catch (Exception ex)

throw ex;

finally

cmd.Connection.Close();

Observação: Na linha onde é feito o First para definir qual o procedure deve ser
utilizado, poderia ser feito de outra forma, utilizando atributos específicos para cada
evento.

Veja como é simples e reutilizável tudo isso. A reflection é capaz de ler os atributos da
classe e os valores das propriedades definidos no objeto, minerando toda essa
informação e facilitando na automatização do processo de envio.
Essa camada, nesse exemplo, entende os parâmetros que o procedure receberá e e
dentro do foreach é feita a separação do que é preciso para a chamada do procedure.

Conclusão
Os atributos são uma vantagem para as aplicações, com eles conseguimos automatizar
processos rotineiros, encapsular e reutilizar tarefas repetitivas para todos os
desenvolvedores de uma célula. Neste caso do CRUD facilitará em constantes acessos
ao banco de dados e em termos de produtividade gera um ganho, pois é possível
reutilizar os atributos e a classe de reflexão em qualquer projeto.

Portanto pesquisem mais ainda sobre o assunto. Baixem o código fonte para ver o
funcionamento!

Foi disponibilizado uma base dentro do App_Data, caso a mesma falhe, utilize o script
de banco de dados e stored procedures disponibilizado dentro do arquivo zip, não
se esqueça de editar o web.config para o endereço do banco corretamente.

Mesclando Objetos em
C# com Reflection
Facebook Twitter
(2) (0)

Veja neste artigo como “mesclar” dois objetos da mesma classe utilizando
Reflection no C#, gerando um terceiro objeto resultante da união dos dois
primeiros.

Vamos imaginar a seguinte situação: duas lojas de uma mesma rede que não se
comunicam entre si e, por este motivo, cadastraram o mesmo cliente, uma vez em cada
loja. Na Loja A, o cliente informou apenas o CPF, o Nome e o Telefone. Em outro dia,
o cliente precisou realizar uma compra na Loja B e lá informou apenas o CPF, o Nome
e o Email. Em certo momento, tornou-se necessário sincronizar os cadastros e então
surgiu a questão: que cadastro devemos considerar como “melhor”? O da Loja A ou o
da Loja B. Reparem que se consideramos apenas o cadastro da Loja A, perderemos a
informação do Email do cliente e, considerando apenas o da Loja B, perderemos a
informação do Telefone. Diante dessa situação, percebemos que o ideal seria “mesclar”
os dois cadastros, preenchendo as informações em branco de uma loja com as
informações existentes na outra, obtendo um cadastro único.

Existe ainda mais uma informação importante com relação ao cadastro. O cliente se
chama “Joel Rodrigues de Lima Neto”, porém, na Loja A, ele informou apenas “Joel
Rodrigues”. Já na Loja B, o cliente cadastrou seu nome completo, “Joel Rodrigues de
Lima Neto”. Nesse caso, o cadastro da Loja B tem prioridade sobre o cadastro da Loja
A, ou seja, no caso de haver a informação preenchida nas duas lojas, deve-se manter o
conteúdo da Loja B.

Bem, vejamos agora na prática como fazer essa “mescla” em C#, utilizando reflection.
Primeiramente, vejamos o código da classe Cliente:

Listagem 1: Classe Cliente

public class Cliente

private String _nome;

private String _telefone;

private String _cpf;

private String _email;

public String CPF

get { return _cpf; }

set { _cpf = value; }

}
public String Email

get { return _email; }

set { _email = value; }

public String Nome

get { return _nome; }

set { _nome = value; }

public String Telefone

get { return _telefone; }

set { _telefone = value; }

public override string ToString()

{
StringBuilder sb = new StringBuilder();

sb.AppendLine("Nome: " + this._nome);

sb.AppendLine("Telefone: " + this._telefone);

sb.AppendLine("CPF: " + this._cpf);

sb.AppendLine("Email: " + this._email);

return sb.ToString();

Sobrecarregamos o método ToString apenas paraa nível de verificação. Após mesclar os


dois objetos, chamaremos o método ToString para verificar os valores das propriedades
do novo objeto.

Agora vejamos o código do método MesclarObjetos:

Listagem 2: Método MesclarClientes

public object MesclarObjetos(object p1, object p2)

if(p1.GetType() != p2.GetType())

throw new Exception("Os parâmetros deve ser do mesmo tipo.");

Object p3 = Activator.CreateInstance(p1.GetType());
foreach (PropertyInfo p in p1.GetType().GetProperties())

object v1 = p1.GetType().GetProperty(p.Name).GetValue(p1, null);

object v2 = p2.GetType().GetProperty(p.Name).GetValue(p2, null);

object v3 = (v1 == null || v1.ToString() == "" || v1.ToString() ==


"0"? v2 : v1);

p3.GetType().GetProperty(p.Name).SetValue(p3, v3, null);

return p3;

Bastante simples, não é mesmo?

Primeiramente, verificamos se os dois parâmetros são do mesmo tipo. Caso isso não se
verifique, geramos uma exceção e encerramos o método.

Depois, criamos o objeto p3 do mesmo tipo do p1 (que é o mesmo do p2). Feito isso,
verificamos cada propriedade dos objetos p1 e p2 e preenchemos o p3. Para cada
iteração, v1 é o valor da propriedade de p1 e v2 é o valor da propriedade de p2. Caso v1
seja nulo, vazio ou zero, passamos para v3 (que é a propriedade de p3) o valor de v2,
caso contrário, passamos o valor de v1.

Vale observar que, com essa estrutura, o objeto que é passado como primeiro parâmetro
é priorizado na mescla dos valores conforme discutimos anteriormente.

Agora que já conhecemos como o método funciona, vamos testá-lo.

Listagem 3: Testando o método

Cliente c1 = new Cliente()

Nome = "Joel Rodrigues",


CPF = "000.000.000-00",

Telefone = "(000)0000-0000"

};

Cliente c2 = new Cliente()

Nome = "Joel Rodrigues de Lima Neto",

CPF = "000.000.000-00",

Email = "joelrlneto@gmail.com"

};

Cliente c3 = (Cliente)MesclarObjetos(c2, c1);


MessageBox.Show(c3.ToString());

O conteúdo do MessageBox deve ser o seguinte:

Listagem 4: Resultado da mescla dos objetos

Nome: Joel Rodrigues de Lima Neto

Telefone: (000)0000-0000

CPF: 000.000.000-00

Email: joelrlneto@gmail.com

Bem, como vimos, o método é bem simples e isso só é possível graças aos recursos do
Reflection do .NET.
Avançando na linguagem
Para lhe ajudar a seguir evoluindo no aprendizado do C#, indo além dos

recursos mais básicos e fundamentais, separamos aqui um curso que


apresenta conceitos avançados dessa linguagem:

Orientação a Objetos
Normalmente, quando começamos a programar, o primeiro paradigma que

conhecemos é o estruturado. Ele facilita o aprendizado por não trazer consigo

tantos conceitos, como os que fazem parte da Orientação a Objetos. Imagine

começar a criar algoritmos e, ao mesmo tempo, conciliar conceitos abstratos


para poder programar as primeiras soluções? Não seria fácil.

Portanto, é natural percorrer o caminho: Programação Estruturada ->

Programação Orientada a Objetos. O único porém desse processo é que

algumas vezes acabamos levando algumas características da primeira para a

segunda, características essas que podem prejudicar um pouco o nosso


código. Pensando nessa etapa de transição, preparamos o seguinte artigo:
Programação
Orientada a Objetos
versus Programação
Estruturada
Facebook Twitter
(20) (0)

Vamos abordar neste artigo qual tipo de programação deve ser utilizada no
decorrer do desenvolvimento: orientada a objetos ou programação estruturada.

Vamos abordar neste artigo os dois tipos de programação bem conhecidos e que ainda
geram algumas dúvidas em suas utilizações e definições, que é a programação
orientada a objetos e a programação estruturada.

Existem dois tipos de programação bem usuais: Orientada a Objetos (OO) e


Estruturada. Em cada uma delas iremos conhecer os seus principais conceitos e suas
respectivas utilizações e, por fim, mostraremos um breve comparativo entre esses dois
tipos e qual o melhor a ser utilizado quando um projeto de desenvolvimento de software
for iniciado. Utilizaremos como linguagem de programação base para os nossos
exemplos as linguagens Java e C#.

Quer aprender mais sobre Orientação a objetos em Java? Confira o curso de Java básico
orientado a objetos da DevMedia.

Programação Orientada a Objetos


A programação orientada a objetos é um modelo de programação onde diversas classes
possuem características que definem um objeto na vida real. Cada classe determina o
comportamento do objeto definido por métodos e seus estados possíveis definidos por
atributos. São exemplos de linguagens de programação orientadas a objetos: C++, Java,
C#, Object Pascal, entre outras. Este modelo foi criado com o intuito de aproximar o
mundo real do mundo virtual. Para dar suporte à definição de Objeto, foi criada uma
estrutura chamada Classe, que reúne objetos com características em comum, descreve
todos os serviços disponíveis por seus objetos e quais informações podem ser
armazenadas.

Vamos começar exemplificando uma classe carro utilizando a linguagem C#, como
mostra a Listagem 1.

Listagem 1. Classe Carro

class Carro {

string marca; // Define a marca do carro

string modelo; // Define o modelo do carro

int ano; // Define o ano de fabricação do carro

string categoria; // Define a categoria, por exemplo carro de


passeio, utilitário...

double potencia; // Define a potência do carro

double autonomia; // Define a autonomia em km

boolean ligado; // Define se o carro está ligado

public Carro() {

public Carro(string marca, string modelo, int ano, string


categoria, double potencia, double autonomia, boolean ligado) {

this.marca = marca;

this.modelo = modelo;
this.ano = ano;

this.categoria = categoria;

this.potencia = potencia;

this.autonomia = autonomia;

this.ligado = ligado;

A classe “Carro” contém um conjunto de características em comum que definem um


objeto do tipo carro: Marca, modelo, ano, categoria, potência e autonomia são atributos
que todos os carros precisam ter. Temos também dois construtores para a classe, ou
seja, os responsáveis por criar o objeto em memória, onde um é inicializa o objeto carro
e o outro, além da inicialização do objeto, define que o preenchimento dos parâmetros
seja obrigatório nos atributos da classe.

Agora vamos criar um objeto para que o mesmo possa ser utilizado, como mostra
a Listagem 2.

Listagem 2. Invocando o construtor e criando um objeto

class Program {

public static void main(String[] args) {

// Instanciando a classe carro a partir do construtor sem a


passagem de parâmetros

Carro meuCarro = new Carro();

// Atribuindo valores aos atributos que compõem a classe


carro

meuCarro.marca = “Fiat”;
meuCarro.modelo = “Palio”;

meuCarro.ano = 2013;

meuCarro.categoria = “Passeio”;

meuCarro.potencia = 86.7;

meuCarro.autonomia = 320.6;

meuCarro.ligado = false;

Instanciamos nossa classe Carro e atribuímos a uma variável “meuCarro” todos os


atributos da nossa classe. Poderíamos também invocar o construtor que já recebe os
parâmetros com os respectivos atributos da classe, como mostra a Listagem 3.

Listagem 3. Invocando o construtor com parâmetros e criando o objeto

class Program {

public static void main(String[] args) {

// Instanciando a classe carro a partir do construtor que


contêm os devidos parâmetros

Carro meuCarro = new Carro("Fiat", "Palio", 2013, "Passeio",


86.7, 420.5, false);

Na Listagem 4 criaremos alguns métodos, que é são comportamentos que a classe


Carro possui.

Listagem 4. Métodos da classe Carro


void Ligar() {

// Atribui true a propriedade ligado

this.ligado = true;

System.out.println("Carro ligado!");

boolean Anda(double autonomia) {

// Condição que verifica se o carro está ligado e se autonomia é


maior que 0,

// retornando verdadeiro caso a condição seja satisfeita, ou


falso caso contrário

if (this.ligado && autonomia > 0) {

return true;

} else {

return false;

void Desligar() {

// Atribui false a propriedade ligado

this.ligado = false;
System.out.println("Carro desligado!");

No método “Liga” atribuímos ao atributo “ligado” o valor verdadeiro e escrevemos no


console de saída uma informação que o carro foi ligado.

No método “Anda” temos uma condição onde se o carro estiver ligado e a autonomia do
carro for maior que zero, o carro ainda pode andar. Caso contrário, se a autonomia for
menor ou igual a zero ou então o carro esteja desligado, o mesmo fica impossibilitado
de andar.

E por último temos o método “Desliga”, que atribui o valor falso ao atributo “ligado” e
escrevemos no console de saída a mensagem que o carro foi desligado.

Veja que para utilizar esses métodos presentes na classe Carro precisamos utilizar dois
conceitos cruciais: Construtores e Destrutores. Construtores são métodos invocados no
momento em que o objeto é criado: por convenção é utilizado o mesmo nome da classe
como, por exemplo, para a classe Carro temos o construtor Carro(). Já os Destrutores
realizam a função inversa aos Construtores, onde são liberados da memória todos os
objetos que foram criados no momento da execução do programa, na maioria das
linguagens de programação esse método é executado automaticamente quando o objeto
é eliminado e, por convenção, é utilizado também o mesmo nome da classe, porém com
um “~” antecedendo o nome como, por exemplo, para a classe Carro temos o destrutor
~Carro().

Agora que já conhecemos um pouco sobre orientação a objetos, vamos conhecer seus
princípios básicos

Abstração
Este princípio é uma forma de abstrair o quão complexo é um método ou rotina de um
sistema, ou seja, o usuário não necessita saber todos os detalhes de como sua
implementação foi realizada, apenas para que serve determinada rotina e qual o
resultado esperado da mesma. Sendo assim, podemos também dividir internamente
problemas complexos em problemas menores, onde resolvemos cada um deles até
encontrarmos a solução do problema inteiro. Um exemplo da vida real para ilustrar esse
conceito seria o conceito de carro a abstração de um veículo, que é utilizado como meio
de transporte por várias pessoas para mover-se de um ponto a outro. Não é necessário
que a pessoa informe que irá se locomover com a ajuda de um veículo movido a
combustível, contendo rodas e motor. Basta a pessoa informar que utilizará um carro
para tal, pois esse objeto é conhecido por todos e abstrai toda essa informação por trás
disso.

Veja na Listagem 5 um exemplo de abstração utilizando a linguagem Java.


Listagem 5. Exemplo de abstração

public class Conta {

int cod_banco;

int num_conta;

double saldo;

double limite;

void ConsultarSaldo() {

System.out.println(“Conta: ” + this.num_conta);

System.out.println(“Saldo: ” + this.saldo);

void Depositar(double valor) {

this.saldo = this.saldo + valor;

void Sacar(double valor) {

this.saldo = this.saldo - valor;

}
Para este caso, um cliente só precisa entender que uma conta é um local, em um
determinado banco, onde é possível ser depositado e sacado valores.Para exemplificar
este caso, criamos uma classe Conta com os atributos: código do banco, número da
conta, saldo e limite. Criamos também um método “ConsultarSaldo”, onde ele retorna
qual o saldo da conta naquele momento. Criamos também outro método chamado
“Depositar” onde passamos um valor como parâmetro e ele soma esse ao saldo atual da
conta. Outro método chamado “Sacar” foi criado com um valor passado por parâmetro,
onde o mesmo subtrai esse valor do saldo atual da conta.

Encapsulamento
O princípio do encapsulamento é a forma pela qual o programa é divido a ponto de se
tornar o mais isolado possível, ou seja, cada método pode ser executado isoladamente e
retornar um resultado satisfatório ou não para cada situação. Sendo assim, o objeto não
necessita conhecer qual forma cada método foi implementado.

Vejamos na Listagem 6 um exemplo prático de encapsulamento, onde é possível obter


e atribuir valores a propriedades da classe Pessoa de forma independente, sem alterar o
funcionamento do restante da classe.

Listagem 6. Exemplo de encapsulamento

public class Pessoa {

private String nome; // Define o nome da pessoa

private String cpf; // Define o cpf da pessoa

private Date dat_nasc; // Define a data de nascimento da pessoa

// Obtem o nome da pessoa

public String getNome() {

return nome;

}
// Atribui o nome a pessoa

public void setNome(String nome) {

this.nome = nome;

// Obtem o cpf da pessoa

public String getCpf() {

return cpf;

// Atribui o cpf a pessoa

public void setCpf(String cpf) {

this.cpf = cpf;

// Obtem a data de nascimento da pessoa

public Date getDatNasc() {

return dat_nasc;

}
// Atribui a data de nascimento a pessoa

public void setDatNasc(Date dat_nasc) {

this.dat_nasc = dat_nasc;

Herança
Esse princípio diz que, uma classe pode compartilhar métodos e atributos entre si como,
por exemplo, em um sistema escolar, onde temos uma classe Pessoa que contém
características que definem uma pessoa. Porém, dentro do sistema temos uma outra
classe Funcionário, que contém os mesmos atributos de Pessoa, além de outros atributos
que apenas funcionários podem ter. Outro exemplo seria uma classe Aluno, que também
contém atributos de pessoas e outros atributos que apenas pertencem a aluno.

Vejamos na Listagem 7 como podemos implementar esse princípio utilizando a


linguagem de programação Java.

Listagem 7. Implementando o princípio da herança

public class Pessoa {

public String nome; // Define o nome da pessoa

public String cpf; // Define o cpf da pessoa

public Date data_nascimento; // Define a data de nascimento da


pessoa

// Construtor da classe Pessoa, passando todos os seus atributos

public Pessoa(String _nome, String _cpf, Date _data) {

this.nome = _nome;
this.cpf = _cpf;

this.data_nascimento = _data;

// Classe Aluno que herda da classe Pessoa

public class Aluno extends Pessoa {

// Herda os atributos da classe super

public Aluno(String _nome, String _cpf, Date _data) {

super(_nome, _cpf, _data);

public String matricula; // Define a matricula do Aluno

public class Professor extends Pessoa {

// Herda os atributos da classe super

public Professor(String _nome, String _cpf, Date _data) {

super(_nome, _cpf, _data);

public double salario; // Define o salário do professor


public String disciplina; // Define a disciplina do professor

public class Funcionario extends Pessoa {

// Herda os atributos da classe super

public Funcionario(String _nome, String _cpf, Date _data) {

super(_nome, _cpf, _data);

public double salario; // Define o salário do funcionário

public Date data_admissao; // Define a data de admissão do


funcionário

public String cargo; // Define o cargo do funcionário

Note que temos uma classe Pessoa que contém propriedades em comum com as classes
Aluno, Professor e Funcionário. Essas outras classes que herdam de Pessoa recebem a
palavra reservada “extends”, que indica que as mesmas contêm as propriedades nome,
cpf e data, presentes na classe Pessoa.

Polimorfismo
Polimorfismo é uma característica na qual os mesmos atributos ou métodos podem ser
utilizados por objetos distintos e com implementações de lógica diferentes. Por
exemplo, podemos dizer que um carro e uma bicicleta são veículos utilizados para
locomoção e ambos contêm um método “Andar” em comum, porém, a implementação
de cada um deles é feita de forma diferente.

Vamos exemplificar a implementação de uma classe Forma, onde a mesma contém um


método Desenhar, como mostra a Listagem 8.
Listagem 8. Classe Forma

public abstract class Forma

public Forma()

public virtual void Desenhar(System.Drawing.Graphics g)

Note que a classe Forma é uma classe abstrata e que o método desenhar não tem
implementação, pois cada figura tem uma forma diferente de ser desenhada. Porém, é
nas classes derivadas da classe Forma que esse método será implementado e, por isso,
essa nova classe será uma classe virtual, como mostra a Listagem 9.

Listagem 9. Classe Circulo

using System.Drawing;

public class Circulo : Forma

// Implementa o método desenhar baseado na forma Circulo

public override void Desenhar(System.Drawing.Graphics g)

{
base.Desenhar (g);

g.DrawEllipse(Pens.Red, 5, 5, 100, 100);

Veja agora que a classe Circulo herda da classe Forma, portanto o método Desenhar
deve ser implementado. Partindo desse princípio podemos ter diversas classe diferentes
que herdem da classe Forma e implementem de outra forma o método Desenhar, como
mostra a Listagem 10.

Listagem 10. Classe Quadrado

public class Quadrado : Forma

// Implementa o método desenhar baseado na forma Quadrado

public override void Desenhar(System.Drawing.Graphics g)

base.Desenhar (g);

g.DrawRectangle(Pens.Green, 5, 5, 100, 100);

Classes abstratas são classes que jamais serão instanciadas diretamente, pois não
possuem implementação suficiente para oferecer funcionalidades concretas a serem
utilizadas.

Programação Estruturada
O princípio básico da programação estruturada é que um programa pode ser divido
em três partes que se interligam: sequência, seleção e iteração.

Sequência
Na sequência são implementados os passos de processamento necessários para
descrever determinada funcionalidade. Um exemplo básico seria um fluxograma, onde
primeiro é executado a Etapa 1 e após a sua conclusão a Etapa 2 é executada, e assim
por diante.

Seleção
Na seleção o fluxo a ser percorrido depende de uma escolha. Existem duas formas
básicas para essa escolha.

 A primeira é através do condicional “Se”, onde se uma determinada condição for


satisfatória o fluxo a ser corrido é um e, caso contrário, o fluxo passa a ser outro. Ou
seja, se o fluxo só percorre apenas um caminho, apenas uma ação é processada.
 A outra forma de escolha é onde o número de condições se estende a serem avaliadas.
Por exemplo, se a Condição 1 for verdade faça Processamento 1, caso contrário, se a
Condição 2 for verdade faça Processamento 2, caso contrário, se a Condição 3 for
verdade faça Processamento 3, e assim por diante.

Iteração
Na iteração é permito a execução de instruções de forma repetida, onde ao fim de cada
execução a condição é reavaliada e enquanto seja verdadeira a execução de parte do
programa continua.

Modularização
A medida que o sistema vai tomando proporções maiores, é mais viável que o mesmo
comece a ser divido em partes menores, onde é possível simplificar uma parte do código
deixando a compreensão mais clara e simplificada. Essa técnica ficou conhecida como
Subprogramação ou Modularização. No desenvolvimento utilizamos essa técnica
através de procedimentos, funções, métodos, rotinas e uma série de outras estruturas.
Com essa divisão do programa em partes podemos extrair algumas vantagens, como:

 Cada divisão possui um código mais simplificado;


 Facilita o entendimento, pois as divisões passam a ser independentes;
 Códigos menores são mais fáceis de serem modificados;
 Desenvolvimento do sistema através de uma equipe de programadores;
 Reutilização de trechos de códigos.

Hoje as linguagens estruturadas, como Algol, Pascal e C, ainda são encontradas em


muito sistemas, apesar das linguagens Orientadas a objetos serem mais usuais.

Para exemplificar esse tipo de programação, vamos ver um exemplo em C na Listagem


11.

Listagem 11. Exemplo de Programação estruturada

# include <stdio.h>

int main()

int soma, n=1;

soma = 0; // inicialização da variável soma

for (n=1; n<=100; n++)

soma= soma + n; // atualização da variável soma

printf("O valor da soma = %d\n",soma);


return 0;

Portanto, como vimos no decorrer do artigo, a programação orientada a objetos define


uma programação voltada aos conceitos de classes e herança e, em contrapartida, a
programação estruturada define uma programação voltada a procedimentos e funções
definidas pelo usuário. Vejamos na Tabela 1 um breve comparativo entre programação
orientada a objetos e programação estruturada.

Programação orientada a objetos Programação estruturada

Métodos Procedimentos e funções

Instâncias de variáveis Variáveis

Mensagens Chamadas a procedimentos e funções

Classes Tipos de dados definidos pelo usuário

Herança Não disponível

Polimorfismo Não disponível

Tabela 1. Comparativo entre programação orientada a objetos e programação


estruturada.

Na Programação estruturada observamos algumas vantagens como um controle mais


eficaz quanto ao fluxo de execução do programa e a facilidade de compreender o código
quando o mesmo é analisado. Como principal desvantagem temos a facilidade em
desenvolver um código confuso caso o desenvolvedor faça o tratamento dos dados
juntamente com o restante da execução do programa, além do que o reuso de código se
torna um pouco complicado devido a não definição da tarefa.

Já na programação orientada a objetos temos como vantagens a reutilização de código e


a melhor organização do código do programa. Porém, alguns detalhes tornam essa
programação um pouco prejudicada quando comparada a programação estruturada
como, por exemplo, o desempenho do código e a confusão na hora de aplicar os
conceitos de orientação a objetos.

Portanto, não podemos assumir que um tipo de programação é mais vantajoso que o
outro, pois em ambos existem características bem peculiares nas suas definições,
deixando cada uma mais viável de ser implementada, dependendo do negócio escolhido,
para facilitar o bom entendimento e a manutenibilidade futura do código feito.

Manutenibilidade é uma característica inerente a um projeto de sistema ou produto, e


se refere à facilidade, precisão, segurança e economia na execução de ações de
manutenção nesse sistema ou produto.

Fundamentos básicos
da Orientação a
Objetos
Facebook Twitter
(7) (0)

Este artigo trata sobre os principais fundamentos da orientação a objetos em


C# explicando os conceitos de abstração, herança e polimorfismo.

De que se trata o artigo

Este artigo trata sobre os principais fundamentos da orientação a objetos em C#,


explicando os conceitos de abstração, herança, polimorfismo além de falar sobre a
implementação de alguns destes fundamentos em C#, assim como palavras reservadas
do mesmo que incidem sobre estes conceitos.

Em que situação o tema é útil

Os tópicos abordados neste artigo poderão ser úteis no dia a dia de todo desenvolvedor,
pois abordará

Fundamentos básicos de Orientação a Objetos

Neste artigo veremos os principais conceitos e fundamentos da orientação a objetos


aplicados no C#. Além disso, veremos o uso das palavras chaves virtual, override,
abstarct, sealed, partial e dos modificadores de acesso public, protected, internal,
protected internal e private.
Atualmente existem dois paradigmas principais de desenvolvimento de software na
mente dos desenvolvedores e no mercado. Temos o paradigma estruturado, que antes
dominava o mercado, e o paradigma da orientação a objetos (O.O), que cada vez mais
vai tomando conta do mercado.

Normalmente, profissionais mais antigos estão habituados com o paradigma estruturado


e os mais novos com o paradigma O.O, mas é claro que isso não é uma regra.

O fato é que a orientação a objetos vem tendo sua adoção numa crescente no mercado,
mas é fato também que na maioria das vezes ela é subutilizada. É muito comum vermos
equipes de desenvolvimento que desconhecem alguns fundamentos básicos da
orientação a objetos, que são os pilares da mesma.

A ideia deste artigo é apresentar alguns dos conceitos mais básicos e fundamentais da
orientação a objetos, com exemplos pontuais e aplicados ao C#. Vamos aproveitar
também para falar sobre algumas palavras reservadas do C# que estão de certa forma
relacionadas aos conceitos que vamos apresentar.

Para começar, vamos traçar um breve comparativo, sobre os paradigmas estruturados e


orientado a objetos.

Programação estruturada VS Programação


Orientada a Objetos
Antigamente, há algumas décadas atrás, o paradigma que predominava no
desenvolvimento de software era o da programação estruturada. Basicamente, a grosso
modo, os softwares eram compostos por rotinas e sub-rotinas que chamavam umas às
outras, além de variáveis que tinham escopo local (dentro de rotinas / sub-rotinas) ou
global. Assim como todo paradigma, o paradigma estruturado tinha seus prós e contras
e foi bastante eficiente no que se propôs durante seus anos de domínio no mercado,
além de ter sido bastante importante para a evolução da engenharia de desenvolvimento
de software.

Com o passar dos anos, surgiu o paradigma da orientação a objetos, que também não é
tão novo como muitos pensam, mas que veio a ganhar mais força na última década.

A orientação a objetos surgiu com o objetivo de tornar o desenvolvimento de software


menos complexo e mais produtivo. A idéia era termos estruturas de dados, que possuem
estado e comportamento e colaboram entre si. Dessa forma, deixaríamos de ter todas as
rotinas e sub-rotinas “espalhadas” pelo sistema e passamos a atribuir elas a uma dessas
estruturas de dados, de forma coesa, cada qual com sua responsabilidade. Além disso,
encapsularíamos as variáveis nestas mesmas estruturas, controlando o acesso às mesmas
e tornando público apenas aquilo que for pertinente.

O paradigma da orientação a objetos possui três conceitos fundamentais:

• Encapsula mento – Prevê o isolamento a determinados elementos do objeto (métodos


/atributos) de acordo com a necessidade de acesso a eles. Este conceito parte da
premissa de que nem todo método e atributo precisam estar visíveis e acessíveis
publicamente. Existem elementos que são pertinentes apenas ao próprio objeto, outros
pertinentes aos objetos filhos e outros que são pertinentes todos os objetos associados.
O encapsula mento se dá através dos modificadores de acesso, que veremos mais a
frente.

• Abstração – É a capacidade de focar nos pontos mais importantes do domínio de


aplicação do sistema e abstrair os detalhes menos relevantes. Na modelagem de um
sistema orientado a objetos, uma classe tende a ser a abstração de entidades existentes
no mundo real (domínio da aplicação). Ex.: Cliente, Funcionário, Conta Bancária.

• Polimorfismo – É a capacidade de um elemento ser capaz de assumir diferentes


formas. Na orientação a objetos, chamamos de polimorfismo a possibilidade que temos
de mudar o comportamento de um mesmo método de um objeto dentro da sua
hierarquia.

Além disso, podemos citar os seguinte elementos com sendo os alguns dos principais da
orientação a objetos:

• Classe – Uma classe é um tipo de dado que representa tudo aquilo que um objeto deste
tipo poderá ter/fazer. Na classe determinamos o que será armazenado em seu estado e
quais comportamentos ele terá, ela funciona como uma estrutura de referência para a
criação de objetos. Uma classe pode ter vários objetos.

• Objeto – Um objeto é uma instância de uma classe. É a estrutura completa, criada em


memória, que irá representar a classe com tudo o que foi definido nela, inclusive com os
valores armazenados nos seus respectivos atributos.

• Atributos – Os atributos representam o estado de um objeto. É neles que


armazenaremos as informações de nossos objetos. Ex.: Nome, Idade, Endereço etc...

• Métodos – Representam os comportamentos (operações) de nossos objetos, são as


operações que o mesmo pode executar.Ex.: ValidarCPF, AprovarCredito,
LiberarPagamento etc...
Figura 1. Representação Gráfica de classe com muitos objetos

Na Figura 1, podemos ver que uma classe pode ser instanciada diversas vezes, dando
origem a diversos objetos diferentes.

Tipos de Referência x Tipos de Valor


Em C# nós temos duas classificações de tipos de dados. Que são os tipos de referência
(References Types) e os tipos de valor (Value Types). A diferença chave entre os dois
tipos é a na passagem de valores dos mesmos. No caso dos reference types, os valores
dos objetos não são copiados, mas apenas sua referência, enquanto que nos value types
os valores são copiados de um objeto para o outro.

Todos os objetos que são do tipo de uma classe ou interface são reference types. Tipos
enumerados e tipos primitivos são value types.

Ao atribuirmos uma referência de value type a outra, estamos literalmente copiando o


seu valor, replicando o mesmo para o novo elemento. Ao atribuirmos uma referência de
um reference type para outro, não há cópia de valores, mas apenas de suas referências.

Listagem 1. Exemplo de Value Types

01 using System;

02 using System.Collections.Generic;
03 using System.Linq;

04 using System.Text;

05 using ExemplosFundamentos.Modificadores;

06 using ExemplosFundamentos.AbstractExemplo;

07 using ExemplosFundamentos.PartialClass;

08

09 namespace ExemplosFundamentos

10 {

11 class Program

12 {

13 static void Main(string[] args)

14 {

15 double valorA = 10;

16 double valorB = 20;

17 Console.WriteLine("valorA : " + valorA);

18 Console.WriteLine("valorB : " + valorB);

19 Console.WriteLine("Copiando valor de A para


B...");

20 valorB = valorA;

21 Console.WriteLine("valorA : " + valorA);


22 Console.WriteLine("valorB : " + valorB);

23 Console.WriteLine("Alterando valor de A para


50...");

24 valorA = 50;

25 Console.WriteLine("valorA : " + valorA);

26 Console.WriteLine("valorB : " + valorB);

27 Console.ReadLine();

28 }

29 }

30 }
"

Entendendo a
Orientação a Objetos
Facebook Twitter
(0) (0)

O artigo apresentará uma breve introdução à orientação a objetos e padrões de


projetos com foco em desenvolvedores que estão começando a programar ou
desenvolvedores que não programam orientado a objetos

Atenção: esse artigo tem um vídeo complementar. Clique e


assista!
De que se trata o artigo
O artigo apresentará uma breve introdução à orientação a objetos e padrões de projetos
com foco em desenvolvedores que estão começando a programar ou desenvolvedores
que não programam orientado a objetos.

Em que situação o tema é útil

A programação orientada a objetos é um dos principais requisitos de conhecimento de


um programador atualmente, presente na maioria das linguagens modernas e
atualizadas. A programação orientada a objetos serve para permitir diversas melhorias
em um projeto. Um dos grandes desafios durante a construção de um projeto é que ele
seja flexível para receber novas funcionalidades, escalável, com códigos reutilizáveis e
compatíveis com outros processos. A orientação a objetos existe para ajudar o
programador a alcançar melhores resultados durante todas as etapas de criação de um
projeto.

OO

A programação Orientada a objetos é uma forma de programação de software adotada


pela maioria das linguagens de programação modernas, ela busca expressar as coisas de
uma forma mais próxima da vida real, baseado na composição e interação entre os
objetos. Neste artigo veremos uma introdução à programação orientada a objetos, seus
fundamentos e sua aplicação. Em seguida será apresentada uma introdução aos padrões
de projetos, mostrando o porquê foram criados e para que servem.

Orientação a objetos talvez seja um dos assuntos mais importantes e aplicados nos dias
de hoje. Apesar de antigo, nem sempre essa forma de programar foi tão adotada no
mercado como atualmente, onde temos como boa prática a criação de projetos
orientados a objetos. Mas existem diversos paradigmas de programação que foram
criados para buscar melhorar e atender a evolução tecnológica de desenvolvimento de
softwares.

Neste artigo veremos de forma resumida alguns conceitos e características da


programação orientada a objetos. O artigo não vai se aprofundar em aplicações práticas,
pois o objetivo é introduzir o leitor que não conhece o que é a programação orientada a
objetos. Por ser um artigo teórico, serão apresentados conceitos sobre os fundamentos
da POO, além de introduzir superficialmente o leitor aos padrões de projetos e boas
práticas de programação.

O artigo não tem foco em nenhuma linguagem de programação específica, ele trata dos
fundamentos que podem ser aplicados nas linguagens de programação com suporte a
orientação a objetos, como o Visual C# .NET e Visual Basic .NET. A IDE de
desenvolvimento da Microsoft, o Visual Studio, atualmente na versão 2010, é repleto de
recursos que facilitam a vida do desenvolvedor a programar orientado a objetos, tendo
inclusive uma versão gratuita (express) que é recomendada para quem está iniciando
com o desenvolvimento de softwares.

Quem está começando na área de desenvolvimento de software, talvez tenha mais


facilidade de entender seus fundamentos e conceitos, começando diretamente a
programar orientado a objetos, porém, uma dúvida pode já surgir, se eu não estou
programando orientado a objetos, eu estou programando em que?
A Orientação a objetos é um paradigma de programação, um paradigma determinará a
visão de como será a estrutura de um software. No início os programas eram criados em
código de máquina, um paradigma complexo e de difícil leitura, em seguida outros
paradigmas foram criados, mas vou destacar três, os paradigmas procedural, funcional e
orientado a objetos.

"

Orientação a Objetos
no .NET Framework
Facebook Twitter
(1) (0)

Este artigo demonstra alguns dos conceitos relacionados com a Orientação a


Objetos, visando demonstrar e esclarecer os conceitos por detrás do .NET
framework.

Atenção: esse artigo tem um vídeo complementar. Clique e


assista!

[lead]Do que trata o artigo

Este artigo demonstra alguns dos conceitos relacionados com a Orientação a Objetos,
visando demonstrar e esclarecer os conceitos por detrás do .NET framework.

Para que serve

A Orientação a Objetos tem por principais objetivos: facilitar o reuso de código, evitar
duplicações, proporcionar maior clareza no desenvolvimento do sistema, aumentar a
compreensão do que está sendo feito e ainda possibilita extensão e adaptação do código
sem grandes impactos e alterações.

Em que situação o tema é útil

Para se trabalhar utilizando o máximo do poder do .Net é preciso conhecer e entender


Orientação a Objetos, obtendo assim todas as facilidades e produtividade deste incrível
framework. Na atualidade praticamente todas as aplicações comerciais são construídas
utilizando Orientação a Objetos, desta forma é imprescindível entender seus conceitos
de forma prática.

Resumo do DevMan

Neste artigo veremos alguns dos conceitos da Orientação a Objetos de forma prática.
Analisaremos uma aplicação construída sem utilizar OO e então transformaremos a
mesma aplicação, utilizando conceitos de Classes, Métodos e Encapsulamento,
entendendo como estes conceitos e práticas tão particulares da OO tornarão nossa
aplicação mais extensível, coesa e simples. Ao final teremos transformado nosso código
de maneira que possamos reutilizá-lo tanto em uma aplicação Windows como em uma
aplicação WEB. [/lead]

Não iniciarei este artigo com um histórico das linguagens que trabalham com
Orientação a Objetos. Definiremos de forma teórica alguns pontos da Orientação a
Objetos e trabalharemos estes pontos de forma prática, visando compreender como eles
realmente funcionam e são aplicados, entendendo seus reais benefícios.

Atualmente existem muitas linguagens que trabalham com Orientação a Objetos, e o


.Net Framework funciona desta maneira com suas linguagens. Quando estamos
utilizando C# estamos trabalhando com uma linguagem Orientada a Objetos e podemos
tirar o máximo proveito de tudo que este paradigma de programação e desenvolvimento
tem anos oferecer. No entanto apesar de uma linguagem ser Orientada a Objetos isso
não significa que todo código criado utilizando esta linguagem seja um código
realmente Orientado a Objetos.

[subtitulo]Definindo alguns conceitos da Orientação a Objetos [/subtitulo]

O paradigma da Orientação a Objetos define alguns pilares sobre os quais as linguagens


devem atuar, e além disso, algumas características que estas linguagens devem possuir.
Os conceitos com os quais trabalharemos neste artigo são:

• Classes;

• Objetos;

• Encapsulamento;

• Atributos/Propriedades;

• Métodos.

Além destes conceitos a Orientação a Objetos também define os seguintes, que não
abordaremos aqui:

• Herança

• Polimorfismo
[subtitulo]Classes [/subtitulo]

O paradigma da orientação a objetos está intimamente ligado com o conceito de classes.


As classes podem, a grosso modo, serem definidas como um molde para a criação de
objetos. Uma classe contém a descrição das características de um objeto, ou seja, suas
propriedades, como também contém o comportamento de um objeto, ou seja, seus
métodos.

"

É válido ressaltar que a Orientação a Objetos também não é uma “bala de prata”, não é
a opção ideal para tudo. Ela possui vantagens e desvantagens. Para conhecer esses
pontos, assim como alguns mitos que foram criados em torno dela, acesse os artigos:

Vantagens e
Desvantagens da POO
Facebook Twitter
(2) (1)

Veremos neste artigo como o paradigma de programação Orientado a Objeto


pode nos ajudar no desenvolvimento de aplicações mais enxutas, flexíveis e
fáceis de manter, bem como situações onde aplica-lo pode não ser a melhor
opção.

Fique por dentro


Este tema é útil para os desenvolvedores que estão iniciando na Programação Orientada a
Objetos, bem como para aqueles que desejam aprofundar-se na mesma. A orientação a
objetos muita das vezes nos impõe certos desafios e não basta apenas conhecer a sua
estrutura e conceitos para resolvê-los, é preciso dominá-los. O objetivo deste artigo é
demonstrar algumas das principais vantagens e desvantagens da aplicação do paradigma de
Orientação a Objetos e situações onde seu uso é fundamental para o desenvolvimento de
aplicações mais flexíveis e fáceis de manter, bem como cenários onde a programação
estruturada pode ainda ser a melhor opção.
Encontramos muitas definições sobre o que é Programação Orientada a Objetos (POO),
que muitas das vezes estão cercadas de mitos e definições que distorcem o paradigma, e
muitos desses mitos assombram os novatos que desejam aprendê-lo.

A intenção desse artigo é mostrar os principais erros e acertos durante o


desenvolvimento orientado a objetos, esclarecendo as dúvidas mais frequentes a
respeito da aplicação correta desse paradigma.

O que é, na verdade, Programação Orientada a Objetos? Como implementar? Quais as


linguagens que suportam a mesma? Orientação a objetos é abstração do mundo real?
Polimorfismo, mais herança, mais abstração é orientação a objetos? Desenvolver um
sistema orientado a objetos é mais lento? Essas são algumas das várias perguntas feitas
por diversos desenvolvedores, até os mais experientes no assunto. Vamos esclarecer
essas questões com exemplos práticos, sempre procurando as melhores soluções.

A Programação Orientada a Objetos é um método de codificação que implica na


utilização de objetos e suas relações a fim de descrever, de forma programática, o
problema a ser resolvido. A definição clássica de POO foi baseada em três pilares
fundamentais: encapsulamento, herança e polimorfismo.

O princípio do encapsulamento define que os elementos de dados não estão disponíveis


para o mundo exterior diretamente. Em vez disso, seriam criados métodos para dar
acesso a esses valores fora do objeto. Hoje em dia, linguagens como C#, por exemplo,
têm a capacidade de utilizar não só métodos para essa função, mas também podemos
criar propriedades que podem acessar elementos de dados internos do objeto, como
mostra o código da Listagem 1.

Listagem 1. Exemplo de implementação do encapsulamento.

01 public class Pessoa

02 {

03 private string _nome;

04

05 public string Nome

06 {

07 get

08 {
09 return _nome;

10 }

11 set

12 {

13 _nome = value;

14 }

15 }

16 }

O código apresentado é uma forma alternativa que a linguagem C# disponibiliza para


acessar elementos que estão protegidos segundo o conceito de encapsulamento. Na linha
09 está sendo retornado conteúdo da variável interna _nome, mas ao invés de
simplesmente retornar o valor, nós poderíamos efetuar várias manipulações dos dados
antes de retornar o mesmo, assim como também antes de definir o valor para a variável
interna _nome (linha 13) nós também poderíamos efetuar várias manipulações
utilizando as propriedades.

Outro conceito fundamental que precisamos conhecer é o de herança, pois este recurso
permite que os desenvolvedores possam definir os objetos de uma forma hierárquica,
como mostra a Figura 1.
Figura 1. Diagrama de classe.

O Diagrama de classe representa a base para criar um objeto ou um conjunto deles de


forma abstrata para que possamos manipular informações do mundo real em nossos
sistemas. Cada nível da hierarquia define um objeto mais específico do que o nível pai e
cada nível herda todas as propriedades e métodos de seu objeto pai. Nesse ponto você
define as propriedades e métodos mais específicos, fazendo assim com que os objetos
que estão mais distantes sejam mais específicos.

Por fim, precisaremos também entender o funcionamento do terceiro pilar da POO que é
o polimorfismo. Este pilar é a capacidade que os objetos de uma classe específica têm
de ser tratados como objetos de uma classe base. As classes base podem definir e aplicar
métodos virtuais e as classes derivadas podem substituí-los, o que significa que elas
fornecem sua própria definição e implementação. Podemos então concluir que
polimorfismo, como o próprio nome já diz, é a capacidade de um objeto de ter várias
formas. Confira na Listagem 2 um exemplo de implementação do polimorfismo em C#.

Listagem 2. Implementação do polimorfismo.

01 public abstract class Pessoa

02 {

03 public string Nome { get; set; }

04

05 public abstract decimal GetSaldoNoBanco();

06

07 }

08

09 public class PessoaFisica : Pessoa

10 {

11 public PessoaFisica()

12 {

13 Nome = "Pessoa Física";

14 }
15

16 public override decimal GetSaldoNoBanco()

17 {

18 return 1500;

19 }

20 }

21

22 public class PessoaJuridica : Pessoa

23 {

24 public PessoaJuridica()

25 {

26 Nome = "Pessoa Jurídica";

27 }

28

29 public override decimal GetSaldoNoBanco()

30 {

31 return 20000;

32 }

33 }

34
35 class Program

36 {

37 static void Main(string[] args)

38 {

39 var pessoaFisica = new PessoaFisica();

40 var pessoaJuridica = new PessoaJuridica();

41

42 ImprimirValorDoBanco(pessoaFisica);

43 ImprimirValorDoBanco(pessoaJuridica);

44

45 Console.Read();

46 }

47

48 private static void ImprimirValorDoBanco(Pessoa pessoa)

49 {

50 var valorBanco = pessoa.GetSaldoNoBanco();

51 Console.WriteLine("Valor disponível na conta da {0} é:


{1}",

52 pessoa.Nome, valorBanco);

53 }
54 }

Como podemos ver no código que implementa de fato os conceitos de polimorfismo, o


método I" [...]

Mitos e Verdades
sobre a Orientação a
Objetos em .NET
Facebook Twitter
(3) (0)

Neste artigo iremos desvendar os mistérios da Programação orientada a


objetos com ênfase em .NET, mostrando os seus princípios e resolvendo
algumas questões referentes a dúvidas cotidianas sobre o assunto.

Artigo no estilo Mentoring (saiba mais)

Fique por dentro


O desenvolvimento orientado a objetos é o principal paradigma de programação
atualmente. O seu tipo de design permite diversas interpretações, o que leva a vários
mitos criados em torno da POO. Esse artigo visa trazer alguns dizeres comuns no meio
de desenvolvimento e analisá-los de forma a entendermos se são, de fato, verdades, ou
apenas mitos criados ao longo do tempo.

A ideia é que o leitor, ao final do artigo, tenha um entendimento desses elementos e


também como eles irão se aplicar ao C#/.NET.

A programação orientada a objetos está presente no dia-a-dia de grande parte dos


desenvolvedores. Com a grande quantidade de programadores que desenvolviam em
linguagens procedurais como C e Pascal, a alteração para a POO criou uma série de
mitos que muitos acreditam até hoje como verdades.

Como veremos ao longo desse artigo, alguns deles são, de fato, verdades, enquanto
outros são mitos que acabam sendo repetidos erroneamente.
O objetivo final é entendermos porque esses dizeres são verdades ou mitos através de
exemplos utilizando a linguagem de programação C# .NET.

Ao longo desse artigo, iremos trazer uma introdução breve à programação orientada a
objetos e o que ela significa no mundo de desenvolvimento. Juntamente com essa
introdução, traremos alguns exemplos de linguagens orientadas a objetos comuns no
mercado. Note que a implementação dos conceitos da POO irá variar de acordo com a
linguagem de programação.

A seguir, iremos trazer alguns dizeres comuns a respeito da POO e analisá-los de forma
detalhada através de exemplo com C#.

Programação Orientada a Objetos


Em primeiro lugar, precisamos entender que a programação orientada a objetos não é
exclusividade de nenhuma linguagem de programação: trata-se de um design de
software, um modelo a ser seguido. Nesse modelo, o objeto é a unidade essencial,
responsável por dados e métodos para modificação do mesmo.

A grande vantagem da POO com relação a outras, como a programação procedural (ou
estruturada - BOX 1), é a divisão extremamente clara entre os elementos do software.
Além disso, o paradigma da orientação a objetos facilita a modelagem do software,
auxiliando na criação de uma documentação muito mais completa e explicativa para a
aplicação.

Essa clara divisão entre os elementos facilita na criação de aplicações modernas. A POO
ainda facilita a introdução de alguns modelos de programação, como o MVC (Model-
View-Controller) e o MVP (Model-View-Presenter).

BOX 1. Programação procedural

A programação procedural (ou estruturada) obedece a um paradigma de programação


que difere da POO em alguns pontos. Nela, os procedimentos estão ligados a dados
globais, diferentemente do que vemos na POO, onde os métodos (equivalentes aos
procedimentos) estão ligados aos dados do objeto.

Esse tipo de diferenciação é importante em termos da organização do software, o que


traz algumas facilidades e alguns problemas.

Atualmente, linguagens estruturadas como C e Pascal são utilizadas principalmente em


microcontroladores e outros elementos de programação embarcada.

O paradigma da orientação a objetos traz o conceito da abstração de objetos do mundo


real. Entretanto, como veremos ao longo de nosso artigo, isso não é uma verdade
absoluta. Isso é devido ao fato de que a programação orientada a objetos trouxe uma
implementação um pouco diferente, baseada em classes que podem ou não ser
representações da realidade.
Como um exemplo, podemos ter uma classe Carro ou Pessoa, mas também podemos ter
classes que não representam um objeto real, como DadosCarro ou DadosPessoa. Essas
classes, então, irão definir os objetos presentes no sistema.

Dentre os desenvolvedores, há a consciência de que uma linguagem orientada a objetos


deve obedecer obrigatoriamente a quatro conceitos, considerados os pilares da POO:

· Abstração: consiste em abstrair o que queremos representar e transformá-lo em


informações para serem utilizadas dentro da aplicação. Essas informações existem na
forma de uma identidade (nome único da classe ou
objeto), propriedades (características que definem o objeto) e métodos (ações ou
eventos aos quais o objeto deve obedecer).

· Encapsulamento: consiste em esconder as informações a respeito de características e


métodos de uma classe. Normalmente, esse encapsulamento é baseado em métodos
especiais getters e setters, que serão considerados as propriedades da classe. O dado
encapsulado é chamado, normalmente, de atributo.

· Herança: consiste em criar uma hierarquia de classes dentro da aplicação. A ideia


principal aqui é a reutilização de código. Por exemplo, temos uma aplicação que possui
uma classe Animal e uma classe Cachorro. O ideal é que a segunda herde informações
da primeira, uma vez que se trata de um tipo específico de animal, com características a
mais.

· Polimorfismo: normalmente, é tratado como uma área adjacente à herança. Isso porque
o polimorfismo consiste em um objeto se comportando como vários. Em poucas
palavras, linguagens que obedecem a esse conceito permitem que um objeto filho (do
tipo Cachorro, para ficarmos no exemplo anterior) possa se comportar como ele mesmo
(Cachorro) ou como sua classe pai (Animal, nesse caso).

Linguagens de programação orientada a objetos modernas, como C# e Java, utilizam


uma artimanha muito interessante para criar aplicações capazes de executar em
diferentes arquiteturas.

No caso do Java, a presença da JVM (Java Virtual Machine) garante que o software
pode ser executado em diferentes máquinas. O compilador Java cria a aplicação
em bytecodes capazes de serem executados por essa JVM, criando uma aplicação capaz
de executar em qualquer dispositivo imaginável, em teoria.

O C# .NET utiliza uma estrutura similar, compilando a aplicação para uma linguagem
intermediária de tempo de execução, que é executada pelo .NET Framework. Existem
também outras abordagens, como a utilizada pelo Python.

Essa linguagem é considerada uma linguagem de script, o que significa que ela não é
compilada, e sim interpretada em tempo de execução. Isso faz com que a mesma tenha
uma melhor performance.

É interessa" [...]
Pilares da Orientação a Objetos
Após o primeiro contato com a Orientação a Objetos, você deve ter observado

termos como abstração, encapsulamento, herança e polimorfismo. Estes são

os fundamentos, os quatro pilares da POO. Para aprender sobre eles, algo


fundamental para programar corretamente com esse paradigma, acesse:

Os 4 pilares da
Programação
Orientada a Objetos
Facebook Twitter
(80) (2)

Conheça nesse artigo os 4 principais pilares, bem como as diferenças para


programação estruturada e as principais vantagens da POO.

O desenvolvimento de software é extremamente amplo. Nesse mercado, existem


diversas linguagens de programação, que seguem diferentes paradigmas. Um desses
paradigmas é a Orientação a Objetos, que atualmente é o mais difundido entre todos.
Isso acontece porque se trata de um padrão que tem evoluído muito, principalmente em
questões voltadas para segurança e reaproveitamento de código, o que é muito
importante no desenvolvimento de qualquer aplicação moderna.

A Programação Orientada a Objetos (POO) diz respeito a um padrão de


desenvolvimento que é seguido por muitas linguagens, como C# e Java. A seguir,
iremos entender as diferenças entre a POO e a Programação Estruturada, que era
muito utilizada há alguns anos, principalmente com a linguagem C. Esse padrão se
baseia em quatro pilares que veremos ao longo desse artigo. Além disso, a POO
diversas vantagens em sua utilização, que também serão vistas e explicadas.
Saiba mais sobre Orientação a Objetos

Programação Estruturada vs Programação Orientada a

Objetos

Na Figura 1 vemos uma comparação muito clara entre a programação estruturada e a


programação orientada a objetos no que diz respeito aos dados. Repare que, no
paradigma estruturado, temos procedimentos (ou funções) que são aplicados
globalmente em nossa aplicação. No caso da orientação a objetos, temos métodos que
são aplicados aos dados de cada objeto. Essencialmente, os procedimentos e métodos
são iguais, sendo diferenciados apenas pelo seu escopo.

Figura 1. Estruturada x Orientação a Objetos

A linguagem C é a principal representante da programação estruturada. Se trata de uma


linguagem considerada de baixo nível, que atualmente não é utilizada para projetos
muito grandes. A sua principal utilização, devido ao baixo nível, é em programação para
sistemas embarcados ou outros em que o conhecimento do hardware se faz necessário
para um bom programa.

Essa colocação nos traz a um detalhe importante: a programação estruturada, quando


bem feita, possui um desempenho superior ao que vemos na programação orientada a
objetos. Isso ocorre pelo fato de ser um paradigma sequencial, em que cada linha de
código é executada após a outra, sem muitos desvios, como vemos na POO. Além
disso, o paradigma estruturado costuma permitir mais liberdades com o hardware, o que
acaba auxiliando na questão desempenho.

Entretanto, a programação orientada a objetos traz outros pontos que acabam sendo
mais interessantes no contexto de aplicações modernas. Como o desempenho das
aplicações não é uma das grandes preocupações na maioria das aplicações (devido ao
poder de processamento dos computadores atuais), a programação orientada a objetos se
tornou muito difundida. Essa difusão se dá muito pela questão da reutilização de código
e pela capacidade de representação do sistema muito mais perto do que veríamos no
mundo real.

Veremos em detalhes esses e outros pontos que dizem respeito a programação


orientada a objetos. Como desenvolvedores, é nossa missão entender quais são as
vantagens e desvantagens de cada um dos paradigmas de programação e escolhermos o
melhor para nossa aplicação. A escolha da linguagem também deve estar presente nessa
escolha.

Saiba mais sobre Orientação a Objetos x Programação Estruturada

DevCast: Por que adotamos Orientação a Objetos?

Os 4 pilares da Programação Orientada a Objetos

Para entendermos exatamente do que se trata a orientação a objetos, vamos entender


quais são os requerimentos de uma linguagem para ser considerada nesse paradigma.
Para isso, a linguagem precisa atender a quatro tópicos bastante importantes:

Abstração

A abstração consiste em um dos pontos mais importantes dentro de qualquer


linguagem Orientada a Objetos. Como estamos lidando com uma representação de um
objeto real (o que dá nome ao paradigma), temos que imaginar o que esse objeto irá
realizar dentro de nosso sistema. São três pontos que devem ser levados em
consideração nessa abstração.

O primeiro ponto é darmos uma identidade ao objeto que iremos criar. Essa identidade
deve ser única dentro do sistema para que não haja conflito. Na maior parte das
linguagens, há o conceito de pacotes (ou namespaces). Nessas linguagens, a identidade
do objeto não pode ser repetida dentro do pacote, e não necessariamente no sistema
inteiro. Nesses casos, a identidade real de cada objeto se dá por ..

A segunda parte diz respeito a características do objeto. Como sabemos, no mundo real
qualquer objeto possui elementos que o definem. Dentro da programação orientada a
objetos, essas características são nomeadas propriedades. Por exemplo, as propriedades
de um objeto “Cachorro” poderiam ser “Tamanho”, “Raça” e “Idade”.

Por fim, a terceira parte é definirmos as ações que o objeto irá executar. Essas ações, ou
eventos, são chamados métodos. Esses métodos podem ser extremamente variáveis,
desde “Acender()” em um objeto lâmpada até “Latir()” em um objeto cachorro.

Saiba mais sobre Abstração e Polimorfismo


Encapsulamento

O encapsulamento é uma das principais técnicas que define a programação orientada a


objetos. Se trata de um dos elementos que adicionam segurança à aplicação em uma
programação orientada a objetos pelo fato de esconder as propriedades, criando uma
espécie de caixa preta.

A maior parte das linguagens orientadas a objetos implementam o encapsulamento


baseado em propriedades privadas, ligadas a métodos especiais
chamados getters e setters, que irão retornar e setar o valor da propriedade,
respectivamente. Essa atitude evita o acesso direto a propriedade do objeto, adicionando
uma outra camada de segurança à aplicação.

Para fazermos um paralelo com o que vemos no mundo real, temos o encapsulamento
em outros elementos. Por exemplo, quando clicamos no botão ligar da televisão, não
sabemos o que está acontecendo internamente. Podemos então dizer que os métodos que
ligam a televisão estão encapsulados.

Saiba mais sobre Encapsulamento em Java

Herança

O reuso de código é uma das grandes vantagens da programação orientada a objetos.


Muito disso se dá por uma questão que é conhecida como herança. Essa característica
otimiza a produção da aplicação em tempo e linhas de código.

Para entendermos essa característica, vamos imaginar uma família: a criança, por
exemplo, está herdando características de seus pais. Os pais, por sua vez, herdam algo
dos avós, o que faz com que a criança também o faça, e assim sucessivamente. Na
orientação a objetos, a questão é exatamente assim, como mostra a Figura 2. O objeto
abaixo na hierarquia irá herdar características de todos os objetos acima dele, seus
“ancestrais”. A herança a partir das características do objeto mais acima é considerada
herança direta, enquanto as demais são consideradas heranças indiretas. Por exemplo, na
família, a criança herda diretamente do pai e indiretamente do avô e do bisavô.
Figura 2. Herança na orientação a objetos

A questão da herança varia bastante de linguagem para linguagem. Em algumas delas,


como C++, há a questão da herança múltipla. Isso, essencialmente, significa que o
objeto pode herdar características de vários “ancestrais” ao mesmo tempo diretamente.
Em outras palavras, cada objeto pode possuir quantos pais for necessário. Devido a
problemas, essa prática não foi difundida em linguagens mais modernas, que utilizam
outras artimanhas para criar uma espécie de herança múltipla.

Outras linguagens orientadas a objetos, como C#, trazem um objeto base para todos os
demais. A classe object fornece características para todos os objetos em C#, sejam
criados pelo usuário ou não.

Polimorfismo

Outro ponto essencial na programação orientada a objetos é o chamado polimorfismo.


Na natureza, vemos animais que são capazes de alterar sua forma conforme a
necessidade, e é dessa ideia que vem o polimorfismo na orientação a objetos. Como
sabemos, os objetos filhos herdam as características e ações de seus “ancestrais”.
Entretanto, em alguns casos, é necessário que as ações para um mesmo método seja
diferente. Em outras palavras, o polimorfismo consiste na alteração do funcionamento
interno de um método herdado de um objeto pai.
Como um exemplo, temos um objeto genérico “Eletrodoméstico”. Esse objeto possui
um método, ou ação, “Ligar()”. Temos dois objetos, “Televisão” e “Geladeira”, que não
irão ser ligados da mesma forma. Assim, precisamos, para cada uma das classes filhas,
reescrever o método “Ligar()”.

Com relação ao polimorfismo, valem algumas observações. Como se trata de um


assunto que está intimamente conectado à herança, entender os dois juntamente é uma
boa ideia. Outro ponto é o fato de que as linguagens de programação implementam o
polimorfismo de maneiras diferentes. O C#, por exemplo, faz uso de método virtuais
(com a palavra-chave virtual) que podem ser reimplementados (com a palavra-
chave override) nas classes filhas. Já em Java, apenas o atributo “@Override” é
necessário.

Esses quatro pilares são essenciais no entendimento de qualquer linguagem orientada a


objetos e da orientação a objetos como um todo. Cada linguagem irá implementar esses
pilares de uma forma, mas essencialmente é a mesma coisa. Apenas a questão da
herança, como comentado, que pode trazer variações mais bruscas, como a presença de
herança múltipla. Além disso, o encapsulamento também é feito de maneiras distintas
nas diversas linguagens, embora os getters e setters sejam praticamente onipresentes.

Saiba mais sobre Polimorfismo em Java

Principais vantagens da POO

A programação orientada a objetos traz uma ideia muito interessante: a representação de


cada elemento em termos de um objeto, ou classe. Esse tipo de representação procura
aproximar o sistema que está sendo criado ao que é observado no mundo real, e um
objeto contém características e ações, assim como vemos na realidade. Esse tipo de
representação traz algumas vantagens muito interessantes para os desenvolvedores e
também para o usuário da aplicação. Veremos algumas delas a seguir.

A reutilização de código é um dos principais requisitos no desenvolvimento de software


atual. Com a complexidade dos sistemas cada vez maior, o tempo de desenvolvimento
iria aumentar exponencialmente caso não fosse possível a reutilização. A orientação a
objetos permite que haja uma reutilização do código criado, diminuindo o tempo de
desenvolvimento, bem como o número de linhas de código. Isso é possível devido ao
fato de que as linguagens de programação orientada a objetos trazem representações
muito claras de cada um dos elementos, e esses elementos normalmente não são
interdependentes. Essa independência entre as partes do software é o que permite que
esse código seja reutilizado em outros sistemas no futuro.

Outra grande vantagem que o desenvolvimento orientado a objetos traz diz respeito a
leitura e manutenção de código. Como a representação do sistema se aproxima muito do
que vemos na vida real, o entendimento do sistema como um todo e de cada parte
individualmente fica muito mais simples. Isso permite que a equipe de desenvolvimento
não fique dependente de uma pessoa apenas, como acontecia com frequência em
linguagens estruturadas como o C, por exemplo.
A criação de bibliotecas é outro ponto que é muito mais simples com a orientação a
objetos. No caso das linguagens estruturadas, como o C, temos que as bibliotecas são
coleções de procedimentos (ou funções) que podem ser reutilizadas. No caso da POO,
entretanto, as bibliotecas trazem representações de classes, que são muito mais claras
para permitirem a reutilização.

Entretanto, nem tudo é perfeição na programação orientada a objetos. A execução de


uma aplicação orientada a objetos é mais lenta do que o que vemos na programação
estruturada, por exemplo. Isso acontece devido à complexidade do modelo, que traz
representações na forma de classes. Essas representações irão fazer com que a execução
do programa tenha muitos desvios, diferente da execução sequencial da programação
estruturada. Esse é o grande motivo por trás da preferência pela linguagem C em
hardware limitado, como sistemas embarcados. Também é o motivo pelo qual a
programação para sistemas móveis como o Google Android, embora em Java
(linguagem orientada a objetos), seja feita o menos orientada a objetos possível.

No momento atual em que estamos, tecnologicamente essa execução mais lenta não é
sentida. Isso significa que, em termos de desenvolvimento de sistemas modernos, a
programação orientada a objetos é a mais recomendada devido as vantagens que foram
apresentadas. Essas vantagens são derivadas do modelo de programação, que busca uma
representação baseada no que vemos no mundo real.

Saiba mais sobre vantagens e desvantagens da Orientação a Objetos

Exemplos de Linguagens Orientadas a Objetos

Há uma grande quantidade de linguagens de programação orientada a objetos no


mercado atualmente. Nesse artigo, iremos apresentar 3 das mais utilizadas no momento:
Java, C# e C++. Cada uma delas possui uma abordagem diferente do problema que as
torna muito boas para alguns tipos de aplicações e não tão boas para outros.

Java

O Java é, muito provavelmente, a linguagem de programação mais utilizada no mercado


atual. Auxiliado pela presença do JRE (Java Runtime Environment), ou variações dele,
em quase todos os dispositivos eletrônicos do momento, a linguagem Java é um grande
sucesso entre os desenvolvedores. O sucesso da linguagem aumentou ainda mais com o
Google Android, que escolheu o Java como linguagem preferencial de desenvolvimento
de aplicações.

O Java implementa os quatro pilares de forma bastante intuitiva, o que facilita o


entendimento por parte do desenvolvedor. A abstração, o primeiro pilar, é
implementado através de classes, que contém propriedades e métodos, de forma
bastante simples. Já o encapsulamento é realizado através de propriedades privadas,
auxiliadas por métodos especiais getters e setters, como mostra a Listagem 1. Vale
ressaltar a palavra-chave “this” mostrada no método SetId(). Essa palavra-chave
funciona como um representante da classe atual, uma auto-referência ao próprio objeto.

private int id;

public int GetId()

return id;

public void SetId(int id)

this.id = id;

}
Listagem 1. Encapsulamento em Java

As questões de herança e polimorfismo no Java são um pouco mais complexas. O Java


possui herança simples, o que significa que cada classe pode herdar de apenas uma
outra. Entretanto, o Java possui as chamadas Interfaces, que possuem propriedades e
assinaturas de métodos. Essas interfaces precisam ser implementadas para funcionar, o
que significa que uma classe pode implementar várias interfaces e herdar de apenas uma
classe. Na questão de polimorfismo, o atributo @Override é responsável por informar
ao Java que o método em questão está sendo reescrito.

C#

O C#, por sua vez, é outra das linguagens mais utilizadas no mercado. Como os
computadores pessoais no mundo, em sua maioria, possuem o sistema operacional
Windows, da Microsoft, o C# se popularizou. Isso porque o Windows implementa o
Framework .NET, ao qual o C# está associado. O C# é uma linguagem de uso geral e
especialmente criada para utilização com a orientação a objetos. Vale ressaltar que, em
C#, tudo é um objeto (herda da classe object).

A abstração é muito simples, e segue o modelo do Java. A questão de encapsulamento é


um pouco diferente devido a implementação dos métodos getter e setter. A
nomenclatura também é um pouco diferente. A variável que realmente guarda o valor
do dado é chamada atributo, enquanto a propriedade é o elemento que realmente acessa
aquele dado do mundo externo. Isso está mostrado na Listagem 2. Além disso, o C# faz
uso de duas palavras-chave especiais: get e set.

// Atributo

private int id;

// Propriedade

public int Id

get;

set;

}
Listagem 2. Encapsulamento em C#

A questão da herança em C# também segue o modelo do Java: herança simples e a


possibilidade de utilização de interfaces. A importância das interfaces é muito grande,
uma vez que elas podem dar o tipo dos dados, que somente posteriormente serão
associados a um tipo real, como mostra a Listagem 3. Isso também é válido para o Java.
Por padrão, as identidades das interfaces começam com a letra “I”. O polimorfismo, por
sua vez, é baseado em métodos virtuais (com a palavra-chave virtual) na classe pai e
reescritos com a palavra-chave override na classe filha.

IExemploInterface exemplo;

exemplo = new ImplementacaoIExemploInterface();


Listagem 3. Interfaces em C#
C++

O C++, por sua vez, é uma linguagem um pouco mais primitiva, e permite muito mais
liberdades com o hardware. Como ele foi derivado imediatamente do C, o C++ permite
a utilização de ponteiros, por exemplo, que irão trabalhar diretamente com a memória.
Além disso, o C++ pode utilizar todas as bibliotecas C que existem diretamente.

Em termos de abstração, o C++ implementa classes, assim como qualquer linguagem


orientada a objetos. Ele também possui o sentido de privado e público, que é utilizado
para encapsulamento. Esse encapsulamento é realizado através de
métodos getter e setter, muito similar ao visto em Java, como mostra a Listagem 4.
Repare que a listagem mostra somente a assinatura dos métodos especiais, sendo que
sua implementação é a mesma que em Java. Esse tipo de adaptação é muito comum em
C++, onde a classe é guardada em um arquivo .h e sua implementação em um arquivo
.cpp.

private:

int id;

public:

int GetId() const;

void SetId(int const id);


Listagem 4. Encapsulamento em C++

A questão da herança no C++ é um pouco diferente. A linguagem permite a herança


múltipla, o que significa que cada classe pode herdar de quantas classes desejar. Isso
pode causar problemas de métodos que possuem o mesmo nome, portanto o
desenvolvedor precisa estar atento. O polimorfismo é baseado em métodos virtuais, da
mesma forma como o C#. A complexidade, entretanto, é maior, uma vez que temos que
cuidar de detalhes de mais baixo nível, como acesso a memória.

Além dessas exemplificadas, existem outras linguagens que merecem ser citadas. Entre
elas, podemos elencar: Python, linguagem de script orientada a objetos que é muito
utilizada em pesquisas científicas devido a sua velocidade; Object Pascal (também
conhecida como Delphi, devido ao nome de sua IDE), apesar do grande número de
sistemas mais antigos que a utilizam; Objective-C, que é a linguagem de preferência
para desenvolvimento de aplicações para os sistemas da Apple, como iPhone e iPad;
Ruby, voltada para o desenvolvimento web; e Visual Basic .NET, muito utilizada até
pouco tempo, mas também caindo em desuso, principalmente devido ao avanço do C#
em popularidade.
Ao longo desse artigo, procuramos elencar os elementos que fazem da programação
orientada a objetos um sucesso no momento. Vimos os quatro pilares desse paradigma e
entendemos como eles são implementados em algumas das linguagens mais utilizadas
no mercado de desenvolvimento. Além disso, entendemos algumas das vantagens que
tornaram a programação orientada a objetos um grande sucesso para o desenvolvimento
de sistemas modernos.

Abstração e
polimorfismo na
prática
Facebook Twitter
(12) (0)

Este artigo apresenta importantes fundamentos da programação orientada a


objetos com C#, como herança, abstração e polimorfismo, aplicados em um
exemplo prático.

Artigo do tipo Tutorial


Recursos especiais neste artigo:
Conteúdo sobre boas práticas.

Abstração e polimorfismo na prática


Este artigo apresenta importantes fundamentos da programação orientada a objetos com C#,
como herança, abstração e polimorfismo, aplicados em um exemplo prático. Usando boas
práticas, veremos como estes fundamentos podem tornar um software mais fácil de ser
mantido e evoluído, usando algumas técnicas de desenvolvimento ágil, como a refatoração.
Veremos como resolver problemas comuns encontrados em código fonte (os chamados bad
smells, ou “mau cheiros”), usando uma abordagem de desenvolvimento corretiva e evolutiva.
Uma introdução ao desenvolvimento ágil evolutivo é apresentada, como forma de unir estas
técnicas de orientação a objeto com as técnicas atuais de engenharia de software,
demonstradas em um exemplo com C# e Visual Studio 2013.

Em que situação o tema é útil


As técnicas aqui apresentadas são úteis para construir softwares mais fáceis de serem
mantidos e evoluídos ao longo do tempo, promovendo reutilização de código, reduzindo
acoplamento entre classes, minimizando a ocorrência de bugs, tornando mais fácil a
implementação de novos requisitos e tornando o código mais fácil de ser entendido.

No documento “The new Methodology” [4], Martin Fowler traz uma excelente visão
sobre um novo estilo de desenvolvimento de software, baseado em metodologias ágeis.
Metodologias tradicionais de engenharia de software, focadas em planejamento, têm
sido utilizadas ao longo do tempo. O grande problema é que elas são muito
burocráticas, dão ênfase em contratos fixos, documentação excessiva, vasto
planejamento antecipado (prematuro). Essa abordagem não condiz mais com a realidade
da maioria dos projetos na área de tecnologia, onde novos requisitos são criados,
modificados ou mesmo removidos após um planejamento inicial. Processos e
metodologias modernas, como as metodologias ágeis de desenvolvimento, são
evolutivos por natureza. Organizam o trabalho de forma interativa, realizado em
pequenas partes, com pequenas entregas (design, código, teste, documentação),
liberadas aos poucos, ao invés de um único release enorme. Dão maior ênfase na
colaboração e comunicação aberta e fluente entre stakeholders, clientes e membros do
time, minimizando as chances de insucesso em projetos. Os princípios básicos da
abordagem ágil sugerem que indivíduos e interações são mais importantes que
processos e ferramentas, trabalho deve ser feito mais focado no software em si que em
uma documentação abrangente, colaboração do cliente mais que negociação de contrato
e, principalmente, responder a mudanças mais que seguir um plano.

O problema é que as abordagens tradicionais de engenharia de software se baseiam em


outros ramos de engenharia, como a civil e mecânica, onde os custos com planejamento
são menores. Em software, estima-se que até 50% de custos são gastos com design e
planejamento. Projetos com um escopo invariável são raros, e se requisitos
constantemente mudam, há um grande desperdício de esforço com análise que é
descartada em virtude disso. Trabalho, tempo e dinheiro que são preciosos são perdidos.
Uma abordagem ágil garante que a equipe e projeto sejam capazes de se adaptar às
mudanças que ocorrerão, dentro de um contexto evolutivo.

A XP – Extreme Programming, uma abordagem ágil, prega cinco valores principais:


comunicação, feedback, simplicidade, coragem e respeito, com foco maior em
disciplina do que processos. Já Scrum prega um desenvolvimento ágil baseado em
iterações chamadas sprints e reuniões de acompanhamento frequentes (meetings).
Aplicações reais precisam continuamente ser evoluídas, novos requisitos
implementados, melhorias devem ser feitas no projeto existente (design), algumas vezes
é necessário mudar de tecnologia ou plataforma, corrigir bugs, otimizar e melhorar a
performance. Isso remete a uma pergunta: O que pode ser considerado um “bom
software”? Um bom software é aquele que funciona como planejado, mas depende do
seu negócio. Para pequenas companhias (startups), bons softwares devem ser criados
rapidamente. Para grandes corporações, o foco na construção de um software é torná-lo
fácil de ser mantido ao longo do tempo. Nesse sentido, é necessário tomar cuidado com
bons princípios de design, como criar componentes com baixo acoplamento,
reutilizáveis, que garantam que alterações em componentes não afetam outros, que
objetos tenham responsabilidades únicas, separadas, partes facilmente substituíveis,
complexidade encapsulada. Esses são princípios básicos da programação orientada a
objetos que, vistos dentro de um contexto de uma abordagem ágil evolutiva, vão tornar
o sistema de software mais fácil de testar, evoluir, manter e entender. Padrões de Projeto
(Design Patterns) [1] ajudam a criar bons designs de software, já o uso de refatoração
[2] contínua (outra técnica amplamente divulgada por Fowler) garante código claro,
limpo e funcional em equipes que programam de forma mais ágil, sem se preocupar em
um primeiro momento com muitos aspectos de design, usando, por exemplo, Padrões de
Projeto.

Padrões de Projeto são soluções reutilizáveis para problemas recorrentes no


desenvolvimento orientado a objetos. Aplicar padrões antecipadamente em um projeto
requer mais tempo e aumenta custos, ao mesmo tempo em que aumenta sua
complexidade. Requisitos são modificados, criados ou mesmo eliminados conforme
projetos evoluem. Tentar aplicar padrões antecipadamente para prever essas mudanças é
um problema conhecido como excesso de engenharia [3]. Já projeto em escassez reduz
custos, pois o sistema de software é criado rapidamente devido à falta de tempo,
conhecimento ou necessidade de se adicionar funcionalidades em curto prazo. Ao longo
do tempo, armazena-se um débito de projeto [3], que torna o sistema de software muito
difícil de ser mantido e evoluído. É nesse contexto que entram as refatorações, como
uma forma de melhorar o projeto existente sem alterar seu comportamento externo
observável. Refatoração é o aspecto-chave para um projeto evolutivo, e é na evolução
que se encontra a verdadeira sabedoria. Nesse caso, desenvolvimento ágil, evolutivo,
incremental, apoiado em refatorações e testes, é uma excelente receita para garantir o
sucesso do projeto, seja ela para um startup ou uma grande corporação.

Este artigo vai mostrar como aplicar estes conceitos em um exemplo prático com C# e
Visual Studio 2013. Serão demonstrados os principais fundamentos da orientação a
objetos (ver BOX 1) em uma aplicação que simula notificação de clientes de uma
determinada companhia, por exemplo, uma operadora de telefonia, que precisa avisar
seus clientes sobre cobranças, promoções e lançamentos. Além dos fundamentos da
orientação a objetos, são demonstradas importantes técnicas como programação para
interfaces, padrões, anti-patterns, bad smells [2] e uso contínuo de refatoração para um
desenvolvimento ágil com código sempre limpo e funcional.

"

Coesão e acoplamento
E agora, como saber se estou programando orientado a objetos, se estou

aplicando corretamente seus conceitos? Uma maneira de sanar essa dúvida é


observar se seu código está coeso e com baixo acoplamento.

Um código coeso é aquele que implementa apenas o que de fato é de sua

responsabilidade, por exemplo: um método responsável por imprimir um


relatório não deve saber como acessar o banco de dados. Se ele sabe como
fazer isso, dizemos que ele tem baixa coesão, o que deve ser evitado.

Já um código fortemente acoplado é aquele que depende de muitos pacotes,

classes e/ou métodos para prover uma funcionalidade. Quando se deparar com

situações assim, cuidado. O post a seguir expõe como melhorar a qualidade do


seu código, o ajudando a reduzir o acoplamento entre classes.

Princípios SOLID
Ao avançar seus estudos em OO, logo você se deparará com os Princípios

SOLID. Esse termo representa cinco regras que são avaliadas por arquitetos e

programadores como as boas práticas para uma programação orientada a

objetos, nos auxiliando na construção de um código de fácil leitura,


manutenção e extensão.

O nome SOLID também representa um acrônimo, e como apresentado a

seguir, é formado pela junção da primeira letra do nome de cada um dos


princípios.

Single Responsibility Principle: De acordo com esse princípio, cada classe

deve ser planejada para possuir apenas uma responsabilidade. Sobre ele,
temos alguns posts específicos, os quais indicamos a seguir:
Arquitetura - O
Princípio da
responsabilidade
única
Facebook Twitter
(4) (0)

O princípio da responsabilidade única é um dos princípios SOLID. Os princípios


SOLID, são uma série de cinco princípios introduzidos por Robert C. Martin(
Uncle bob ),como boas práticas de design de software. O princípio da
responsabilidade única, foca na preocupação de que uma classe tenha seu
papel e venha desempenhar somente ele de forma eficiente.

Muito tenho falado sobre boas práticas de engenharia de software. Aplicar boas práticas,
nos rende muitos benefícios como manutenção facilitada, um código organizado,
dependências mais leves e uma arquitetura pronta para “abraçar” as mudanças, ao invés
de brigar com elas. Hoje estarei abordando um dos princípios SOLID, que são
princípios que foram introduzidos por Robert C. Martin, ou como é comumente
chamado Uncle Bob. Os princpipios SOLID, são cinco princípios de engenharia de
software para alcançar os benefícios que falamos anteriormente. Ficou famoso como
SOLID, por que são as iniciais dos princípios, e o pessoal percebeu que formava a
palavra SOLID e passou a usá-la. Veja abaixo:

Principios SOLID
 Sigle Responsability Principle
 Open Close Principle
 Liskov substitution Principle
 Interface segregation principle
 Dependency inversion principle

Abordaremos neste artigo o Sigle Responsability Principle.


Combatendo a Classes faz tudo
Uncle Bob diz eu seu livro clean code, que se uma classe tem mais de um motivo para
ser alterada ela já está ferindo o princípio da responsabilidade única. As classes devem
ter apenas uma responsabilidade, realizar somente ela e realizá-la bem. Observe o
diagrama abaixo:

Figura 1. Classe com muitas responsabilidades

O que você vê de arrado na classe acima? Concorda que ela esta assumindo
responsabilidades que não são suas? Concorda que ela esta fazendo coisas demais? Ela
além de suas propriedades, obtem por id, obtem por nome e ainda salva; diriamos que é
uma geniuna classe “canivete suiço”. E geralmente, um modelo de um sistema não tem
somente uma classe, mais várias e se todas estivessem assim? Teriamos vários
problemas, para dar manutenção e evoluir. Agore veja o exemplo abaixo, refatorado e
contemplando o princípio da responsabilidade única.
Figura 2. Classes com responsabilidades bem definidas

Note que temos um cenário completamente diferente, desacolplado e com cada “peça”
realizando somente o seu papel. Temos uma classe Entidade, imaginando que estejamos
seguindo as premissas do DDD( Domain Drive Design ), uma classe produto que herda
de entidade e possui propriedades comuns a todos os produtos, como por exemplo nome
e fabricante e classes que herdam de produto e tem suas particularidades em cada uma
delas; note que estas classes possuem apenas propriedades e poderiam também, ter
métodos e eventos, mais que tratassem apenas do negócio e não de persistência como
vimos no exemplo anterior. Na hora de persistir, temos classes apropriadas que vão
pegar este objeto de negócio e salvar no banco de dados, ou no repositório destinado a
armazená-los. Lembrando que estamos utilizando um padrão de arquitetura chamado
repository pattern.

O Mandamento do princpipio da
responsabilidade única
Uma classe deve fazer apenas uma coisa, deve fazê-la bem e deve fazer somente ela. Se
uma classe tem mais de um motivo para ser alterada, ela não segue este princípio. Se ao
se referir a uma determinada classe, você diz por exemplo: “minha classe tem as
informações do cliente e salva o mesmo no banco de dados” perceba que o “e” na frase,
indica mais de uma responsabilidade, ferindo assim o SRP( single responsability
rpinciple ).

Aplicar o princípio da responsabilidade única, é importante para uma arquitetura


madura e sustentável. Quando começamos a dar valor a princípios como este, vemos
que estamos amadurecendo e fazendo melhor o que gostamos de fazer: software. Bom
pessoal espero que eu possa ter ajudado, e colaborado em algo com o crescimento
profissional de cada leitor deste artigo. Um abraço e até a próxima.

SOLID: Padrões
flexíveis para suas
classes C#
Facebook Twitter
(19) (0)

Aprenda com esse artigo a criar suas classes obedecendo estes princípios de
orientação a objetos.

Artigo no estilo Mentoring (saiba mais)

Fique por dentro


Neste artigo veremos importantes princípios da orientação a objetos que farão com que os
softwares sejam mais flexíveis, de fácil manutenção e fácil entendimento do código.
Aprenderemos a aplicar os cinco princípios SOLID com o C# em situações bem comuns do dia a
dia de cada desenvolvedor.

Veremos as vantagens e as maneiras corretas de aplicar este princípio passo a passo.


Ainda veremos como eles se relacionam com vários dos vinte três Designs Patterns do
GoF, através de exemplos práticos de aplicação destes princípios, com situações reais
que todo desenvolvedor enfrenta no dia a dia de seu trabalho.

Uma das maiores dificuldades nos dias atuais em termos de desenvolvimento de


software se refere à manutenção de sistemas computacionais. Pesquisas indicam que as
empresas gastam mais de 80% do orçamento destinado ao software em manutenção.
Isso ocorre porque na maioria dos desenvolvedores de sistemas não seguem os
princípios da orientação a objetos e não utilizam os padrões de projetos e técnicas
corretas de desenvolvimento.

São sistemas mal codificados que o tornam difícil de manter e evoluir. Outros ainda
utilizam outros paradigmas, como a programação estruturada, que demanda muito mais
manutenção que o paradigma orientado a objetos.

Em sistemas de informação que adotam o paradigma orientado a objetos, existem vários


padrões, princípios e técnicas que o desenvolvedor deve seguir para que o sistema seja
de fácil entendimento para outros desenvolvedores, de fácil manutenção após o sistema
estar em ambiente de produção, que mudanças e novas funcionalidades não causem
impacto em todo o sistema já existente.

Para um melhor entendimento sobre padrões de projetos é necessário um sólido


conhecimento de orientação a objetos, do contrário, os padrões não serão bem
compreendidos e o leitor acaba não sabendo aplicar estes no seu dia a dia.

Os princípios SOLID são a base para vários padrões de projetos criados e tornam
softwares mais evolutivos, de fácil manutenção e mudanças efetuadas depois de
finalizado, não impactando em outras áreas do programa, mudanças não propagam erros
por outras partes desse sistema.

Muitos desenvolvedores discutem sobre os padrões de projeto de software, alguns


dizem que os eles servem simplesmente para resolver problemas das linguagens de
programação orientadas a objetos e que outras linguagens, com mais recursos, não
precisam implementar nenhum desses padrões, mas esse não é um tópico abordado
neste artigo.

Mas o que todos não discutem é que os princípios SOLID da orientação a objetos são a
base para todo e qualquer projeto que siga este paradigma de desenvolvimento.

Com certeza, um código que não segue estes princípios não é de qualidade e um
desenvolvedor ao olhar para esse código percebe isso. Existem várias dicas, macetes e
técnicas para aplicação de todos estes princípios.

Para um código limpo e de qualidade, ele deve estar bem modularizado, cada classe
deve ter a sua responsabilidade e as relações entre essas classes devem ser claras e bem
definidas. É aqui que entra a ideia de código sólido, de onde vem o tema deste artigo.

Antes de entrarmos no estudo destes princípios, veremos quais os principais problemas


encontrados em sistemas e que poderiam ser evitados com a aplicação de padrões de
projetos e princípios de orientação a objetos (BOX 1):

· Rigidez: o sistema foi desenvolvido de forma que é muito difícil de mudar, qualquer
alteração provoca uma cascata de operações por todo o restante do sistema;

· Imobilidade: a codificação foi feita de forma que o reuso é muito difícil, nenhuma
parte do sistema pode ser reaproveitada em outro sistema;
· Fragilidade: o sistema foi feito de maneira que qualquer mudança o desestabiliza e o
torna inoperante;

· Complexidade: o sistema foi desenvolvido utilizando-se de muitos padrões para


resolver problemas simples, algo desnecessário (dependendo do caso);

· Repetição: mesmos trechos de códigos espalhados por todo o sistema, ao invés de


estarem encapsulados em um método ou classe.

Aplicar boas práticas de engenharia de software nos rende muitos benefícios como
manutenção facilitada, código organizado, dependências mais leves e uma arquitetura
completamente aberta a receber atualizações, melhorias e novos recursos.

Além disso, quando temos o sistema com várias classes desempenhando bem o seu
papel e cada método tendo o seu objetivo bem definido, ficam muito mais fáceis de ser
realizados os testes unitários do sistema.

BOX 1. Padrões de Projeto GoF

Padrões de Projeto do GoF tem como objetivo solucionar problemas comuns de


software relacionados as orientação a objetos.

Ganharam este nome de GoF, pois foram concebidos num livro de 1995 chamado
Design Patterns: Elements of Reusable Object-Oriented Software por Erich Gamma,
Richard Helm, Ralph Johnson e John Vlissides, a chamada Gangue of Four. São
organizados em três grupos:

· Padrões de criação: relacionados à criação de objetos (Abstract Factory, Builder,


Factory Method, Prototype, Singleton);

· Padrões estruturais: tratam das associações entre classes e objetos (Adapter, Bridge,
Composite, Decorator, Facade, Flyweight, Proxy).

· Padrões comportamentais: tratam das interações e divisões de responsabilidades entre


as classes ou objetos (Chain of Responsibility, Command, Interpreter, Iterator,
Mediator, Memento, Observer, State, Strategy, Template Method, Visitor).

Princípios SOLID
SOLID são cinco princípios básicos que todos os analistas e desenvolvedores deveriam
seguir para uma boa modelagem de classes. Cada letra da palavra SOLID representa um
princípio, são eles:

· S – Single Responsibility Principle (SRP): princípio da responsabilidade única;

· O – Open Closed Principle (OCP): princípio do aberto/fechado;

· L – Liskov Substituition Principle (LSP): princípio da substituição de Liskov;


· I – Inteface Segregation Principle (ISP): princípio da segregação de interfaces;

· D – Dependency Inversion Principle (DIP): princípio da inversão de dependência.

SRP (Sigle Responsibility Principle)


O princí" [...]

Open Closed Principle: Esse princípio determina que uma classe deve ser fechada
para modificações e aberta para extensões. Sobre ele, recomendamos os posts:

Open Closed Principle


Facebook Twitter
(0) (0)

O princípio open Close ou aberto/fechado, é um dos princípios SOLID, O


princípio do aberto/fechado nos auxilia a desenhar um software que seja fácil
de modificar, e que não sofre com o impacto das mesmas.

A uma das grande dificuldades hoje em dia em termos de desenvolvimento de software,


esta relacionada a manutenção. Segundo pesquisas, 80% do investimento feito em software
está relacionado ao custo da manutenção.O fato de muitos softwares serem construindo sem
boa práticas e padrões adequados, ou até mesmo, construídos sem uma sólida arquitetura;
acaba por se ter um produto mal feito, um verdadeiro “elefante branco”. Mais ai fica a
pergunta: o que torna um software difícil de se evoluir ou manter? Além dos problemas
citados anteriormente, o que ocorre, é que o código mal escrito faz com que uma pequena
alteração cause outros erros, e isto quando ao solucionar um problema não surja outro. O
padrão Open Close Princíple, um dos princípios SOLID, diz o seguinte: “Entidades de software (
classes, métodos, módulo, etc ) devem estar abertas para extensão, mas fechadas para
modificação”.

A afirmação acima, só pode ser mantida se o software tiver um design concebido sob os
pilares das boas praticas de desenvolvimento de software, e isto incluir aplicar padrões e
princípios que deem base para um softwares que tenha uma base sólida. Veja o exemplo
abaixo, sem ter sido considerado o princípo do aberto/fechado:

Veja que no código acima, temos muitos problemas de design. O principal é que, caso seja
feita uma modificação no cálculo do salário, seja a inclusão de alguma fórmula adicional ou
qualquer outro fator, pode causar comportamentos inesperados para quem usa esta classe,
até mesmo um bug. Observe agora o cenário do exemplo acima, aplicando o Open Close
Principle:

Diagrama
Exemplo de código:

Note que no exemplo aciuma, a base não muda, ou seja, é fechada para modificação;
passando assim ser de responsabilidade das classe herdeiras, implementarem o
comportamento de acordo com um determinado cenário. Implementando um design de
arquitetura, tendo em mente o princípio do aberto/fechado, o impacto das mudanças reduz
drasticamente. Fazendo com que tenhamos um software flexível, robusto e aceita as
mudanças, ao invés de brigar com elas. O exemplo acima deixa claro isto, e uma vez sendo
alterado o cálculo do salário do analista, como o do gerente, a base se manter imutável
diminuindo a possibilidade de bugs no sistema.

Open/Closed Principle:
Estenda
comportamentos sem
alterar o código
Facebook Twitter
(0) (0)

Melhorias de código e design de software usando algumas técnicas da


orientação a objetos e principalmente o princípio Aberto/Fechado (Open/Closed
principle - OCP) para que possamos estender o comportamento de uma classe
sem modificá-la.

De que se trata o artigo

Melhorias de código e design de software usando algumas técnicas da orientação a


objetos e principalmente o princípio Aberto/Fechado (Open/Closed principle - OCP)
para que possamos estender o comportamento de uma classe sem modificá-la e também
sem comprometer o código-fonte que já foi implementado e testado.

Em que situação o tema é útil

Em qualquer situação onde é necessário introduzir novas funcionalidades em um


sistema ou código já existente, equipes de manutenção de software que precisam fazer
alterações sem que isso introduza algum tipo de erro, manutenção de código legado e
melhorias de design tendo como objetivos a criação de classes que sejam extensíveis,
mas, que não permitam serem modificadas.
Open/Closed Principle

Mudanças ou novos requisitos em projetos de software são sempre constantes e para


isso nós alteramos o nosso código-fonte para que nosso software possa atender aos
nossos clientes e usuários. O grande problema é que muito possivelmente a nossa
aplicação já esteja testada e em produção. Estas novas alterações têm a possibilidade de
introduzir erros. Para que isto não ocorra, podemos usar algumas técnicas que nos
ajudam a abordar esse tipo de problema, de modo que as alterações sejam feitas de
forma consciente e sem quebrar o que já está construído.

Uma simples mudança em uma parte de um software pode resultar em atualizações em


inúmeros pontos da aplicação que fazem uso deste trecho de código que foi modificado,
ou seja, existe um alto nível de dependência entre a classe modificada e os usuários que
a utilizam. Na verdade, isto é considerado como sendo um design mal feito, de difícil
manutenção e nem um pouco extensível, já que esta mudança deveria ser transparente
para todos aqueles que dependem deste código modificado.

Por outro lado, a cada mudança feita há sempre uma possibilidade de introduzir
comportamentos inesperados (bugs) na aplicação, que podem passar despercebidos
durante nossos testes e, com isso, muito possivelmente estes bugs sejam descobertos
somente quando o software estiver em produção.

Por mais estranhas que as possibilidades comentadas possam parecer, é muito comum
nos depararmos com estes tipos de situações durante o desenvolvimento de um
software. Mas, como garantir que modificações em um software sejam feitas de forma
segura e da maneira mais correta possível? Como nosso código pode ser projetado de
forma que todas as outras partes não precisam nem ficar sabendo desta alteração?

Infelizmente, não existe uma fórmula mágica, mas, existe um conjunto de boas práticas
que devem sempre ser seguidas. Dentre este conjunto de boas práticas, podemos citar:

· Uso de padrões de projeto (ou do inglês Design Patterns)

· Desenvolvimento dirigido por testes (ou do inglês Test Driven Development – TDD)

· Não adicionar complexidade desnecessária ao nosso código (princípio YAGNI)

· Manter as coisas sempre o mais simples possível (princípio KISS)

Nota do DevMan

Test Driven Development (TDD) – é uma técnica de desenvolvimento de software na


qual um desenvolvedor escreve um teste automatizado que defina uma melhoria,
correção ou uma nova funcionalidade. Após isso, é feito o código a ser avaliado pelo
teste e consequentemente validado. Posteriormente, o código é refatorado e testado
novamente com intuito de checarmos se esta refatoração não alterou o comportamento
do código escrito anteriormente.
Na verdade o nome TDD é um nome ruim, já que ele utiliza testes para guiar o
desenvolvimento da aplicação, mas o foco não são os testes e sim o design da aplicação.
Criando testes antes da escrita do código fornece a visão de como os outros irão utilizar
o método a ser desenvolvido (semelhante ao uso de uma API), já que estamos
desenvolvendo de fora para dentro (dos testes para dentro da implementação).

Nota do DevMan

Princípio YAGNI – Esta abreviação quer dizer You Ain´t Gonna Need It e defende que
não devemos adicionar funcionalidades ao nosso código até que elas sejam realmente
necessárias. Este princípio tem como principal função fazer com que o requisito de
negócios seja satisfeito, nenhuma linha de código a mais deve ser adicionada.

Nota do DevMan

Princípio KISS – Esta abreviação em inglês que significa Keep It Simple, Stupid! e diz
que devemos valorizar a simplicidade do código eliminando toda a complexidade
desnecessária. Este princípio teve a sua inspiração diretamente do princípio da Navalha
de Occam e das máximas de Albert Einstein ("tudo deve ser feito da forma mais simples
possível, mas não mais simples que isso").

Classes Abstratas
Quando criamos nossas classes é comum percebermos que algumas delas possuem
alguns métodos em comum (por exemplo: andar, voar ou correr) e que estes métodos
podem se comportar de forma idêntica, ou assumir um comportamento diferente em
cada classe que o contém.

As classes abstratas são a base do modelo de abstração na programação orientada a


objetos. Uma classe abstrata representa um modelo abstrato para outras classes. Através
da herança, este modelo permite reaproveitar métodos já implementados, possuindo
assim um comportamento igual neste caso, e também fazer com que as classes derivadas
definam as suas próprias versões de um mesmo método, possuindo assim um
comportamento diferente neste outro caso.

A abstração é um poderoso recurso que faz com que os usuários de uma classe não
precisem conhecer a sua implementação concreta, ao invés disso, apenas conhecer o
modelo abstrato que possui o método em que ela necessita, não importando como as
diferentes classes se comportam em relação a este método.

"

Liskov Substitution Principle: Esse princípio está relacionado à herança e

indica que devemos ser capazes de substituir a classe filha pela classe pai sem
que o sistema apresente problemas;
Interface Segregation Principle: Já o princípio da segregação dita que

precisamos planejar interfaces específicas, com propósito bem determinado, de

modo que quando implementada a classe que o faz não precise codificar
métodos desnecessários;

Dependency Inversion Principle: Por fim, o princípio da inversão de

dependência sinaliza que o recomendado é depender apenas de classes


abstratas, e não de classes concretas.

Para aprender sobre todos esses princípios, acesse o conteúdo a seguir:

SOLID: Padrões
flexíveis para suas
classes C#
Facebook Twitter
(19) (0)

Aprenda com esse artigo a criar suas classes obedecendo estes princípios de
orientação a objetos.

Artigo no estilo Mentoring (saiba mais)

Fique por dentro


Neste artigo veremos importantes princípios da orientação a objetos que farão com que os
softwares sejam mais flexíveis, de fácil manutenção e fácil entendimento do código.
Aprenderemos a aplicar os cinco princípios SOLID com o C# em situações bem comuns do dia a
dia de cada desenvolvedor.
Veremos as vantagens e as maneiras corretas de aplicar este princípio passo a passo.
Ainda veremos como eles se relacionam com vários dos vinte três Designs Patterns do
GoF, através de exemplos práticos de aplicação destes princípios, com situações reais
que todo desenvolvedor enfrenta no dia a dia de seu trabalho.

Uma das maiores dificuldades nos dias atuais em termos de desenvolvimento de


software se refere à manutenção de sistemas computacionais. Pesquisas indicam que as
empresas gastam mais de 80% do orçamento destinado ao software em manutenção.

Isso ocorre porque na maioria dos desenvolvedores de sistemas não seguem os


princípios da orientação a objetos e não utilizam os padrões de projetos e técnicas
corretas de desenvolvimento.

São sistemas mal codificados que o tornam difícil de manter e evoluir. Outros ainda
utilizam outros paradigmas, como a programação estruturada, que demanda muito mais
manutenção que o paradigma orientado a objetos.

Em sistemas de informação que adotam o paradigma orientado a objetos, existem vários


padrões, princípios e técnicas que o desenvolvedor deve seguir para que o sistema seja
de fácil entendimento para outros desenvolvedores, de fácil manutenção após o sistema
estar em ambiente de produção, que mudanças e novas funcionalidades não causem
impacto em todo o sistema já existente.

Para um melhor entendimento sobre padrões de projetos é necessário um sólido


conhecimento de orientação a objetos, do contrário, os padrões não serão bem
compreendidos e o leitor acaba não sabendo aplicar estes no seu dia a dia.

Os princípios SOLID são a base para vários padrões de projetos criados e tornam
softwares mais evolutivos, de fácil manutenção e mudanças efetuadas depois de
finalizado, não impactando em outras áreas do programa, mudanças não propagam erros
por outras partes desse sistema.

Muitos desenvolvedores discutem sobre os padrões de projeto de software, alguns


dizem que os eles servem simplesmente para resolver problemas das linguagens de
programação orientadas a objetos e que outras linguagens, com mais recursos, não
precisam implementar nenhum desses padrões, mas esse não é um tópico abordado
neste artigo.

Mas o que todos não discutem é que os princípios SOLID da orientação a objetos são a
base para todo e qualquer projeto que siga este paradigma de desenvolvimento.

Com certeza, um código que não segue estes princípios não é de qualidade e um
desenvolvedor ao olhar para esse código percebe isso. Existem várias dicas, macetes e
técnicas para aplicação de todos estes princípios.

Para um código limpo e de qualidade, ele deve estar bem modularizado, cada classe
deve ter a sua responsabilidade e as relações entre essas classes devem ser claras e bem
definidas. É aqui que entra a ideia de código sólido, de onde vem o tema deste artigo.
Antes de entrarmos no estudo destes princípios, veremos quais os principais problemas
encontrados em sistemas e que poderiam ser evitados com a aplicação de padrões de
projetos e princípios de orientação a objetos (BOX 1):

· Rigidez: o sistema foi desenvolvido de forma que é muito difícil de mudar, qualquer
alteração provoca uma cascata de operações por todo o restante do sistema;

· Imobilidade: a codificação foi feita de forma que o reuso é muito difícil, nenhuma
parte do sistema pode ser reaproveitada em outro sistema;

· Fragilidade: o sistema foi feito de maneira que qualquer mudança o desestabiliza e o


torna inoperante;

· Complexidade: o sistema foi desenvolvido utilizando-se de muitos padrões para


resolver problemas simples, algo desnecessário (dependendo do caso);

· Repetição: mesmos trechos de códigos espalhados por todo o sistema, ao invés de


estarem encapsulados em um método ou classe.

Aplicar boas práticas de engenharia de software nos rende muitos benefícios como
manutenção facilitada, código organizado, dependências mais leves e uma arquitetura
completamente aberta a receber atualizações, melhorias e novos recursos.

Além disso, quando temos o sistema com várias classes desempenhando bem o seu
papel e cada método tendo o seu objetivo bem definido, ficam muito mais fáceis de ser
realizados os testes unitários do sistema.

BOX 1. Padrões de Projeto GoF

Padrões de Projeto do GoF tem como objetivo solucionar problemas comuns de


software relacionados as orientação a objetos.

Ganharam este nome de GoF, pois foram concebidos num livro de 1995 chamado
Design Patterns: Elements of Reusable Object-Oriented Software por Erich Gamma,
Richard Helm, Ralph Johnson e John Vlissides, a chamada Gangue of Four. São
organizados em três grupos:

· Padrões de criação: relacionados à criação de objetos (Abstract Factory, Builder,


Factory Method, Prototype, Singleton);

· Padrões estruturais: tratam das associações entre classes e objetos (Adapter, Bridge,
Composite, Decorator, Facade, Flyweight, Proxy).

· Padrões comportamentais: tratam das interações e divisões de responsabilidades entre


as classes ou objetos (Chain of Responsibility, Command, Interpreter, Iterator,
Mediator, Memento, Observer, State, Strategy, Template Method, Visitor).

Princípios SOLID
SOLID são cinco princípios básicos que todos os analistas e desenvolvedores deveriam
seguir para uma boa modelagem de classes. Cada letra da palavra SOLID representa um
princípio, são eles:

· S – Single Responsibility Principle (SRP): princípio da responsabilidade única;

· O – Open Closed Principle (OCP): princípio do aberto/fechado;

· L – Liskov Substituition Principle (LSP): princípio da substituição de Liskov;

· I – Inteface Segregation Principle (ISP): princípio da segregação de interfaces;

· D – Dependency Inversion Principle (DIP): princípio da inversão de dependência.

SRP (Sigle Responsibility Principle)


O princí" [...]

Orientação a Objetos na prática


Agora que você já conhece os conceitos, que tal colocá-los em prática

programando com a linguagem C#? Os artigos e o DevCast a seguir lhe

ensinarão como aplicar os conceitos da Orientação a Objetos com os recursos


do C#:

Como criar minha


primeira classe em C#
Facebook Twitter
(25) (0)

Neste conteúdo você aprenderá a criar sua primeira classe na linguagem C#.
Aprenda também a usar herança e interfaces, bem como métodos, atributos e
propriedades.

Ir para o código

A definição de classes na linguagem C# é feita com uma sintaxe simples, de fácil


compreensão e nos permite criar atributos, propriedades e métodos. Por exemplo,
na Figura 1 temos a representação de uma classe chamada Produto com seus campos e
logo em seguida vemos como essa classe seria implementada em C#:

Figura 1. Diagrama da classe Produto

1
public class Produto
2
{
3 private int codigo;
4 private string nome;

5 private decimal preco;

7 public int Codigo { get => codigo; set => codigo = value; }

public string Nome { get => nome; set => nome = value; }
8
public decimal Preco { get => preco; set => preco = value; }
9
}
10

Linha 1: Nessa linha temos os seguintes termos: public define a visibilidade da classe como
pública; class define que estamos criando uma classe; e Produto é o nome da classe.

Linhas 3 a 5: Aqui temos o que chamamos de atributos. São variáveis privadas que representam as características
da classe. Note o modificador de acesso private, o tipo e o nome de cada atributo;

Linhas 7 a 9: Já aqui temos as propriedades da classe, que são públicas e encapsulam os atributos. Observe que
para cada atributo há uma propriedade e que no corpo delas definimos os métodos get e set. O get retorna o
atributo correspondente e o set recebe um valor e o repassa para o atributo.
No corpo de cada propriedade usamos expressões lambda para definir os
métodos get e set. Essa sintaxe é equivalente à seguinte:

1 public int Codigo


2 {

3 get { return codigo; }

4 set { codigo = value; }

}
5

O parâmetro value possui o mesmo tipo da propriedade e é recebido de forma


implícita no método get, sem que seja necessário declará-lo. Isso é equivalente a criar,
por exemplo, os métodos getCodigo e setCodigo, como fazemos em outras
linguagens:

1
public int getCodigo
2 {
3 return codigo;
4 }

6 public void setCodigo(int value)

7 {

codigo = value;
8
}
9

Herança

Herança é um tipo de relacionamento muito comum na orientação a objetos e ocorre


quando uma classe descende da outra e herda suas características e comportamentos,
além de implementar os seus próprios. Por exemplo, considere o diagrama de classes
da Figura 2 em que Assinatura herda de Produto.

Figura 2. Diagrama
classes com Assinatura herdando de Produto
Nesse cenário a classe Assinatura herda de Produto e também define uma propriedade e
método próprios. Em C# essa classe seria implementada da seguinte forma:

1
public class Assinatura : Produto
2
{
3 private DateTime dataExpiracao;
4

5 public DateTime DataExpiracao { get => dataExpiracao;


6 set => dataExpiracao = value; }

8 public TimeSpan GetTempoRestante()

9 {

return dataExpiracao - DateTime.Today;


10
}
11
}
12

Linha 1: A herança em C# é representada pelos dois pontos na definição da classe, seguido do nome da classe que
está sendo herdada. Nesse caso, Assinatura herda de Produto;

Linhas 3 a 6: Nesse trecho temos o atributo e propriedade que dizem respeito apenas à assinatura;

Linhas 8 a 11: Aqui temos o método GetTempoRestante da assinatura, que retorna um TimeSpan
representando o tempo que falta até a assinatura expirar;

Interfaces

Na Orientação a Objetos as interfaces funcionam como contratos, ou seja, elas definem


comportamentos que devem ser cumpridos pelas classes. Quando uma classe atende a
uma interface, dizemos que ela implementa essa interface. Na Figura 3 temos um
diagrama que mostra uma nova configuração para as classes que representamos
anteriormente:
Figura 3. Diagrama
de classes com interface

Nesse cenário a interface IExpiravel define que toda classe que representa um
produto cuja data de expiração ou validade chega ao fim (expira) deve implementar o
método GetTempoRestante. Por exemplo, se tivéssemos outra classe Voucher ou
Desconto, por exemplo, ela poderia implementar essa classe e definir o comportamento
desse método.

Em C# a interface IExpiravel seria escrita da seguinte forma:

1 public interface IExpiravel


2 {

3 TimeSpan GetTempoRestante();

4 }

Linha 1: Note o uso da palavra reservada interface e também no nome da interface: IExpiravel. Em C#
convenciona-se iniciar o nome das interfaces com I (maiúsculo);

Linha 3: As interfaces definem apenas a assinatura do método, não seu comportamento. A implementação do
método fica por conta da classe que implementar essa interface.

Agora, considerando que a classe Assinatura implementa essa interface, seu código
seria modificado da seguinte forma:

public class Assinatura : Produto, IExpiravel


1
{
2
private DateTime dataExpiracao;
3

4
public DateTime DataExpiracao { get => dataExpiracao;
5
set => dataExpiracao = value; }
6
7 public TimeSpan GetTempoRestante()

8 {

return dataExpiracao - DateTime.Today;


9
}
10
}
11

12

Linha 1: A sintaxe para implementar uma interface em C# é a mesma que para herdar de outra classe:
usando dois pontos;

Linhas 8 a 11: Como a classe implementa a interface IExpiravel, ela deve obrigatoriamente definir o
comportamento do método GetTempoRestante.

A partir desses conceitos podemos criar diversas outras classes e interfaces, com seus
atributos, propriedades e métodos específicos.

C# Orientado a
Objetos: Introdução
Facebook Twitter
(22) (0)

Neste artigo será apresentado uma introdução a orientação a objetos de uma


forma geral em CSharp, em seguida colocaremos em prática os conhecimentos
teóricos adquiridos para a linguagem de programação C#.

A orientação a objetos é uma ponte entre o mundo real e virtual, a partir desta é possível
transcrever a forma como enxergamos os elementos do mundo real em código fonte, a
fim de nos possibilitar a construção de sistemas complexos baseados em objetos.

O primeiro passo para utilizar o paradigma da orientação a objetos é perceber o universo


em que se deseja trabalhar, uma vez que possuímos essa informação é necessário
descrever a estrutura desses elementos, ou seja quais são as características mais
marcantes destes para o desenvolvimento do sistema proposto.

Vejamos como descrever um objeto “Pessoa”. As perguntas a serem feitas são: “O que é
uma pessoa?”,”Quais são as características de uma pessoa?”,”Como uma pessoa se
comporta?”. Após responder a este questionário, teremos condições de modelar o objeto
da forma como queremos. Vamos responder essas perguntas:

· O que é uma pessoa?

Resposta: A pessoa é um ser do mundo real que interage com toda a natureza.

· Quais são as características de uma pessoa?

Resposta: Uma pessoa possui: nome, olhos, boca, braços, pernas, cabelos e etc.

· Como uma pessoa se comporta?

Resposta: Uma pessoa corre, anda, fala, pula, come e etc.

Nota: Os objetos a serem implementados no sistema devem obedecer as regras de


negócio solicitadas pelo cliente. O analista deve perceber a partir da explicação do
usuário o que realmente é necessário e perceber a responsabilidade que os objetos terão.

Agora que já possuímos as informações do que realmente representa um objeto do tipo


pessoa, precisaremos passar esse conhecimento utilizando uma linguagem de
programação; é claro que qualquer linguagem que se utilize da orientação a objetos,
neste artigo será exemplificado com C#.

Nota: O suporte a orientação a objetos está em muitas linguagens, no entanto a sintaxe e


as funções das linguagens são diferentes, ou seja, caso esteja utilizando o Java ou o PHP
é necessário verificar as particularidades destes para continuar com o artigo.

Principais conceitos da OO
Projetamos o nosso elemento do mundo real, agora como iremos traduzir isto para o
código fonte? Antes disso é preciso compreender o que foi feito até agora veja os
passos:

· Descrever uma estrutura de uma pessoa

· Descrever as características de uma pessoa

· Descrever o comportamento de uma pessoa

Quando descrevemos de estrutura de um elemento, na verdade criamos uma


especificação básica do que todo elemento daquele tipo deve ter, isso se
chama Classe. Observe a Listagem 1.

Listagem 1. Classe C# de pessoa


class Pessoa

Tudo que faz parte de uma pessoa deve estar contido neste bloco de chaves.

Uma pessoa possui características que as diferem de outros seres do mundo real, isso no
mundo da programação é chamado de Atributos (Listagem 2).

Listagem 2. Atributos C# de pessoa

class Pessoa

string nome;

int olhos,bracos,pernas;

string cor_olhos;

string cor_cabelos;

Nota: É uma boa prática declarar todos os atributos (variáveis) com o modificador de
acesso private.

Os atributos devem vir acompanhados dos tipos de dados e também dos modificadores
de acesso, neste caso não estamos declarando nenhum modificador de acesso, o C#
implicitamente introduz a palavra “private” antes dos atributos e métodos (veremos a
seguir). Classes só podem ser “public” ou “internal”. No Java, que possui uma estrutura
semelhante à linguagem C#, seria a palavra “default”.

Uma pessoa também possui um comportamento diferenciado de todos os outros


elementos da natureza, na POO isso é conceituado como métodos (Listagem 3).

Listagem 3. Métodos C#

class Pessoa
{

string nome;

int olhos, bracos, pernas;

string cor_olhos;

string cor_cabelos;

void andar(int velocidade)

// Ande um pouco

void falar()

// Converse mais

void comer()

// alimente-se mais

Finalmente criamos e modelamos o objeto. Isso pode parecer simples mas no entanto é
fundamental para que um projeto orientado a objetos tenha sucesso que o programador
de sistemas conheça os conceitos abordados anteriormente.
Refinando a classe “Pessoa”
Um dos pontos fortes da Orientação a objetos é o reuso de código, ou seja, permitir que
outras rotinas utilizem funções predefinidas no sistema. Ou seja, evitar escrever uma
rotina várias vezes em locais diferentes. A nossa classe pessoa possui alguns atributos e
métodos que podem ser utilizados por outras classes, no entanto falta um pequeno
detalhe para que essa comunicação seja realizada. Veja o seguinte exemplo da Figura 1.

Figura 1. Método inacessível

Para utilizar a classe Pessoa é necessária a criação de objetos vinculados a esta classe.
Para fazer isto seguimos os seguintes passos:

· Declarar a classe em que deseja que o objeto pertença;

· Crie um nome para o objeto, neste caso declaramos como ”p”;

· Acrescente o sinal de “=”, que neste caso não é uma comparação e sim uma atribuição;

· “new” para a criação de todo objeto;

Veja o resultado na Listagem 4.

Listagem 4. Objeto C#

Pessoa p = new Pessoa();

Veja que ao fazer a chamada ao método “falar()”, o IntelliSense não reconheceu este
procedimento, pois o nível de acessibilidade (como foi visto anteriormente) está
declarado de forma implícita como “private”. Uma forma de resolver este problema é
declarar o modificador de acesso public no início dos métodos, conforme a Listagem 5.

Listagem 5. Métodos públicos

class Pessoa

string nome;

int olhos, bracos, pernas;

string cor_olhos;

string cor_cabelos;

public void andar(int velocidade)

// Ande um pouco

public void falar()

// Converse mais

public void comer(string comida)

// alimente-se mais

}
}

Na classe Program.cs instancie (crie) um objeto do tipo de pessoa e cheque se o Visual


Studio reconhece os métodos e propriedades a partir do IntelliSense. Repare que os
métodos são reconhecidos, no entanto as propriedades (atributos) ainda continuam
invisíveis, identificamos que isso acontece por causa dos modificadores de acesso. O
nível de proteção está sendo restringido, isso na POO é definido
como encapsulamento.

Para resolvermos os níveis de acesso dos atributos seria muito mais fácil declararmos
todos como “public”, a visibilidade se estenderia a todos a classes da aplicação, no
entanto não é uma boa prática fazer isto, pois desta forma toda classe pode alterar os
atributos.

Uma forma elegante de tratar isto é manter os atributos como privados e utilizar os
“Getters and Setters”, como o próprio nome diz “Retornar e alterar”. O método “Get()”
será responsável por retornar alguma informação do objeto atual, enquanto o
“Set(param)” realizará a alteração do objeto. Vejamos a Listagem 6.

Listagem 6. Get and Set C#

public string getNome()

return nome; // Retorne o nome da pessoa

public void setNome(string nome)

this.nome = nome; //Altere o nome da pessoa

Essa é a estrutura básica dos Getters and setters em C#. Veja que no método getNome(),
nenhum parâmetro é especificado, apenas a palavra reservada “return” vem seguida do
atributo desejado, neste caso o “nome”.

No método setNome(string nome), ao contrário do método getNome(), declaramos um


parâmetro que é justamente o valor a ser alterado e não possui nenhum tipo de retorno,
por isso utilizamos a palavra “void”.
Nota: Veja que agora podemos manipular as variáveis através dos getters and setters,
mesmo os atributos declarados como “private”. Isto é uma boa prática utilizada em todo
projeto. Podemos ver que a única classe que pode alterar as variáveis é a classe que
possui os métodos get e set declarados.
Caso queira um atalho para esses métodos digite “prop” e em seguida aperte duas vezes
a tecla tab; será criado um get e set automático. A estrutura será igual a essa: public int
MyProperty{get;set ;}. Uma grande vantagem é que desta forma a variável é criada
implicitamente nessa propriedade.

Na Listagem 7 vemos a execução do projeto.

Listagem 7. Executando o projeto

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace POO

class Program

static void Main(string[] args)

Pessoa p = new Pessoa();

p.setNome("Tiago de Oliveira Vale");

Console.WriteLine("A pessoa: " + p.getNome() +


" foi criada com sucesso");

Console.ReadKey();

Execute o projeto e visualize a informação apresentada na tela de console. Primeiro foi


criado o objeto “p” do tipo Pessoa, em seguida realizamos uma chamada ao método
setNome(string nome), para alterar o nome do objeto e em seguinte o objeto chama o
método getNome() para retornar o nome da pessoa.

Inicialização dos objetos


Existem objetos que possuem valores padrões, ou seja, nós queremos que toda instância
de uma classe apresente os mesmos valores. No nosso exemplo declaramos variáveis de
tipos primitivos com os seguintes nomes: bracos, pernas, olhos.

Para fazer isto podemos utilizar os getters and setters, mas só isso não ia adiantar, seria
necessário declarar manualmente a quantidade de braços, olhos e pernas. Assumindo
que nosso sistema não terá ninguém com três olhos, dez pernas e cinco braços, faz-se
necessário a criação de um construtor.

O .NET nos presenteia com um construtor padrão que não recebe nenhum argumento e
ele é executado sempre que criamos um objeto na memória (Listagem 8).

Listagem 8. Construtor padrão

public Pessoa()

Observe que em nosso exemplo não criamos este construtor, no entanto se precisarmos
de mais construtores é necessário que especifique o “construtor padrão”. Todo
construtor deve possuir o mesmo nome da classe. Observe a Listagem 9.
Listagem 9. Classe Pessoa final com construtores

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace POO

public class Pessoa

public Pessoa(string cabelo)//Valor obrigatório

Olhos = 2;//Valor default

Bracos = 2;

Pernas = 2;

CorCabelo = cabelo;

public Pessoa() {

public string Nome { get; set; }


public int Olhos { get; set; }

public string CorCabelo { get; set; }

public int Bracos { get; set; }

public int Pernas { get; set; }

Veja como a estrutura da nossa classe está bem mais enxuta com os getters and setters
automáticos. Observe que em vez de apenas um construtor temos dois.

Agora veja a Listagem 10.

Listagem 10. Program.cs final

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace POO

class Program

static void Main(string[] args)


{

Pessoa p = new Pessoa();//UTILIZANDO CONSTRUTOR DEFAULT

p.Nome = "Tiago de Oliveira Vale";

p.Bracos = 2;

p.Pernas = 2;

p.Olhos = 2;

p.CorCabelo = "Preto";

Console.WriteLine(p.Nome + " possui " + p.Bracos +

" braços," + "\n" + p.Pernas + " pernas, \n " + p.Olhos


+

" olhos e cabelo " + p.CorCabelo+"\n");

Pessoa p1 = new Pessoa("Loiro");

//UTILIZANDO CONSTRUTOR PERSONALIZADO

p1.Nome = "Fulano";

Console.WriteLine(p1.Nome+" possui "+p1.Bracos+

" braços"+",\n"+p1.Pernas+" pernas, \n "+p1.Olhos+

" olhos e cabelo "+p1.CorCabelo);


Console.ReadKey();

Observe como reduzimos a quantidade de linhas de código com um construtor


personalizado, sem dúvidas a melhor saída para utilizar valores default na POO.

Na Figura 2 vemos o resultado do nosso exemplo.

Figura 2. Executando o projeto

Portanto, neste artigo abordamos os conceitos básicos de orientação a objetos,


aprendemos como modelar uma classe e algumas boas práticas adotadas pelo mercado,
como a utilização de getters and setters. Todos os exemplos foram utilizados com a
linguagem C#, mas os conceitos aprendidos são reaproveitados para outras linguagens
que suportam a orientação a objetos.
Orientação a Objetos
em .NET: uma
abordagem completa
Facebook Twitter
(31) (0)

Veja nesse artigo os conceitos fundamentais sobre o paradigma de


programação orientada a objetos.

Demais posts desta série:


Programação Orientada a Objetos: OO na Prática e Encapsulamento – Parte 2

Artigo no estilo Curso

Fique por dentro


Este artigo trata de fazer uma abordagem teórica e prática sobre os principais conceitos da
POO (Programação Orientada a Objetos), explorando temas importantes como o
encapsulamento, herança e polimorfismo. O leitor terá a oportunidade de conhecer os
paradigmas de programação que antecederam o principal paradigma usado atualmente, o
paradigma da programação orientação a objetos.

Este artigo também fala a respeito do surgimento da orientação a objetos, desde o


processo de abstração até o entendimento do objeto e suas características como a
identidade, propriedades, estado e comportamento, para que seja possível mostrar ao
leitor uma base inicial da orientação a objetos antes de mostrar exemplos práticos dos
principais recursos da POO com a linguagem de programação C#.

Esse artigo abordará assuntos importantes a respeito da programação orientada a


objetos. Aqui serão apresentados ao leitor os conceitos introdutórios à orientação a
objetos, como o conceito de objetos e suas representações gráficas através dos
diagramas da UML, chegando até a prática propriamente dita, onde iremos mostrar
exemplos de como pôr em prática os conceitos da OO com a linguagem de programação
C#.

Veremos como utilizar os conceitos de encapsulamento, herança, polimorfismo, dentre


outros assuntos inerentes.
Os paradigmas das linguagens de
programação
Antes de entrarmos no mundo da programação orientada a objetos, precisamos conhecer
um pouco sobre os paradigmas das linguagens de programação de computadores,
paradigmas estes que até os tempos atuais ainda dividem espaço junto à comunidade
desenvolvedora de software, buscando a resolução de problemas no mundo real, criando
soluções para os problemas de forma computadorizada, através do produto de software.

Antes da programação do software, o problema a ser revolvido deve ser analisado e


transformado em uma documentação contendo aspectos fundamentais ao domínio do
negócio e de sua solução, documentação esta que posteriormente servirá como base para
desenvolver o produto de software, ou sistema computacional, que tenha como objetivo
principal a resolução do problema central e assim possa cumprir com seu papel,
atendendo a todas as necessidades a que se propõe.

Mas a final, o que é o tal paradigma? Podemos conceituar um paradigma como sendo
uma visão, ou um ponto de vista, do mundo real (da realidade em que vivemos) e a
forma de atuação sobre tal concepção. Resumindo, é a forma como o analista e o
programador lidam com um determinado problema na busca de uma solução em forma
de sistema de software.

Os paradigmas são classificados em imperativo, estruturado, funcional, lógico e


orientado a objetos. Cada qual possui seus conceitos e métodos de como abordar um
determinado problema do mundo real para elaborar e propor uma solução para o
mesmo.

Há seguir conheceremos um pouco sobre os paradigmas das linguagens de


programação:

· Paradigma Imperativo: o paradigma imperativo foi o primeiro paradigma a existir.


Também é conhecido como paradigma procedural e trata de resolver um problema com
comandos (instruções) um após o outro, até sua solução, ou seja, de forma sequencial.

A arquitetura de computadores exerce uma forte influência para projetar uma linguagem
de programação, assim sendo, as linguagens consideradas imperativas têm como base a
tão conhecida arquitetura de computadores de Von Neumann.

A linguagem de programação imperativa tem algumas características como: as variáveis


que reservam os espaços na memória, comandos para atribuição e transferência de
dados, execução sequencial das instruções e a possibilidade de repetição de blocos de
instrução.

Podemos considerar como linguagens imperativas o ALGOL, Assembler, Basic, Pascal,


Cobol, C, Fortran e etc.

· Paradigma Estruturado: este paradigma é caracterizado pela forma de programação


que deve ser composta por três estruturas simples: sequência, decisão e iteração. Na
prática, é uma programação modular, onde a estrutura de um programa deve ser simples
e usar funções e sub-rotinas.

Cobol, Pascal, Basic e C são exemplos de linguagem de programação estruturada.

· Paradigma Funcional: o propósito deste paradigma é dividir um problema em partes


menores (funções) que recebem dados de entrada e retornam dados na saídas para a base
chamadora.

Dessa forma, este paradigma busca na programação resolver o problema central através
da divisão do problema em partes menores e, ao final do processamento, mostrar o
resultado. A linguagem LISP é baseada no modelo funcional.

· Paradigma Lógico (declarativo): para definir este paradigma precisamos fazer uma
pergunta que o precede: Qual o problema? O mesmo trata de descobrir um algoritmo
geral para res" [...]

Orientação a Objetos
em .NET: uma
abordagem completa –
Parte 2
Facebook Twitter
(9) (0)

Veja neste artigo a prática da Programação Orientada a Objetos, criando uma


classe a partir de um diagrama UML, definindo seus atributos e também como
funciona o encapsulamento.

Demais posts desta série:


Programação Orientada a Objetos: Conceitos fundamentais - Parte 1

Artigo no estilo Curso


Fique por dentro
Este artigo é importante, pois mostra em detalhes os principais conceitos da programação
orientada a objetos (POO). Assim, o leitor terá uma base mais sólida para iniciar ou seguir sua
carreira na programação de sistemas orientado a objetos.

A utilização da O.O. na construção de um software o torna mais próximo da


compreensão do problema do mundo real a ser resolvido através de uma solução
computacional, pois o software é modularizado através de suas classes com
responsabilidades distintas, sendo que cada classe representa aspectos importantes da
entidade da realidade em se detêm o contexto do problema.

No primeiro artigo sobre a POO focamos em uma introdução aos paradigmas das
linguagens de programação, introdução a orientação a objetos e conhecemos um pouco
da POO e sua estrutura e como funciona no mundo real.

Neste segundo artigo iremos de fato aprender a programação orientada a objetos, ou


seja, criar uma classe a partir de um diagrama UML, definir seus atributos, criar e
simular a implementação de métodos, entender os modificadores de acesso, o
encapsulamento, gets e sets e por fim instanciaremos um objeto fazendo uso de seus
recursos em aplicação Windows Forms.

Para isso, será demonstrado o funcionamento POO através da linguagem de


programação C#.

O mais interessante na POO é poder capturar objetos e características das entidades e


assim iniciar o processo modelagem das classes que irão compor um sistema. Este
conjunto de classes troca mensagens entre si, e é importante ressaltar que um
programador faz uso das boas práticas que a OO nós trás e deixa de lado o vício de ficar
criando e reinventando código a cada novo projeto que surge.

Uma equipe de desenvolvimento que se presa faz uso de boas bibliotecas de código que
já deram certo em projetos passados, melhorando o tempo de produção do sistema.

De agora em diante iremos analisar representações de um sistema orientado a objetos


através da modelagem UML e codificar o mesmo em um sistema real com o Visual
Studio 2012 e linguagem de programação C# da Microsoft.

Composição e codificação de uma classe


Uma classe representa uma entidade do mundo real, seja algo concreto ou abstrato.
Assim, precisamos abstrair pontos importantes da entidade que fazem sentido na
proposta do software a que se pretende construir. As abstrações retiradas das entidades
devem compor a estrutura da classe formando seus atributos e métodos.

A seguir iremos detalhar algumas das características importantes que o leitor deve ter
conhecimento a respeito de uma classe. Para isso, teremos como base uma classe
representada através do diagrama de classes da UML, conforme a Figura 1, pois trata
de representar a base para criar um objeto produto ou um conjunto deles de forma
abstrata para que possamos manipular informações de produtos do mundo real em
nossos sistema computacional.

Perceba na representação que temos vários atributos como o nome do produto, preço de
venda, data de cadastro, e cada atributo é de um determinado tipo de dado como um
inteiro, sequência de caracteres string, número decimal, valor booleano e um tipo que
representa data e hora na linguagem de programação C#.

Figura 1. Classe produto representação com diagrama UML

Ainda observando a classe produto da Figura 1 temos os métodos que representam as


operações que a classe pode executar, neste contexto temos quatro operações que
representa um CRUD (BOX 1) para a manipulação de dados em um banco de dados
qualquer.

BOX 1. CRUD

O CRUD é um acrônimo de create, read, update e delete, que são quatro operações
básicas de inclusão, consulta, alteração e exclusão de dados em um SGDB. Estas
operações geralmente são realizadas em banco de dados relacionais através de
transações enviadas por um cliente, ou seja, um software pode manipular dados entre o
banco de dados onde estão armazenados os dados e a exibição em UI para o usuário
final, pois a interface de usuário pode permitir ao mesmo realizar as operações CRUD
de forma simples e eficaz.

Para tornar este artigo bem produtivo, daqui para frente usaremos duas ferramentas para
exemplificar a estrutura da POO. Neste caso iremos usar o Visual Studio 2012 para criar
um projeto e codificar o mesmo em C# e também será disponível para download o
projeto UML criada com a ferramenta Astah Community no qual pode ser baixado no
link que se encontra na sessão Links ao final do artigo.

Em projetos de software bem organizados e estruturados, a codificação das classes de


um sistema é criada a partir de diagramas de classes da UML, que é uma linguagem de
modelagem bem difundida há anos e que auxilia na construção de sistemas e seus
diagramas fazem parte da documentação.

Os conceitos da UML são bem aplicados dentro da engenharia de software etapa esta
que antecede a codificação. Geralmente, a documentação inicial do software tem
diagramas de caso de uso que representam as funcionalidades do sistema a que se
pretende construir.

Os diagramas UML que representam as características do software são criados por um


analista de negócio, analista de sistemas ou pelo um arquiteto de software.

Para codificar um software e iniciar a criação de suas classes é necessário escolher uma
linguagem de programação e a ferramenta IDE para ajudar a automatizar parte das
tarefas durante o processo de desenvolvimento como, por exemplo, a compilação do
software, que é o processo que transforma o código fonte escrito em um código que
possa ser entendível pelo hardware e assim execute o programa construído em cima
sistema operacional.

Partindo deste princípio uma linguagem de programação segue uma sintaxe, que são
regras a serem seguidas na escrita da codificação do software, ou seja, os comandos da
própria linguagem de programação seguem uma ordem lógica. Na Figura 2 podemos
ver a sintaxe correta para escrever uma classe.

Vejamos que a sequência de comandos deve ser seguida na ordem correta, informando o
modificador de acesso, palavra que define que é uma classe e o nome da classe. Após
isso, temos um par chaves que definem o escopo da classe. É aqui que devemos colocar
os atributos, métodos, variáveis e a lógica para a classe.

Figura 2. Sintaxe para criar uma classe

Para escrever uma classe devemos seguir uma nomenclatura para a escrita do nome da
classe e de seus atributos e métodos. Assim, a primeira letra do nome da classe deve
estar em maiúsculo e, caso tenha palavras compostas, devemos colocar a primeira letra
da palavra subsequente também em maiúsculo para diferenciá-las, ou também podemos
separar as palavras pelo caractere “_”.
As primeiras letras dos atributos e métodos de uma classe devem ser escritas em
minúsculo e o restante da composição do nome dos atributos e métodos pode seguir o
mesmo conceito do nome de uma classe.

A ideia da nomenclatura é que se possa diferenciar logo de cara, na hora da codificação,


o que é uma classe e quais são atributos, métodos e instância de classe. No mercado
existem ferramentas capazes de diferenciar os tipos objetos, como é caso do Visual
Studio.

Modificadores de acesso
As classes e seus membros internos, como os atributos e métodos, possuem um nível de
acessibilidade ou visibilidade tanto para o acesso interno quanto externo, ou seja, é a
forma de segurança de definir se os componentes do software poderão ser visíveis e
usados apenas pelo próprio sistema ou se outros sistemas poderão fazer uso de
componentes, como o conjunto de classes através de seus assemblies.

Como estamos falando de orientação a objetos, logo pensamos na reutilização do


código. Para fazer uso desse recurso sempre se deve levar em conta o aspecto da
segurança para que o software (ou parte dele) não fique vulnerável. Pensando nisso,
devemos definir a acessibilidade através de modificadores de acesso. A seguir veremos
os tipos de modificadores:

· public: Define que a acessibilidade de um tipo ou membro dele pode ser acessado
pelo mesmo assemblie onde está contido. É possível que outro sistema possa fazer
referência a ele e faça uso de seus recursos.

· private: Define que o tipo ou membro dele pode ser acessado apenas pelo código na
mesma classe.

· protected: Define que o tipo ou membro dele pode ser acessado apenas pelo código
na mesma classe ou uma classe derivada da classe base.

· internal: Define que a classe pode ser acessada por qualquer código no mesmo
assemblie, mas não através de outros assemblies.

Para entender melhor este conceito imaginemos um sistema com diversas classes e cada
uma com o propósito de executar uma tarefa específica. Assim, só será possível
executar tarefas se o método main tiver instâncias de várias classes para executar as
tarefas que lhe é designado e concluí-las, assim, precisamos definir o nível acesso e
tornar visível as classes que método main irá precisar para cumprir seu papel em
executar as tarefas.

O conceito de acessibilidade de atributos e métodos em uma classes também é de suma


importância quando aplicamos o encapsulamento. Veremos detalhes o encapsulamento
mais a frente neste artigo.

Atributos
Os atributos de uma classe representam as características abstraídas do objeto do mundo
real, assim, uma classe pode ter vários atributos (ou campos). Cada atributo em uma
classe em C# deve ter um tipo predefinido no momento de sua criação, já que a
linguagem é fortemente tipada.

Uma característica importante a respeito dos atributos é que os mesmos sempre devem
ter seu modificador de acesso definido como privado, já que é uma boa prática de
programação encapsular, principalmente quando se programa com uma linguagem de
programação orientada a objetos e o uso do recurso de encapsulamento de dados em
uma classe se faz necessário." [...]

Trabalhando com
Structures e Enum em
C#
Facebook Twitter
(3) (0)

Veja nesse artigo o desenvolvimento de um projeto com o Visual Studio,


mostrando boas práticas de programação no desenvolvimento de Structures e
Enums.

Esse artigo trata dois assuntos bastante importantes e que segue uma das boas práticas
de programação que é o uso de “Structures” e “Enum”e mostrar como usar os mesmo
em seu projeto. Vamos primeiramente falar um pouco sobre o que é um “Structures”.

Muito semelhante a uma classe, a Structure requer menos memória que uma classe,
instancia mais rapidamente e é considerada uma boa prática de programação para
objetos menores, mas somente para objetos menores. Um tipo struct é um tipo de valor
normalmente usado para encapsular pequenos grupos de variáveis relacionadas, como
as coordenadas de um retângulo ou as características de um item em um inventário. Os
Structs são tipos de dados definidos pelo usuário e que consistem em um conjunto de
variáveis simples. Os tipos de valores são armazenados no stack e cada vez que
alteramos seu valor, um novo endereço de memória é utilizado. Além disso, as structs se
comportam como classes.
Apesar das funcionalidades semelhantes, as structs são geralmente mais eficientes do
que classes em termos de consumo de recursos. Para decidir quando usar uma estrutura
ao invés de classes, deve-se analisar se o objeto:

 Possui instâncias com tamanho de até 16 bytes;


 Não é alterado com frequência após sua criação;
 Não é convertido para uma classe.

As structs podem implantar uma interface, mas não podem herdá-la de outra struct. Por
esse motivo, as struct membros não podem ser declarados como protegidos.

Uma estrutura possui as seguintes características:

 Pode ter métodos, campos, indexadores, propriedades, métodos de operação e


eventos;
 Pode ter construtores definidos, mas não destruidores. No entanto, você não pode
definir um construtor padrão para uma estrutura, pois ele é definido automaticamente
e não pode ser alterado;
 Não pode herdar outras estruturas ou classes;
 Não pode ser usada como uma base para outras classes ou estruturas;
 Pode implementar uma ou mais interfaces;
 Membros de estrutura não podem ser especificados como abstratos, virtuais ou
protegidos;
 Quando se cria um objeto struct usando o operador ”New”, ele é criado e o construtor
apropriado é chamado. Ao contrário de classes, estruturas podem ser instanciadas
sem usar o novo operador;
 Se o operador New não é utilizado, o campo permanecerá não atribuído e o objeto
não pode ser utilizado até que todos os campos são inicializados.

Uma estrutura em C# é simplesmente um tipo de dados composto que consiste em um


número de elementos de outros tipos. Pode conter campos, métodos, constantes,
construtores, propriedades, indexadores, operadores e até mesmo outros tipos de
estrutura.

Veja Também
 Curso Básico de C#
 Curso de C# Avançado
 Curso de Padrões de Projetos em C#

A palavra-chave struct pode ser usada para declarar uma estrutura. A forma geral de
uma declaração de estrutura em C# é a mesma apresentada na Listagem 1.

Listagem 1. Estrutura Declaração e criação de objetos

<Modificadores> struct <struct_name>

// membros Estrutura

Os objetos de uma struct podem ser criados usando o operador New, como se segue
na Listagem 2.

Listagem 2. Instanciando uma Struct.

MinhaEstrutura ms = new MinhaEstrutura ();

Uma struct em C# podem conter vários campos que podem ser declarados como
privados, públicos ou interno. Lembre-se que dentro de uma struct só podemos declarar
um campo.

Não podemos inicializar um campo dentro de uma estrutura. No entanto, podemos usar
um construtor para inicializar os campos de estrutura.

Uma struct também pode conter métodos estático ou não estático, mas os métodos
estáticos podem acessar somente outros membros estáticos e não podem invocar usando
um objeto da estrutura, mas apenas usando o nome da struct.

Os operadores podem ser sobrecarregados dentro da struct e as mesmas regras


aplicáveis para a classe C# também são aplicáveis neste caso. Ambos os operadores
unários e binários podem ser sobrecarregados. A estrutura não pode herdar de outra
classe ou estrutura e não podem ser a classe base para uma classe. Mas lembre-se que
em C# todos os tipos são direta ou indiretamente herdando o objeto classe base super e,
portanto, a estrutura também.

As estruturas não suportam herança e não podemos usar as palavras-chave virtual,


override, new, abstratic, com os métodos struct. Os tipos de struct nunca são abstratos e
estão sempre implicitamente vedados. Os modificadores abstratos ou selados não são
permitidos em uma declaração struct. Como a herança não é suportada por estruturas, a
acessibilidade declarada de um membro do struct não pode ser protegido ou interno.
Todos os tipos struct são implicitamente herdados de classe de objeto e é possível
substituir os métodos da classe de objeto dentro de um struct usando a palavra-chave
override. Lembre-se que este é um caso especial em estruturas C#.

Apesar das funcionalidades semelhantes, as structs são geralmente mais eficientes do


que classes em termos de consumo de recursos. Para decidir quando usar uma estrutura
ao invés de classes deve-se analisar se o objeto:

 Possui instâncias com tamanho de até 16 bytes;


 Não é alterado com frequência após sua criação;
 Não é convertido para uma classe.

As Structs devem ser usadas para objetos com poucas informações e por objetos como
tipo .NET, como Integer, String, Decimal, Date e etc.

Podemos usá-las sempre que você precisar de uma estrutura para armazenar dados e não
precisar criar heranças, polimorfismos e quando a quantidade de dados a armazenar seja
até 24 bytes. Para acima desse valor use classes.

A estrutura encapsula um pequeno grupo de variáveis relacionadas dentro de um único


tipo de dados definido pelo usuário. Além disso, melhora o uso de velocidade e
memória, além da melhora no desempenho e clareza de seu código. Uma estrutura pode
ser definida em um namespace, independentemente de qualquer outro tipo definido pelo
usuário (classe ou estrutura), ou pode ser definida como um tipo aninhado dentro da
declaração de outro tipo. Quando for necessário criar um objeto de estrutura usando o
operadornew, o construtor apropriado é chamado.Diferentemente das classes, as structs
podem ser instanciadas sem usar o operador new.Em tais casos, não há nenhuma
chamada de construtor, que faz a alocação mais eficiente.

Vamos mostrar na prática como podemos usar uma estrutura em nossos projetos. Para
começarmos, abra o Visual Studio e crie um projeto Windows Forms, como mostra
a Figura 1.
Figura 1. Criando um novo projeto

Depois do projeto ControlIT.SisPac estiver criado, vamos criar a estrutura com o código
da Listagem 3.

Listagem 3. Criando uma estrutura.

public struct stEndereco

private string tipologra;

private string logradouro;

private int numero;

private string complemento;

private string bairro;


private string cidade;

private string uf;

private string cep;

A palavra Strucure identifica um código de declaração de uma estrutura. Em seguida


foram definidos os campos que vão fazer parte junto com seus respectivos tipos. Não
existe nenhuma herança para estruturas como há para classes.Uma estrutura não pode
herdar uma outra estrutura ou classe, e também não pode ser base de uma
classe.Estruturas, no entanto, herdam da classe baseObject.

Na Listagem 4 temos a criação dos objetos criados a partir da Structure.

Listagem 4. Criação de objetos

public string Tipologra

set { tipologra = value; }

get { return tipologra; }

public string Logradouro

set { logradouro = value; }

get { return logradouro; }

}
public int Numero

set { numero = value; }

get { return numero; }

public string Complemento

set { complemento = value; }

get { return complemento; }

Na Listagem 5 temos a lista de parâmetros passados ao se criar o construtor. Esses


parâmetros recebidos servem para iniciar os valores dos atributos.

Listagem 5. Parâmetros

public stEndereco(string ptl, string plo, int pnu, string pco)

tipologra = ptl;

logradouro = plo;

numero= pnu;

complemento = pco;

}
Depois disso vamos criar o formulário de cadastro de clientes para testar nossa
aplicação, conforme mostra a Figura 2.

Figura 2. Tela de Cadastro de Cliente

Repare que a tela é bem simples, apenas com os labels e os texts para recolher as
informações e um único botão, o qual terá a mesma configuração presente na Listagem
6.

Listagem 6. Botão salvar de nossa aplicação.

private void bt_salvar(object sender, EventArgs e)

stEndereco ste = new stEndereco();

ste.Tipologra = cmbTipoVia.Text;

ste.Logradouro = txtLogradouro.Text;

ste.Numero = txtnumero.Text;

ste.Complemento = txtcomplemento.Text;

MessageBox.Show("ok");
}

Enum
Uma das boas práticas de programação em C# é o uso do enum. Ele serve para
substituirmos constantes nomeadas que são relacionadas mas ficam “perdidas” no
código. A palavra-chave enum é usada para declarar uma enumeração, um tipo distinto
que consiste em um conjunto de constantes. O Enum é um tipo de valor e não pode
herdar ou ser herdado.

Geralmente é melhor definir um enum dentro de um namespace para que todas as


classes dele possam acessá-lo. No entanto, um enum também pode ser aninhado em
classes ou Structures. O. NET Framework Class Library enums contém muitos
exemplos de como eles são utilizados. Por exemplo, toda vez que você colocar um ícone
em um MessageBox, você usa o MessageBoxIcon ENUM. Sempre existem situações
em que você está usando um conjunto de números relacionados em um programa, então
considere substitui estes números com enums.

Ele é um objeto string cujo valor normalmente é escolhido de uma lista de valores
permitidos e que são enumerados explicitamente na especificação da coluna na criação
da tabela. O valor pode ser a string vazia ("") ou NULL sob as seguintes circunstâncias:

 Se você inserir um valor inválido em um enum(isto é, uma string que não está presente
na lista de valores permitidos), a string vazia é inserida no lugar como um valor
especial de erro. Esta string pode se diferenciar de uma string vazia 'normal' pelo fato
de que esta string tem o valor numérico 0. Veremos mais sobre este assunto mais
tarde.
 Se um enum é declarado null, null é também um valor permitido para a coluna, e o
valor padrão é null. Se um enum é declaarado not null, o valor padrão é o primeiro
elemento da lista de valores permitidos.

Cada enumeração tem um índice, onde:

 Valores da lista de elementos permitidos na especificação da coluna são números


começados com 1;
 O valor de índice de uma string vazia que indique erro é 0.
Por padrão, o primeiro enumerador tem o valor 0 e o valor de cada enumerador sucessor
aumente em 1.

Os tipos aceitos para um enum são byte, sbyte, short, ushort, int, uint, long, ou ulong.
Uma variável do tipo Dia pode ser atribuída a qualquer valor no intervalo do tipo
subjacente, pois os valores não são limitados às constantes nomeadas. Porém, algumas
vezes torna-se necessário fazer uma representação dos valores deste Enum na interface,
mais comumente em um DropDownList.

O Enum tem os métodos descritos a seguir:

 String toString(), que retorna uma string com o nome da instância (em maiúsculas);
 valueOf (String nome), que retorna o objeto da classe enum cujo nome é a string do
argumento e;
 int ordinal(), que retorna o número de ordem do objeto na enumeração.

Em se tratando do tempo de execução no projeto, um tipo de dado enumerado é


geralmente implementado usando-se inteiros. Entretanto, comparando-se a usar somente
inteiros, os tipos enumerados tornam o código fonte mais bem documentado do que
através do uso explícito de "números mágicos".

A seguir estão vantagens de usar um enum em vez de um tipo numérico:

Você especifica claramente para o código do cliente que os valores são válidos para a
variável;

No Visual Studio, o IntelliSense lista os valores definidos.

Os Enum são tipos que herdam a System.Enum na Base Class Library (BCL). Um
requisito comum é converter entre o ENUM e uma variável do tipo sua base. Caso você
esteja recebendo contribuições na forma de um int de um usuário ou um arquivo, então
você pode lançá-la para um ENUM e utilizá-la de forma significativa em seu programa.
Devemos considerar que a manipulação de variáveis do tipo valor oferece uma
performance superior a variável do tipo referência, por não possuir a necessidade de
alocação em heap (área de alocação dinâmica) e não ser acessada através de um
ponteiro. Dependendo da linguagem, a representação de inteiros pode não ser visível ao
programador, o que previne operações como aritmética. Uma das vantagens de se ter
enum é que podem ser tratados como se fossem inteiros, ou seja, podemos usar em
switchs e ifs, além de podermos comparar a tipagem ao invés de comparar strings.
Nas Listagens 7 a 9 temos exemplo de três Enums: Enum Status, Tamanho e
TipoCliente. É possível criar qualquer tipo de lista enumerada e utilizá-la para
referenciar internamente no código, deixando mais claro seu código e ações.

Listagem 7. Lista enumerada de Status.

public enum Status

Ativo = 1;

Inativo = 2;

Aguardando = 3;

Cancelado =4;

Como você pode observar, a sintaxe da declaração da estrutura acima é semelhante a de


uma classe, ou seja, uma estrutura pode possuir construtores, porém não possui
destrutores e os construtores têm de ser customizados. A tentativa de declarar o
construtor default gera um erro de compilação.

Listagem 8. Lista enumerada de Tamanho.

public enum Tamanho{ pequeno, medio, grande, extragrande }

Listagem 9. Lista enumerada de TipoCliente

public enum TipoCliente

Pessoafisica = 1;

Pessoajuridica = 2;

Orgaopublico = 3;

ONG = 4;
}

Veja que foi criada uma lista enum do tipo de cliente que será gravada no banco
somente aquele que tiver o valor inteiro, e toda vez que for exibido ao usuário por meio
de uma interface, será exibida a descrição ao invés do valor inteiro.

Dentro do conceito que é chamado de programação robusta, ou como acontece com


qualquer constante, todas as referências para os valores individuais de um enum são
convertidas em numéricos em tempo de compilação.Isso pode criar possíveis problemas
de versionamento. Atribuindo valores adicionais para novas versões de enums ou
alterando os valores dos membros enum em uma nova versão, pode causar problemas
para o código-fonte dependente.Valores de enumeração são frequentemente usados em
alternar instruções.Se os elementos adicionais foram adicionados para o enum,pode ser
selecionado o tipo e a seção padrão da instrução switch inesperadamente.

O enum nos permitem criar listas para tornar mais fácil a vida do desenvolvedor e
principalmente para evitar erros de comparação e ainda padronizar determinada opção
dentro do sistema, pois são basicamente listas que são enumeradas. Desta forma, sempre
terá uma Chave e um Valor que juntos formam um registro único, ou seja, não é
possível criar um enumerador que tenha a mesma chave e valor. Essas listas geralmente
são pequenas e atribuir valores adicionais para novas versões de enums ou alterar os
valores dos membros em uma nova versão pode causar problemas para o código-fonte
dependente. Valores de enumeração são frequentemente usados em alternar instruções.

É possível também definir outros tipos numéricos para os elementos em vez de apenas
int, por exemplo, é possível defini-los como byte e long,porém, nestes casos, você terá
de fazer casta todo o momento que for usar o enumerador. Então, modificar o tipo do
enumerador só é aconselhado quando realmente se faz necessário.

Em um enumerador, quando não são atribuídos valores para as opções, este sempre irá
iniciar com o valor zero e sempre o valor de um enumerador será um número. Não há
como definir uma opção com valor string, por exemplo. No entanto, é liberado para que
você mesmo atribua os valores desejados para cada elemento de um enumerador ou até
mesmo que omita essa atribuição se achar necessário ou ver que é necessário em apenas
alguns elementos.

Se outros desenvolvedores usam seu código, você deve fornecer diretrizes sobre como o
seu código deve reagir se novos elementos forem adicionados a qualquer enum.
Integração Contínua
em .NET – Parte 1
Facebook Twitter
(8) (0)

Veja neste artigo como automatizar a construção, documentação e entrega de


seus sistemas em .NET utilizando a integração contínua.

Demais posts desta série:


Integração Contínua em .NET – Parte 2
Integração Contínua em .NET: Implementação – Parte 3

Artigo no estilo Curso

Fique por dentro


A Integração Contínua (CI) deixa o desenvolvedor livre para se preocupar com o que realmente
importa: criar e desenvolver software.

Não importa o tamanho do projeto, seja ele um pequeno sistema de cadastro de contatos
ou um sistema especialista para simulações do clima terrestre ou da expansão do
Universo: suas tarefas básicas consistem em compilar, testar, documentar, verificar
qualidade e consistência do código e finalmente informar sobre erros.

Nessa primeira parte do curso veremos uma introdução aos passos que a empresa
precisa dar para implementar a CI em sua rotina de desenvolvimento.

Uma das chaves para a melhoria da produtividade e, consequentemente das aplicações


entregues, é a automatização da parte repetitiva do trabalho e a Integração Contínua é
uma das melhores formas de se fazer isso.

Não podemos crer que o processo de Integração Contínua seja a solução para todos os
males, mas onde ela tem sido implementada tem-se tido grande sucesso com melhorias
no processo de software para as equipes de desenvolvimento.

A Integração Contínua, também conhecida como CI - Continuous Integration - é o


ponto central do desenvolvimento de software atualmente. De fato, quando a CI é
introduzida em uma empresa, torna-se o grande divisor de águas, pois ela altera
radicalmente a forma como as equipes pensam sobre todo o processo de
desenvolvimento.

A CI tem o potencial de executar uma série de melhorias contínuas e incrementais nos


processos, indo de um processo de simples agendamento de construção (build) até o
processo de entrega contínua que culmina no processo de publicação nos demais
ambientes para teste, homologação ou produção.

Uma boa infraestrutura que possibilita a Integração Contínua pode simplificar o


processo de desenvolvimento até a entrega do software ou de sua iteração, auxiliando a
identificação e correção de falhas rapidamente, afinal de contas, precisa integrar toda a
equipe e precisa armazenar todas as versões do software.

Também facilita a disponibilização de uma dashboard muito útil no Projeto, tanto para
desenvolvedores quanto para equipes, por agregar mais valores ao negócio e ao produto.
Todo time de desenvolvimento, não importando se grande ou pequeno, deve
implementar e pensar como Integração Contínua.

O que é a Integração Contínua?


Logo que uma alteração no código é identificada, o servidor automaticamente compila e
testa o código de sua aplicação. Se algo dá errado, a ferramenta imediatamente notifica
os desenvolvedores para que eles possam corrigir o problema imediatamente. Mas, a
integração contínua pode fazer muito mais do que isso.

A visibilidade das métricas de qualidade de código faz com que a equipe se preocupe
mais com o desenvolvimento, pois o principal fundamento da Integração Contínua é
facilitar a manutenção do código.

Combinando testes de aceitação, a CI pode também funcionar como uma ferramenta de


comunicação, mostrando um completo resumo do estado atual do projeto em
andamento, dentro e fora da equipe. Pode simplificar e acelerar a entrega, auxiliando a
automação do processo, deixando a publicação da última versão da aplicação totalmente
automática ou permitindo a operação com apenas um aperto de botão.

Em sua essência, a Integração Contínua age sempre na redução de riscos à aplicação e


seu desenvolvimento nos dá uma resposta rápida.

O uso da Entrega Contínua está ligado diretamente a Integração Contínua, pois o passo
de compilação e testes é tarefa da Integração Contínua, enquanto que o Deploy da
aplicação já fica sob o domínio da Entrega Contínua. Formalmente, a entrega contínua é
definida como “um conjunto de práticas e princípios com o objetivo de compilar, testar
e liberar software de forma mais rápida e frequente”.

Observe que a definição em si não menciona programas específicos, mas sim uma
filosofia a ser seguida.

O processo de Integração Contínua, e em particular a Entrega Contínua, é muito mais"


[...]
A exibição deste artigo foi interrompida :(
Este post está disponível para assinantes MVP

Integração Contínua
em .NET – Parte 2
Facebook Twitter
(1) (0)

Veja nesse artigo como evitar erros comuns na implementação de


projetos.Veremos na prática como implementar as boas práticas da IC.

Demais posts desta série:


Integração Contínua em .NET – Parte 1
Integração Contínua em .NET: Implementação – Parte 3

Artigo no estilo Curso

Fique por dentro


A integração contínua (CI) é uma prática que vem se disseminando nas empresas como um
todo, trazendo vantagens importantes como a automatização da parte repetitiva do trabalho
do desenvolvedor, permitindo que se concentrem no que realmente importa: o
desenvolvimento dos softwares.

Como vimos na primeira parte dessa série, a implementação da CI pode trazer alguns
problemas, muitas vezes devido a erros nessa implementação. Nessa segunda parte iremos
exemplificar alguns desses erros e mostrar como eles podem ser evitados e/ou resolvidos.

A integração contínua traz grandes vantagens para as empresas no que diz respeito a
integração das diferentes partes do software sendo criado em equipe. Quem trabalha em
equipe, sabe que um dos principais gargalos de tempo dentro do projeto é a integração
de todas as partes, ao final.
Muitas vezes, todas essas partes, que funcionam perfeitamente individualmente, quando
juntas acabam não trabalhando da mesma forma. Tal problema traz um atraso bastante
grande, porque um ou mais membros da equipe precisam entender o que está acontecendo e
corrigir esses problemas para que o sistema completo possa ser dado como concluído.

É daí que vem talvez a principal vantagem da integração contínua. Ela traz uma ideia na
qual as partes são integradas constantemente durante o desenvolvimento.

Esse tipo de ação faz com que, quando o software é dado como concluído, ele funcione como
um todo, e não apenas como partes. Essa atitude evita esse gargalo de tempo ao final do
projeto, aumentando a produtividade e diminuindo o tempo de projeto, um ponto que todas
as empresas procuram.

Porém, a CI não traz apenas benefícios e, ao longo desse artigo, iremos exibir alguns
dos principais problemas que podem acontecer durante e depois da implementação
desses processos. Veremos que todos eles podem ser evitados ou ao menos corrigidos,
no caso de uma implementação correta.

Alguns deles, inclusive, existem apenas por uma falta de entendimento do que é e como
funciona profundamente a integração contínua, e como aplicá-la adequadamente dentro da
empresa.

Na primeira parte dessa série trouxemos alguns elementos básicos sobre a integração
contínua e alguns passos de como implementá-la corretamente dentro das empresas.

Reparamos que a integração contínua consiste de uma série de fatores que precisam ser
levados em conta, desde o estilo de trabalho da equipe de desenvolvimento até o hardware
utilizado.

Além disso, trouxemos informações a respeito dos processos de desenvolvimento para


integração contínua, bem como os tipos de construção (build) utilizados (contínuo,
diário/noturno, semanal, QA (BOX 1), staging/pré-produção, release).

BOX 1. QA (Quality Assurance)

Essa sigla representa uma série de atividades que tem como objetivo final garantir a
qualidade do software que está sendo avaliado. Como sabemos, a qualidade de um
software não é medida apenas pelo fato de que ele faz o trabalho bem feito, e são
medidos fatores para garantir que o software vá de encontro aos requerimentos do
projeto.

Esse tipo de teste é aplicado para garantir que as funcionalidades estejam funcionando
corretamente, que não haja nenhum tipo de bug dentro do sistema. Essas atividades são
aplicadas no software antes da produção final.

A QA é baseada em alguns princípios que irão estabelecer a qualidade do software,


dependendo do que está sendo medido. Alguns deles são a verificação se o produto está
dentro dos parâmetros requeridos para o seu propósito e outra, obviamente, é se o
software está totalmente livre de erros. Esses dois princípios são aplicados em
virtualmente todos os softwares que passam por QA.
Outro ponto importante apresentado na primeira parte dessa série foram os passos para
implementação da CI dentro da empresa. Vimos que a implementação da integração
contínua é baseada em sete ciclos, e é muito importante que esses ciclos sejam
respeitados para que haja uma implementação gradual no dia-a-dia da empresa. Esses
ciclos são resumidos a seguir:

1º) Ciclo inicial, onde não há um servidor de build disponível para a empresa, fazendo
com que todos os passos de construção e integração do software sejam realizados
manualmente.

2º) O time dispõe de um servidor de build, sendo que a construção é automatizada para
períodos regulares, em momentos que o fluxo de trabalho é baixo.

3º) O servidor já é capaz de aplicar alguns testes ao código, além de o build contínuo já
estar sendo aplicado (toda vez que o código é comitado no SCM).

4º) Aplicação da verificação de qualidade de código (QA).

5º) O time está mais organizado e a integração contínua já começa a se encaixar às


práticas de teste (TDD).

6º) A maioria dos principais testes já estão incluídas na CI, bem como ferramentas de
relatórios e comunicação entre os membros da equipe.

7º) A integração contínua está integralmente implementada dentro da rotina da empresa,


com tudo que se espera dela.

Agora que temos uma ideia mais clara sobre a integração contínua, vamos entender o
que pode acontecer quando alguns dos passos são realizados de forma errada dentro das
empresas. Além disso, vamos entender que, em um processo bem organizado e
implementado, as vantagens da integração contínua são muito maiores do que eventuais
distúrbios que ela possa trazer.

Objetivos e Vantagens da Integração Contínua


Conforme foi comentado, a integração contínua, uma vez implementada no dia-a-dia da
empresa pode trazer inúmeros benefícios. Ao conhecer trabalhos de grandes
companhias, temos uma ideia melhor do quão bom pode ser ess" [...]

A exibição deste artigo foi interrompida :(


Este post está disponível para assinantes MVP
Integração Contínua
em .NET:
Implementação – Parte
3
Facebook Twitter
(1) (0)

Veja nesse artigo como implementar os sete ciclos da Integração Contínua.


Além disso, vamos aprender a usar o Git e Hudson e ver porque os dois são
boas ferramentas para qualquer empresa que empregue I.C.

Demais posts desta série:


Integração Contínua em .NET – Parte 1
Integração Contínua em .NET – Parte 2

Artigo no estilo Curso

Fique por dentro


A integração contínua (CI) é uma prática que vem se disseminando nas empresas como um
todo, trazendo vantagens importantes como a automatização da parte repetitiva do trabalho
do desenvolvedor, permitindo que se concentrem no que realmente importa: o
desenvolvimento dos softwares.

Nos demais artigos desse curso focamos em detalhes teóricos da implementação da


integração contínua nas empresas. Nesse artigo iremos começar a implementação da
integração contínua em uma empresa totalmente do zero. Apresentaremos uma
alternativa de servidor de CI, o Hudson, bem como o Git como servidor SCM, uma vez
que apresentamos o SVN e o CruiseControl.NET (servidor CI) na segunda parte dessa
série.

A integração contínua é uma das principais ferramentas de integração no


desenvolvimento de software em equipe atualmente. Esse tipo de técnica permite que as
partes do software que estão sendo desenvolvidas por diferentes pessoas estejam
integradas constantemente, evitando atrasos ao final do projeto. Porém, a
implementação não é tão simples como possa parecer.
Nas primeiras partes de nossa série trouxemos os principais passos para a
implementação da integração contínua e os principais erros que são cometidos. É muito
importante que se tenha uma noção do que essas informações significam para a
implementação correta das técnicas de CI.

Como se trata de uma arte que, embora simples no contexto, traz uma série de nuances,
e é muito fácil para desenvolvedores com pouca experiência se perderem nesses passos
e acabarem cometendo alguns erros, como os que foram explicados na parte dois de
nossa série.

Inicialmente, falamos sobre o que é a integração contínua e como ela pode ser
implementada dentro das empresas. Nessa primeira parte foram abordados temas como
processos de desenvolvimento para integração contínua, que incluem os processos
comuns à maioria das empresas mais alguns específicos para a ação do servidor de CI,
onde ele observa as alterações no código do SCM, efetua o build, executa os testes
unitários e envia os resultados do build e dos testes para algum tipo de sistema de
comunicação.

Vale ressaltar que esses passos somente são utilizados quando os processos de
integração contínua estão completos, o que pode levar algum tempo. Nesse curso vimos
detalhes de tipos de build, os passos pelos quais a empresa precisa passar para
corretamente implementar a integração contínua e alguns pontos de custo benefício e
possível resistência a mudança por parte dos membros da equipe de desenvolvimento.

Além disso, no curso também foram pontuados os principais objetivos para a


implementação da integração contínua. É muito importante que tenhamos essa ideia de
qual é o objetivo final da implementação da IC, e de qualquer outro processo, bem
como dos objetivos parciais a serem alcançados. Esse tipo de atitude traz um grande
benefício para a equipe de desenvolvimento, fazendo com que a mudança não seja tão
brusca e que todos tenham uma noção do lugar para onde se está indo.

Nessa segunda parte, vimos também a implementação do servidor de CI, com poucos
detalhes e com alguns erros criados propositalmente para entendermos o que
comumente pode acontecer durante a implementação. Esses erros ocorrem,
principalmente, devido à falta de conhecimento dos processos de integração contínua,
acarretando muitos problemas.

Durante esse artigo iremos começar a implementação de nosso servidor de integração


contínua. Serão seguidos alguns passos rígidos - vistos brevemente na parte um da série
- que serão vistos a seguir para termos, ao final de nossa série, uma ideia muito clara do
que é a integração contínua, os erros que podem acontecer quando a implementamos e,
obviamente, como implementá-la da forma mais correta com as principais ferramentas
do mercado. Para isso, utilizaremos uma estrutura fictícia composta por uma empresa
que está começando no mercado e quer desde o começo possuir uma estrutura de
integração contínua, o que irá evitar alguns dos problemas que comentamos nas partes
anteriores de nossa série.

Passos para implementação da Integração


Contínua
A integração contínua é uma ferramenta muito útil para construção de softwares em
equipe. É uma das chaves para a melhoria de produtividade nos dias atuais pelo fato de
automatizar a parte repetitiva do trabalho. A sua implementação, entretanto, requer
bastante atenção e uma série de passos que devem ser seguidos para que o impacto da
implementação não seja muito grande nem que hajam erros na mesma.

Isso é muito importante, uma vez que, normalmente, os softwares criados pelas
empresas possuem prazos muito rígidos que podem ser prejudicados no caso de uma
integração contínua inconfiável.

Esses passos que serão apresentados a seguir foram apresentados na parte 1 de nossa
série sob uma ótica um tanto diferente, mais didática e menos prática. O objetivo aqui é
mostrar o que será feito em cada ciclo para que, quando a empresa atingir o sétimo
ciclo, todo o sistema de integração contínua dentro da empresa esteja implementado.
Atualmente, podemos colocar nossa empresa fictícia em um ciclo 0, uma vez que não há
nenhum tipo de infraestrutura nem processos ainda disponíveis na empresa.

· Ciclo 1: No primeiro ciclo, a empresa não possui nenhum tipo de servidor de build
centralizado. Isso faz como que todos os builds sejam realizados manualmente na
máquina do desenvolvedor.

Com isso, a integração de todas as peças do software é realizada somente ao final, o que
pode e deve gerar muita dor de cabeça para a equipe, além de possíveis atrasos. Nossa
empresa fictícia não irá passar por esse ciclo, uma vez que iremos implementar
inicialmente um servidor de build e adaptar o time de desenvolvedores para trabalhar
dessa forma.

· Ciclo 2: Há um servidor de build automatizados, agendados para serem operados em


um período " [...]

A exibição deste artigo foi interrompida :(


Este post está disponível para assinantes MVP

Você também pode gostar