3. Noções básicas da linguagem C#
3.1. Introduction
Vamos tratar o C#, inicialmente, como uma linguagem de programação clássica. Abordaremos as classes posteriormente. Em um programa, encontramos duas coisas:
- dados
- as instruções que os manipulam
Geralmente, procuramos separar os dados das instruções:
![]() |
3.2. Os dados do C#
O C# utiliza os seguintes tipos de dados:
- números inteiros
- números reais
- números decimais
- caracteres e cadeias de caracteres
- booleanos
- os objetos
3.2.1. Tipos de dados predefinidos
Tipo C# | Tipo .NET | Dado representado | Sufixo dos valores literais | Codificação | Intervalo de valores |
Caractere(s) | caractere | 2 bytes | caractere Unicode (UTF-16) | ||
String (C) | sequência de caracteres | referência a uma sequência de caracteres Unicode | |||
Int32 (S) | número inteiro | 4 bytes | [-231, 231-1] [–2147483648, 2147483647] | ||
UInt32 (S) | .. | U | 4 bytes | [0, 232-1] [0, 4294967295] | |
Int64 (S) | .. | L | 8 bytes | [-263, 263 -1] [–9223372036854775808, 9223372036854775807] | |
UInt64 (S) | .. | UL | 8 bytes | [0, 264 -1] [0, 18446744073709551615] | |
.. | 1 byte | [-27 , 27 -1] [-128,+127] | |||
Byte(s) | .. | 1 byte | [0 , 28 -1] [0,255] | ||
Int16 (S) | .. | 2 bytes | [-215, 215-1] [-32768, 32767] | ||
UInt16 (S) | .. | 2 bytes | [0, 216-1] [0,65535] | ||
Único (S) | número real | F | 4 bytes | [1.5 10-45, 3.4 10+38] em valor absoluto | |
Duplo (S) | .. | D | 8 bytes | [-1.7 10+308, 1.7 10+308] em valor absoluto | |
Decimal (S) | número decimal | M | 16 bytes | [1.0 10-28,7.9 10+28] em valor absoluto com 28 dígitos significativos | |
Booleano (S) | .. | 1 byte | verdadeiro, falso | ||
Objeto (C) | referência de objeto | referência de objeto |
Acima, colocamos ao lado dos tipos C# seus tipos equivalentes em .NET, com o comentário (S) se esse tipo for uma estrutura e (C) se for uma classe. Percebe-se que existem dois tipos possíveis para um inteiro de 32 bits: int e Int32. O tipo int é um tipo C#. Int32 é uma estrutura pertencente ao namespace System. Seu nome completo é, portanto, System.Int32. O tipo int é um alias em C# que designa a estrutura .NET System.Int32. Da mesma forma, o tipo C# string é um alias para o tipo .NET System.String. System.String é uma classe e não uma estrutura. Os dois conceitos são semelhantes, mas apresentam a seguinte diferença fundamental:
- uma variável do tipo Structure é manipulada por meio de seu valor
- uma variável do tipo Classe é manipulada por meio de seu endereço (referência na linguagem orientada a objetos).
Tanto um structure quanto um classe são tipos complexos que possuem atributos e métodos. Assim, é possível escrever:
No exemplo acima, o literal 3 é, por padrão, do tipo C# int e, portanto, do tipo .NET System.Int32. Essa estrutura possui um método GetType() que retorna um objeto que encapsula as características do tipo de dados System.Int32. Entre elas, a propriedade FullName retorna o nome completo do tipo. Vemos, portanto, que o literal 3 é um objeto mais complexo do que parece à primeira vista.
Aqui está um programa que ilustra esses diferentes pontos:
using System;
namespace Chap1 {
class P00 {
static void Main(string[] args) {
// exemplo 1
int ent = 2;
float fl = 10.5F;
double d = -4.6;
string s = "essai";
uint ui = 5;
long l = 1000;
ulong ul = 1001;
byte octet = 5;
short sh = -4;
ushort ush = 10;
decimal dec = 10.67M;
bool b = true;
Console.WriteLine("Type de ent[{1}] : [{0},{2}]", ent.GetType().FullName, ent,sizeof(int));
Console.WriteLine("Type de fl[{1}]: [{0},{2}]", fl.GetType().FullName, fl, sizeof(float));
Console.WriteLine("Type de d[{1}] : [{0},{2}]", d.GetType().FullName, d, sizeof(double));
Console.WriteLine("Type de s[{1}] : [{0}]", s.GetType().FullName, s);
Console.WriteLine("Type de ui[{1}] : [{0},{2}]", ui.GetType().FullName, ui, sizeof(uint));
Console.WriteLine("Type de l[{1}] : [{0},{2}]", l.GetType().FullName, l, sizeof(long));
Console.WriteLine("Type de ul[{1}] : [{0},{2}]", ul.GetType().FullName, ul, sizeof(ulong));
Console.WriteLine("Type de b[{1}] : [{0},{2}]", octet.GetType().FullName, octet, sizeof(byte));
Console.WriteLine("Type de sh[{1}] : [{0},{2}]", sh.GetType().FullName, sh, sizeof(short));
Console.WriteLine("Type de ush[{1}] : [{0},{2}]", ush.GetType().FullName, ush, sizeof(ushort));
Console.WriteLine("Type de dec[{1}] : [{0},{2}]", dec.GetType().FullName, dec, sizeof(decimal));
Console.WriteLine("Type de b[{1}] : [{0},{2}]", b.GetType().FullName, b, sizeof(bool));
}
}
}
- linha 7: declaração de um inteiro ent
- linha 19: o tipo de uma variável v pode ser obtido por v.GetType().FullName. O tamanho de uma estrutura S pode ser obtido por sizeof(S). A instrução Console.WriteLine("... {0} ... {1} ...", param0, param1, ...) exibe na tela o texto que é seu primeiro parâmetro, substituindo cada notação {i} pelo valor da expressão parami.
- linha 22: como o tipo string designa uma classe e não uma estrutura, não é possível utilizar o operador sizeof.
Aqui está o resultado da execução:
A exibição mostra os tipos .NET e não os aliases do C#.
3.2.2. Notação de dados literais
145, -7, 0xFF (hexadecimal) | |
100000L | |
134,789, -45E-18 (-45 × 10⁻¹⁸) | |
134.789F, -45E-18F (-45 10⁻¹⁸) | |
100000M | |
'A', 'b' | |
"hoje" "c:\\cap1\\parágrafo3" @"c:\cap1\parágrafo3" | |
true, false | |
new DateTime(1954,10,13) (ano, mês, dia) para 13/10/1954 |
Observe as duas cadeias literais: "c:\\chap1\\paragraph3" e @"c:\chap1\paragraph3". Nas cadeias literais, o caractere \ é interpretado. Assim, “\n” representa o caractere de fim de linha e não a sucessão dos dois caracteres \ e n. Se quiséssemos essa sucessão, seria necessário escrever “\\n”, onde a sequência \\ é interpretada como um único caractere \. Também seria possível escrever @“\n” para obter o mesmo resultado. A sintaxe @“texto” exige que o texto seja interpretado exatamente como está escrito. Às vezes, isso é chamado de cadeia verbatim.
3.2.3. Declaração de dados
3.2.3.1. Função das declarações
Um programa manipula dados caracterizados por um nome e um tipo. Esses dados são armazenados na memória. No momento da compilação do programa, o compilador atribui a cada dado um local na memória caracterizado por um endereço e um tamanho. Ele faz isso com base nas declarações feitas pelo programador.
Além disso, essas declarações permitem que o compilador detecte erros de programação. Assim, a operação
x = x * 2;
será declarada como errada se x for uma sequência de caracteres, por exemplo.
3.2.3.2. Declaração de constantes
A sintaxe para declarar uma constante é a seguinte:
Por exemplo:
Por que declarar constantes?
- A leitura do programa será mais fácil se dermos à constante um nome significativo:
- A modificação do programa será mais fácil caso a “constante” venha a mudar. Assim, no caso anterior, se a alíquota do IVA passar para 33%, a única alteração a ser feita será modificar a instrução que define seu valor:
Se tivéssemos usado 0,186 explicitamente no programa, seria necessário alterar várias instruções.
3.2.3.3. Declaração de variáveis
Uma variável é identificada por um nome e está associada a um tipo de dados. O C# diferencia maiúsculas de minúsculas. Assim, as variáveis FIN e fin são diferentes.
As variáveis podem ser inicializadas no momento da sua declaração. A sintaxe para declarar uma ou mais variáveis é:
onde Identificateur_de_type é um tipo predefinido ou um tipo definido pelo programador. Opcionalmente, uma variável pode ser inicializada ao mesmo tempo em que é declarada.
Também é possível não especificar o tipo exato de uma variável usando a palavra-chave var em vez de Identificateur_de_type:
A palavra-chave var não significa que as variáveis não tenham um tipo específico. A variável variablei possui o tipo dos dados valeuri que lhe são atribuídos. A inicialização é obrigatória neste caso para que o compilador possa deduzir o tipo da variável.
Veja um exemplo:
using System;
namespace Chap1 {
class P00 {
static void Main(string[] args) {
int i=2;
Console.WriteLine("Type de int i=2 : {0},{1}",i.GetType().Name,i.GetType().FullName);
var j = 3;
Console.WriteLine("Type de var j=3 : {0},{1}", j.GetType().Name, j.GetType().FullName);
var aujourdhui = DateTime.Now;
Console.WriteLine("Type de var aujourdhui : {0},{1}", aujourdhui.GetType().Name, aujourdhui.GetType().FullName);
}
}
}
- linha 6: um dado explicitamente tipado
- linha 7: (dado).GetType().Name é o nome abreviado de (dado), (dado).GetType().FullName é o nome completo de (dado)
- linha 8: um dado tipado implicitamente. Como 3 é do tipo int, j será do tipo int.
- linha 10: um dado tipado implicitamente. Como DateTime.Now é do tipo DateTime, aujourdhui será do tipo DateTime.
Na execução, obtém-se o seguinte resultado:
Uma variável cujo tipo é definido implicitamente pela palavra-chave var não pode, posteriormente, mudar de tipo. Assim, não seria possível escrever, após a linha 10 do código, a linha:
var aujourdhui = "aujourd'hui";
Veremos mais adiante que é possível declarar um tipo “na hora” em uma expressão. Trata-se, nesse caso, de um tipo anônimo, ou seja, um tipo ao qual o usuário não atribuiu um nome. É o compilador que atribuirá um nome a esse novo tipo. Se um valor de tipo anônimo precisar ser atribuído a uma variável, a única maneira de declarar essa variável é usando a palavra-chave var.
3.2.4. Conversões entre números e cadeias de caracteres
nombre.ToString() | |
int.Parse(cadeia) ou System.Int32.Parse | |
long.Parse(cadeia) ou System.Int64.Parse | |
double.Parse (cadeia) ou System.Double.Parse (cadeia) | |
float.Parse (cadeia) ou System.Float.Parse (cadeia) |
A conversão de uma string em um número pode falhar se a string não representar um número válido. Nesse caso, é gerado um erro fatal chamado exceção. Esse erro pode ser tratado pela seguinte cláusula try/catch:
try{
appel de la fonction susceptible de générer l'exception
} catch (Exception e){
traiter l'exception e
}
instruction suivante
Se a função não gerar uma exceção, passa-se para a instrução seguinte; caso contrário, passa-se para o corpo da cláusula catch e, em seguida, para a instrução seguinte. Voltaremos mais tarde ao gerenciamento de exceções. Aqui está um programa que apresenta algumas técnicas de conversão entre números e cadeias de caracteres. Neste exemplo, a função affiche exibe na tela o valor de seu parâmetro. Assim, affiche(S) exibe o valor de S na tela, sendo que S é do tipo string.
using System;
namespace Chap1 {
class P01 {
static void Main(string[] args) {
// dados
const int i = 10;
const long l = 100000;
const float f = 45.78F;
double d = -14.98;
// número --> string
affiche(i.ToString());
affiche(l.ToString());
affiche(f.ToString());
affiche(d.ToString());
//booleano --> string
const bool b = false;
affiche(b.ToString());
// cadeia de caracteres --> inteiro
int i1;
i1 = int.Parse("10");
affiche(i1.ToString());
try {
i1 = int.Parse("10.67");
affiche(i1.ToString());
} catch (Exception e) {
affiche("Erreur : " + e.Message);
}
// cadeia de caracteres --> inteiro longo
long l1;
l1 = long.Parse("100");
affiche(l1.ToString());
try {
l1 = long.Parse("10.675");
affiche(l1.ToString());
} catch (Exception e) {
affiche("Erreur : " + e.Message);
}
// cadeia de caracteres --> double
double d1;
d1 = double.Parse("100,87");
affiche(d1.ToString());
try {
d1 = double.Parse("abcd");
affiche(d1.ToString());
} catch (Exception e) {
affiche("Erreur : " + e.Message);
}
// cadeia de caracteres --> float
float f1;
f1 = float.Parse("100,87");
affiche(f1.ToString());
try {
d1 = float.Parse("abcd");
affiche(f1.ToString());
} catch (Exception e) {
affiche("Erreur : " + e.Message);
}
}// fim main
public static void affiche(string S) {
Console.Out.WriteLine("S={0}",S);
}
}// fim da classe
}
Nas linhas 30 a 32, trata-se a eventual exceção que possa ocorrer. e.Message é a mensagem de erro associada à exceção e.
Os resultados obtidos são os seguintes:
Observe-se que os números reais na forma de cadeia de caracteres devem utilizar a vírgula e não o ponto decimal. Assim, escrever-se-á
mas
3.2.5. Tabelas de dados
Uma tabela em C# é um objeto que permite agrupar, sob um mesmo identificador, dados do mesmo tipo. Sua declaração é a seguinte:
n é o número de dados que a matriz pode conter. A sintaxe Matriz[i] designa o dado nº i, onde i pertence ao intervalo [0,n-1]. Qualquer referência ao dado Tableau[i] em que i não pertença ao intervalo [0,n-1] provocará uma exceção. Uma matriz pode ser inicializada ao mesmo tempo em que é declarada:
ou, de forma mais simples:
As matrizes possuem uma propriedade Length, que corresponde ao número de elementos da matriz.
Um array bidimensional pode ser declarado da seguinte forma:
Type[,] array = new Type[n,m];
onde n é o número de linhas e m, o número de colunas. A sintaxe Tableau[i,j] designa o elemento j da linha i da matriz. A matriz bidimensional também pode ser inicializada ao mesmo tempo em que é declarada:
ou, de forma mais simples:
O número de elementos em cada uma das dimensões pode ser obtido pelo método GetLength(i), em que i=0 representa a dimensão correspondente ao primeiro índice, i=1 a dimensão correspondente ao segundo índice, …
O número total de dimensões é obtido com a propriedade Rank, e o número total de elementos com a propriedade Length.
Uma matriz de matrizes é declarada da seguinte forma:
Type[][] array = new Type[n][];
A declaração acima cria uma matriz com n linhas. Cada elemento matriz[i] é uma referência de matriz unidimensional. Essas referências matriz[i] não são inicializadas na declaração acima. Elas têm como valor a referência nula.
O exemplo abaixo ilustra a criação de uma matriz de matrizes:
// uma matriz de matrizes
string[][] noms = new string[3][];
for (int i = 0; i < noms.Length; i++) {
noms[i] = new string[i + 1];
}//for
// inicialização
for (int i = 0; i < noms.Length; i++) {
for (int j = 0; j < noms[i].Length; j++) {
noms[i][j] = "nom" + i + j;
}//for j
}//for i
- linha 2: um array noms com 3 elementos do tipo string[][]. Cada elemento é um ponteiro de array (uma referência de objeto) cujos elementos são do tipo string[].
- linhas 3-5: os 3 elementos da matriz noms são inicializados. Cada um deles agora “aponta” para uma matriz de elementos do tipo string[]. noms[i][j] é o elemento j da matriz do tipo string [] referenciada por noms[i].
- linha 9: inicialização do elemento nomes[i][j] dentro de um ciclo duplo. Aqui, noms[i] é um array de i+1 elementos. Como noms[i] é um array, noms[i].Length representa o número de elementos.
Aqui está um exemplo que reúne os três tipos de matrizes que acabamos de apresentar:
using System;
namespace Chap1 {
// matrizes
using System;
// classe de teste
public class P02 {
public static void Main() {
// um array unidimensional inicializado
int[] entiers = new int[] { 0, 10, 20, 30 };
for (int i = 0; i < entiers.Length; i++) {
Console.Out.WriteLine("entiers[{0}]={1}", i, entiers[i]);
}//para
// um array bidimensional inicializado
double[,] réels = new double[,] { { 0.5, 1.7 }, { 8.4, -6 } };
for (int i = 0; i < réels.GetLength(0); i++) {
for (int j = 0; j < réels.GetLength(1); j++) {
Console.Out.WriteLine("réels[{0},{1}]={2}", i, j, réels[i, j]);
}//for j
}//for i
// uma matriz de matrizes
string[][] noms = new string[3][];
for (int i = 0; i < noms.Length; i++) {
noms[i] = new string[i + 1];
}//para
// inicialização
for (int i = 0; i < noms.Length; i++) {
for (int j = 0; j < noms[i].Length; j++) {
noms[i][j] = "nom" + i + j;
}//para j
}//para i
// exibição
for (int i = 0; i < noms.Length; i++) {
for (int j = 0; j < noms[i].Length; j++) {
Console.Out.WriteLine("noms[{0}][{1}]={2}", i, j, noms[i][j]);
}//para j
}//para i
}//Principal
}//classe
}//namespace
Ao executar, obtemos os seguintes resultados:
3.3. As instruções básicas do C#
Distinguem-se
1 as instruções elementares executadas pelo computador.
2 as instruções de controle do andamento do programa.
As instruções elementares ficam claras quando se analisa a estrutura de um microcomputador e de seus periféricos.
![]() |
-
leitura de informações provenientes do teclado
-
processamento de informações
-
exibição de informações na tela
3.3.1. Gravação na tela
Existem diferentes instruções para exibição na tela:
Console.Out.WriteLine(expression)
Console.WriteLine(expression)
Console.Error.WriteLine (expression)
onde expression representa qualquer tipo de dado que possa ser convertido em uma sequência de caracteres para ser exibido na tela. Todos os objetos em C# ou .NET possuem um método ToString() que é utilizado para realizar essa conversão.
A classe System.Console fornece acesso às operações de gravação na tela (Write, WriteLine). A classe Console possui duas propriedades, Out e Error, que são fluxos de gravação do tipo TextWriter:
- Console.WriteLine() é equivalente a Console.Out.WriteLine() e grava no fluxo Out normalmente associado à tela.
- Console.Error.WriteLine() grava no fluxo Error, que normalmente também está associado à tela.
Os fluxos Out e Error podem ser redirecionados para arquivos de texto durante a execução do programa, como veremos em breve.
3.3.2. Leitura de dados digitados no teclado
O fluxo de dados proveniente do teclado é designado pelo objeto Console.In, do tipo TextReader. Esse tipo de objeto permite ler uma linha de texto com o método ReadLine:
A classe Console oferece um método ReadLine associado por padrão ao fluxo In. Portanto, é possível escrever:
A linha digitada no teclado é armazenada na variável ligne e pode, em seguida, ser utilizada pelo programa. O fluxo In pode ser redirecionado para um arquivo, assim como os fluxos Out e Error.
3.3.3. Exemplo de entradas e saídas
Aqui está um pequeno programa para ilustrar as operações de entrada e saída do teclado/tela:
using System;
namespace Chap1 {
// classe de teste
public class P03 {
public static void Main() {
// gravação no fluxo Out
object obj = new object();
Console.Out.WriteLine(obj);
// gravação no fluxo Error
int i = 10;
Console.Error.WriteLine("i=" + i);
// leitura de uma linha digitada no teclado
Console.Write("Tapez une ligne : ");
string ligne = Console.ReadLine();
Console.WriteLine("ligne={0}", ligne);
}//fim da função main
}//fim da classe
}
- linha 9: obj é uma referência de objeto
- linha 10: obj é exibido na tela. Por padrão, é chamado o método obj.ToString().
- linha 14: também é possível escrever:
Console.Error.WriteLine("i={0}",i);
O primeiro parâmetro “i={0}” é o formato de exibição; os demais parâmetros são as expressões a serem exibidas. Os elementos {n} são parâmetros “posicionais”. Na execução, o parâmetro {n} é substituído pelo valor da expressão nº n.
O resultado da execução é o seguinte:
- linha 1: a exibição gerada pela linha 10 do código. O método obj.ToString() exibiu o nome do tipo da variável obj: System.Object. O tipo object é um alias em C# do tipo .NET System.Object.
3.3.4. Redirecionamento de E/S
Existem, sob DOS e UNIX, três dispositivos padrão denominados:
- dispositivo de entrada padrão — designa, por padrão, o teclado e possui o nº 0
- dispositivo de saída padrão — designa, por padrão, a tela e possui o nº 1
- dispositivo de erro padrão — designa, por padrão, a tela e possui o número 2
Em C#, o fluxo de gravação Console.Out grava no dispositivo 1, o fluxo de gravação Console.Error grava no dispositivo 2 e o fluxo de leitura Console.In lê os dados provenientes do dispositivo 0.
Ao executar um programa no DOS ou no Unix, é possível definir quais serão os dispositivos 0, 1 e 2 para o programa em execução. Consideremos a seguinte linha de comando:
Atrás dos argumentos argi do programa pg, é possível redirecionar os dispositivos de E/S padrão para arquivos:
o fluxo de entrada padrão nº 0 é redirecionado para o arquivo in.txt. No programa, o fluxo Console.In, portanto, obterá seus dados do arquivo in.txt. | |
redireciona a saída nº 1 para o arquivo out.txt. Isso faz com que, no programa, o fluxo Console.Out grave seus dados no arquivo out.txt | |
o mesmo, mas os dados gravados são adicionados ao conteúdo atual do arquivo out.txt. | |
redireciona a saída nº 2 para o arquivo error.txt. Isso faz com que, no programa, o fluxo Console.Error grave seus dados no arquivo error.txt | |
O mesmo, mas os dados gravados são adicionados ao conteúdo atual do arquivo error.txt. | |
Os dispositivos 1 e 2 são ambos redirecionados para arquivos |
Observe-se que, para redirecionar os fluxos de E/S do programa pg para arquivos, o programa pg não precisa ser modificado. É o sistema operacional que define a natureza dos dispositivos 0, 1 e 2. Consideremos o seguinte programa:
using System;
namespace Chap1 {
// redirecionamentos
public class P04 {
public static void Main(string[] args) {
// leitura do fluxo In
string data = Console.In.ReadLine();
// gravação no fluxo de saída
Console.Out.WriteLine("écriture dans flux Out : " + data);
// gravação no fluxo de erros
Console.Error.WriteLine("écriture dans flux Error : " + data);
}//Principal
}//classe
}
Vamos gerar o executável a partir desse código-fonte:
![]() |
- em [1]: o executável é criado clicando com o botão direito do mouse no projeto / Build
- em [2]: em uma janela do DOS, o executável 04.exe foi criado na pasta bin/Release do projeto.
Digite os seguintes comandos na janela do DOS [2]:
- linha 1: inserimos a string test no arquivo in.txt
- linhas 2-3: exibimos o conteúdo do arquivo in.txt para verificação
- linha 4: execução do programa 04.exe. O fluxo In é redirecionado para o arquivo in.txt, o fluxo Out para o arquivo out.txt, o fluxo Error para o arquivo err.txt. A execução não gera nenhuma exibição.
- linhas 5-6: conteúdo do arquivo out.txt. Esse conteúdo nos mostra que:
- o arquivo in.txt foi lido
- a saída para a tela foi redirecionada para o arquivo out.txt
- linhas 7-8: verificação semelhante para o arquivo err.txt
Vemos claramente que os fluxos Out e In não gravam nos mesmos dispositivos, já que foi possível redirecioná-los separadamente.
3.3.5. Atribuição do valor de uma expressão a uma variável
Aqui, estamos interessados na operação variable=expression;
A expressão pode ser do tipo: aritmética, relacional, booleana, de caracteres
3.3.5.1. Interpretação da operação de atribuição
A operação variable=expression;
é, por si só, uma expressão cuja avaliação ocorre da seguinte maneira:
- a parte direita da atribuição é avaliada: o resultado é um valor V.
- o valor V é atribuído à variável
- o valor V é também o valor da atribuição, vista agora como uma expressão.
É assim que a operação
é válida. Devido à prioridade, o operador = mais à direita será avaliado. Portanto, temos
A expressão V2=expressão é avaliada e tem como valor V. A avaliação dessa expressão provocou a atribuição de V a V2. O operador = seguinte é então avaliado na forma:
O valor dessa expressão ainda é V. Sua avaliação resulta na atribuição de V a V1.
Assim, a operação V1=V2=expressão
é uma expressão cuja avaliação
- provoca a atribuição do valor de expression às variáveis V1 e V2
- retorna como resultado o valor expression.
É possível generalizar para uma expressão do tipo:
3.3.5.2. Expressão aritmética
Os operadores das expressões aritméticas são os seguintes:
-
adição
-
subtração
* multiplicação
/ divisão: o resultado é o quociente exato se pelo menos um dos operandos for real. Se ambos os operandos forem inteiros, o resultado é o quociente inteiro. Assim, 5/2 → 2 e 5,0/2 → 2,5.
% divisão: o resultado é o resto, independentemente da natureza dos operandos, sendo o quociente inteiro. Trata-se, portanto, da operação módulo.
Existem diversas funções matemáticas. Aqui estão algumas delas:
raiz quadrada | |
Cosseno | |
Senus | |
Tangente | |
x elevado a y (x > 0) | |
Exponencial | |
Logaritmo natural | |
valor absoluto |
etc...
Todas essas funções estão definidas em uma classe C# chamada Math. Ao utilizá-las, é necessário antepor o nome da classe em que estão definidas. Assim, escreveremos:
A definição completa da classe Math é a seguinte:





3.3.5.3. Prioridades na avaliação de expressões aritméticas
A prioridade dos operadores na avaliação de uma expressão aritmética é a seguinte (da mais alta à mais baixa):
Os operadores de um mesmo bloco [ ] têm a mesma prioridade.
3.3.5.4. Expressões relacionais
Os operadores são os seguintes:
prioridades dos operadores
O resultado de uma expressão relacional é o valor booleano false se a expressão for falsa; caso contrário, é true.
Comparação de dois caracteres
Sejam dois caracteres C1 e C2. É possível compará-los com os operadores
São, portanto, seus códigos Unicode — que são números — que são comparados. De acordo com a ordem Unicode, temos as seguintes relações:
Comparação de duas cadeias de caracteres
Elas são comparadas caractere por caractere. A primeira desigualdade encontrada entre dois caracteres determina uma desigualdade no mesmo sentido nas cadeias.
Exemplos:
Suponhamos que se queira comparar as sequências “Gato” e “Cão”
![]() |
Essa última desigualdade permite afirmar que “Gato” < “Cão”.
Suponha que se queira comparar as cadeias “Gato” e “Gatinho”. Há igualdade o tempo todo até que a cadeia “Gato” seja esgotada. Nesse caso, a cadeia esgotada é declarada como a mais “pequena”. Temos, portanto, a relação
Funções de comparação de duas cadeias de caracteres
É possível usar os operadores relacionais == e != para verificar se duas cadeias de caracteres são iguais ou não, ou ainda o método Equals da classe System.String. Para as relações < <= > >=, é necessário utilizar o método CompareTo da classe System.String:
using System;
namespace Chap1 {
class P05 {
static void Main(string[] args) {
string chaine1="chat", chaine2="chien";
int n = chaine1.CompareTo(chaine2);
bool egal = chaine1.Equals(chaine2);
Console.WriteLine("i={0}, egal={1}", n, egal);
Console.WriteLine("chien==chaine1:{0},chien!=chaine2:{1}", "chien"==chaine1,"chien" != chaine2);
}
}
}
Na linha 7, a variável i terá o valor:
0 se as duas cadeias forem iguais
1 se a sequência n.º 1 for maior que a sequência n.º 2
-1 se a sequência n.º 1 for menor que a sequência n.º 2
Na linha 8, a variável egal terá o valor true se as duas cadeias forem iguais; caso contrário, terá o valor false. Na linha 10, utilizam-se os operadores == e != para verificar se duas cadeias são iguais ou não.
Resultados da execução:
3.3.5.5. Expressões booleanas
Os operadores que podem ser utilizados são AND (&&) OR (||) NOT (!). O resultado de uma expressão booleana é um valor booleano.
Prioridades dos operadores:
- !
- &&
- ||
double x = 3.5;
bool valide = x > 2 && x < 4;
Os operadores relacionais têm prioridade sobre os operadores && e ||.
3.3.5.6. Operações em bits
Os operadores
Sejam i e j dois inteiros.
desloca i de n bits para a esquerda. Os bits de entrada são zeros. | |
desloca i n bits para a direita. Se i for um inteiro com sinal (signed char, int, long), o bit de sinal é preservado. | |
realiza a operação lógica ET entre i e j, bit a bit. | |
realiza a operação lógica bit a bit entre i e j, resultando em OU. | |
completa i com 1 | |
gera o OU a partir do EXCLUSIF de i e j |
Seja o seguinte código:
short i = 100, j = -13;
ushort k = 0xF123;
Console.WriteLine("i=0x{0:x4}, j=0x{1:x4}, k=0x{2:x4}", i,j,k);
Console.WriteLine("i<<4=0x{0:x4}, i>>4=0x{1:x4},k>>4=0x{2:x4},i&j=0x{3:x4},i|j=0x{4:x4},~i=0x{5:x4},j<<2=0x{6:x4},j>>2=0x{7:x4}", i << 4, i >> 4, k >> 4, (short)(i & j), (short)(i | j), (short)(~i), (short)(j << 2), (short)(j >> 2));
- o formato {0:x4} exibe o parâmetro nº 0 no formato hexadecimal (x) com 4 caracteres (4).
Os resultados da execução são os seguintes:
3.3.5.7. Combinação de operadores
a=a+b pode ser escrito como a+=b
a=a-b pode ser escrito como a-=b
O mesmo se aplica aos operadores /, %, *, <<, >>, &, |, ^. Assim, a=a/2; pode ser escrito como a/=2;
3.3.5.8. Operadores de incremento e decremento
A notação variable++ significa variable=variable+1 ou ainda variable+=1
A notação variable-- significa variable=variable-1 ou ainda variable-=1.
3.3.5.9. O operador ternário?
A expressão
é avaliada da seguinte forma:
1 a expressão expr_cond é avaliada. Trata-se de uma expressão condicional cujo valor é vrai ou faux
2 Se for verdadeira, o valor da expressão é o de expr1, e expr2 não é avaliada.
3 Se for falsa, ocorre o contrário: o valor da expressão é o de expr2 e expr1 não é avaliado.
A operação i=(j>4 ? j+1:j-1); atribuirá à variável i: j+1 se j>4, j-1 caso contrário. É o mesmo que escrever if(j>4) i=j+1; else i=j-1;, mas é mais conciso.
3.3.5.10. Prioridade geral dos operadores
gd | |
dg | |
dg | |
gd | |
gd | |
gd | |
gd | |
gd | |
gd | |
gd | |
gd | |
gd | |
gd | |
dg | |
dg |
gd indica que, em caso de igualdade de prioridade, é observada a regra de prioridade da esquerda para a direita. Isso significa que, quando em uma expressão há operadores com a mesma prioridade, o operador mais à esquerda na expressão é avaliado primeiro. dg indica a regra de prioridade da direita para a esquerda.
3.3.5.11. Mudanças de tipo
É possível, em uma expressão, alterar temporariamente a codificação de um valor. Isso é chamado de alteração do tipo de um dado ou, em inglês, “type casting”. A sintaxe para alterar o tipo de um valor em uma expressão é a seguinte:
O valor assume, então, o tipo indicado. Isso resulta em uma alteração na codificação do valor.
using System;
namespace Chap1 {
class P06 {
static void Main(string[] args) {
int i = 3, j = 4;
float f1=i/j;
float f2=(float)i/j;
Console.WriteLine("f1={0}, f2={1}",f1,f2);
}
}
}
- na linha 7, f1 terá o valor 0,0. A divisão 3/4 é uma divisão inteira, uma vez que ambos os operandos são do tipo int.
- linha 8, (float)i é o valor de i convertido para float. Agora, temos uma divisão entre um número real do tipo float e um inteiro do tipo int. É a divisão entre números reais que é então realizada. O valor de j também será convertido para o tipo float, e então será feita a divisão entre os dois reais. f2 terá, então, o valor 0,75.
Aqui estão os resultados da execução:
Na operação (float)i:
- i é um valor codificado exatamente em 2 bytes
- (float); i é o mesmo valor codificado de forma aproximada como número real em 4 bytes
Portanto, ocorre uma transcodificação do valor de i. Essa transcodificação ocorre apenas durante o cálculo, já que a variável i mantém sempre seu tipo int.
3.4. As instruções de controle do andamento do programa
3.4.1. Parada
O método Exit, definido na classe Environment, permite interromper a execução de um programa.
syntaxe void Exit(int status)
action arrête le processus en cours et rend la valeur status au processus père
Exit encerra o processo em andamento e devolve o controle ao processo chamador. O valor de status pode ser utilizado por este. Em DOS, essa variável de status é retornada na variável de sistema ERRORLEVEL, cujo valor pode ser verificado em um arquivo em lote. No Unix, com o interpretador de comandos Shell Bourne, é a variável $? que recupera o valor de status.
interromperá a execução do programa com um valor de status igual a 0.
3.4.2. Estrutura de escolha simples
observações:
- a condição está entre parênteses.
- cada ação é terminada por ponto-e-vírgula.
- As chaves não são encerradas por ponto-e-vírgula.
- As chaves só são necessárias se houver mais de uma ação.
- a cláusula else pode estar ausente.
- Não há cláusula then.
O equivalente algorítmico dessa estrutura é a estrutura “se... então... senão”:
![]() |
Exemplo
if (x>0) { nx=nx+1;sx=sx+x;} else dx=dx-x;
É possível aninhar as estruturas de escolha:
Às vezes surge o seguinte problema:
using System;
namespace Chap1 {
class P07 {
static void Main(string[] args) {
int n = 5;
if (n > 1)
if (n > 6)
Console.Out.WriteLine(">6");
else Console.Out.WriteLine("<=6");
}
}
}
No exemplo anterior, a linha else da linha 10 se refere a qual if? A regra é que um else sempre se refere ao if mais próximo: if(n>6), na linha 8, no exemplo. Consideremos outro exemplo:
if (n2 > 1) {
if (n2 > 6) Console.Out.WriteLine(">6");
} else Console.Out.WriteLine("<=1");
Aqui, queríamos associar um else ao if(n2>1), e não um else ao if(n2>6). Devido à observação anterior, somos obrigados a colocar chaves no if(n2>1) {...} else ...
3.4.3. Estrutura de casos
A sintaxe é a seguinte:
switch(expression) {
case v1:
actions1;
break;
case v2:
actions2;
break;
. .. .. .. .. ..
default:
actions_sinon;
break;
}
notas
- o valor da expressão de controle do switch pode ser um número inteiro, um caractere ou uma sequência de caracteres
- A expressão de controle está entre parênteses.
- A cláusula default pode estar ausente.
- Os valores vi são valores possíveis da expressão. Se a expressão tiver o valor vi, as ações associadas à cláusula case vi serão executadas.
- A instrução break faz com que se saia da estrutura de caso.
- Cada bloco de instruções associado a um valor vi deve terminar com uma instrução de ramificação (break, goto, return, ...); caso contrário, o compilador sinaliza um erro.
Exemplo
Em algoritmos
selon la valeur de choix
cas 0
fin du module
cas 1
exécuter module M1
cas 2
exécuter module M2
sinon
erreur<--vrai
findescas
Em C#
3.4.4. Estruturas de repetição
3.4.4.1. Número conhecido de repetições
Estrutura for
A sintaxe é a seguinte:
for (i=id;i<=if;i=i+ip){
actions;
}
Notas
- os três argumentos do for estão entre parênteses e separados por ponto-e-vírgula.
- Cada ação do for é terminada por um ponto-e-vírgula.
- A chave só é necessária se houver mais de uma ação.
- A chave não é seguida de ponto-e-vírgula.
O equivalente algorítmico é a estrutura pour:
que pode ser traduzida por uma estrutura tantque:
Estrutura foreach
A sintaxe é a seguinte:
foreach (Type variable in collection)
instructions;
}
Notas
- collection é uma coleção de objetos enumerável. A coleção de objetos enumerável que já conhecemos é o array
- Type é o tipo dos objetos da coleção. No caso de um array, seria o tipo dos elementos do array
- variable é uma variável local do loop que assumirá sucessivamente todos os valores da coleção.
Assim, o código a seguir:
string[] amis = { "paul", "hélène", "jacques", "sylvie" };
foreach (string nom in amis) {
Console.WriteLine(nom);
}
exibiria:
3.4.4.2. Número de repetições desconhecido
Existem diversas estruturas em C# para esse caso.
Estrutura while
while(condition){
actions;
}
O loop é executado enquanto a condição for verdadeira. O loop pode nunca ser executado.
Notas:
- a condição está entre parênteses.
- cada ação é terminada por ponto-e-vírgula.
- A chave só é necessária se houver mais de uma ação.
- A chave não é seguida de ponto-e-vírgula.
A estrutura algorítmica correspondente é a estrutura “enquanto”:
Estrutura de repetição até (do while)
A sintaxe é a seguinte:
do{
instructions;
}while(condition);
O ciclo é repetido até que a condição se torne falsa. Aqui, o ciclo é executado pelo menos uma vez.
Notas
- a condição está entre parênteses.
- Cada ação é terminada por ponto-e-vírgula.
- A chave só é necessária se houver mais de uma ação.
- A chave não é seguida de ponto-e-vírgula.
A estrutura algorítmica correspondente é a estrutura “repetir … até”:
Estrutura para “for”
A sintaxe é a seguinte:
for(instructions_départ;condition;instructions_fin_boucle){
instructions;
}
O loop é executado enquanto a condição for verdadeira (avaliada antes de cada iteração do loop). Instructions_départ são executadas antes de entrar no loop pela primeira vez. Instructions_fin_boucle são executadas após cada iteração do loop.
notas
- as diferentes instruções em instructions_depart e instructions_fin_boucle são separadas por vírgulas.
A estrutura algorítmica correspondente é a seguinte:
Exemplos
Todos os trechos de código a seguir calculam a soma dos primeiros 10 números inteiros.
int i, somme, n=10;
for (i = 1, somme = 0; i <= n; i = i + 1)
somme = somme + i;
for (i = 1, somme = 0; i <= n; somme = somme + i, i = i + 1) ;
i = 1; somme = 0;
while (i <= n) { somme += i; i++; }
i = 1; somme = 0;
do somme += i++;
while (i <= n);
3.4.4.3. Instruções para gerenciamento de loop
sai do loop for, while, do ... while. | |
avança para a próxima iteração dos loops for, while, do ... while |
3.5. Gerenciamento de exceções
Muitas funções em C# podem gerar exceções, ou seja, erros. Quando uma função pode gerar uma exceção, o programador deve gerenciá-la com o objetivo de obter programas mais resistentes a erros: é preciso sempre evitar que um aplicativo “trave” de forma inesperada.
O tratamento de uma exceção é feito de acordo com o esquema a seguir:
try{
code susceptible de générer une exception
} catch (Exception e){
traiter l'exception e
}
instruction suivante
Se a função não gerar uma exceção, passa-se para a instrução seguinte; caso contrário, passa-se para o corpo da cláusula catch e, em seguida, para a instrução seguinte. Observe os seguintes pontos:
- e é um objeto do tipo Exception ou derivado. É possível ser mais preciso utilizando tipos como IndexOutOfRangeException, FormatException, SystemException, etc.: existem vários tipos de exceções. Ao escrever catch (Exception e), indicamos que queremos tratar todos os tipos de exceções. Se o código da cláusula try for suscetível de gerar vários tipos de exceções, podemos querer ser mais precisos, tratando a exceção com várias cláusulas catch:
try{
code susceptible de générer les exceptions
} catch ( IndexOutOfRangeException e1){
traiter l'exception e1
}
} catch ( FormatException e2){
traiter l'exception e2
}
instruction suivante
- É possível adicionar uma cláusula `finally` às cláusulas try/catch:
try{
code susceptible de générer une exception
} catch (Exception e){
traiter l'exception e
}
finally{
code exécuté après try ou catch
}
instruction suivante
Haja ou não exceção, o código da cláusula finally será sempre executado.
- Na cláusula catch, talvez não se queira utilizar o objeto Exception disponível. Em vez de escrever catch (Exception e){..}, escreve-se então catch(Exception){...} ou, mais simplesmente, catch {...}.
- A classe Exception possui uma propriedade Message, que é uma mensagem detalhando o erro ocorrido. Portanto, se quisermos exibi-la, escreveremos:
catch (Exception ex){
Console.WriteLine("L'erreur suivante s'est produite : {0}",ex.Message);
...
}//catch
- A classe Exception possui um método ToString que retorna uma sequência de caracteres indicando o tipo da exceção, bem como o valor da propriedade Message. Assim, pode-se escrever:
catch (Exception ex){
Console.WriteLine("L'erreur suivante s'est produite : {0}", ex.ToString());
...
}//catch
Também é possível escrever:
O compilador atribuirá ao parâmetro {0} o valor ex.ToString().
O exemplo a seguir mostra uma exceção gerada pelo uso de um elemento de tabela inexistente:
using System;
namespace Chap1 {
class P08 {
static void Main(string[] args) {
// declaração e inicialização de uma matriz
int[] tab = { 0, 1, 2, 3 };
int i;
// exibição da matriz com um for
for (i = 0; i < tab.Length; i++)
Console.WriteLine("tab[{0}]={1}", i, tab[i]);
// exibição de tabela com um for each
foreach (int élmt in tab) {
Console.WriteLine(élmt);
}
// geração de uma exceção
try {
tab[100] = 6;
} catch (Exception e) {
Console.Error.WriteLine("L'erreur suivante s'est produite : " + e);
return;
}//try-catch
finally {
Console.WriteLine("finally ...");
}
}
}
}
Na linha 18 acima, será gerada uma exceção porque a matriz tab não possui o elemento nº 100. A execução do programa produz os seguintes resultados:
- linha 9: ocorreu a exceção [System.IndexOutOfRangeException]
- linha 11: a cláusula finally (linhas 23-25) do código foi executada, embora na linha 21 houvesse uma instrução return para sair do método. Vale ressaltar que a cláusula finally é sempre executada.
Aqui está outro exemplo em que se lida com a exceção provocada pela atribuição de uma sequência de caracteres a uma variável do tipo inteiro, quando a sequência não representa um número inteiro:
using System;
namespace Chap1 {
class P08 {
static void Main(string[] args) {
// exemplo 2
// Solicita-se o nome
Console.Write("Nom : ");
// leitura da resposta
string nom = Console.ReadLine();
// pergunta-se a idade
int age = 0;
bool ageOK = false;
while (!ageOK) {
// pergunta
Console.Write("âge : ");
// leitura e verificação da resposta
try {
age = int.Parse(Console.ReadLine());
ageOK = age>=1;
} catch {
}//try-catch
if (!ageOK) {
Console.WriteLine("Age incorrect, recommencez...");
}
}//while
// exibição final
Console.WriteLine("Vous vous appelez {0} et vous avez {1} an(s)",nom,age);
}
}
}
- linhas 15-27: o loop de entrada da idade de uma pessoa
- linha 20: o texto digitado no teclado é convertido em número inteiro pelo método int.Parse. Esse método lança uma exceção se a conversão não for possível. Por isso, a operação foi colocada em um try/catch.
- linhas 22-23: se uma exceção for lançada, entramos no `catch`, onde nada é feito. Assim, a variável booleana ageOK, definida como false na linha 14, permanecerá como false.
- linha 21: se chegarmos a esta linha, significa que a conversão de string para int foi bem-sucedida. Verifica-se, no entanto, se o inteiro obtido é realmente maior ou igual a 1.
- linhas 24-26: uma mensagem de erro é exibida se a idade estiver incorreta.
Alguns resultados da execução:
3.6. Aplicativo de exemplo - V1
Propõe-se escrever um programa que permita calcular o imposto de um contribuinte. Consideramos o caso simplificado de um contribuinte que tenha apenas seu salário a declarar (dados de 2004 referentes aos rendimentos de 2003):
- calcula-se o número de cotas do empregado nbParts = nbEnfants/2 + 1 se ele for solteiro, nbEnfants/2 + 2 se for casado, onde nbEnfants é o número de filhos.
- se ele tiver pelo menos três filhos, recebe mais meia parcela
- calcula-se sua renda tributável R = 0,72 * S, sendo que S é seu salário anual
- calcula-se seu coeficiente familiar QF = R/nbParts
- calcula-se seu imposto I. Consideremos a tabela a seguir:
4262 | 0 | 0 |
8382 | 0,0683 | 291,09 |
14.753 | 0,1914 | 1.322,92 |
23.888 | 0,2826 | 2.668,39 |
38.868 | 0,3738 | 4.846,98 |
47.932 | 0,4262 | 6.883,66 |
0 | 0,4809 | 9505,54 |
Cada linha possui 3 campos. Para calcular o imposto I, procura-se a primeira linha em que QF ≤ campo1. Por exemplo, se QF = 5000, será encontrada a linha
O imposto I é, então, igual a 0,0683*R - 291,09*nbParts. Se QF for tal que a relação QF <= campo1 nunca for verificada, então serão utilizados os coeficientes da última linha. Aqui:
0 0,4809 9505,54
o que resulta no imposto I = 0,4809 * R - 9505,54 * nbParts.
O programa em C# correspondente é o seguinte:
using System;
namespace Chap1 {
class Impots {
static void Main(string[] args) {
// tabelas de dados necessárias para o cálculo do imposto
decimal[] limites = { 4962M, 8382M, 14753M, 23888M, 38868M, 47932M, 0M };
decimal[] coeffR = { 0M, 0.068M, 0.191M, 0.283M, 0.374M, 0.426M, 0.481M };
decimal[] coeffN = { 0M, 291.09M, 1322.92M, 2668.39M, 4846.98M, 6883.66M, 9505.54M };
// recuperação do estado civil
bool OK = false;
string reponse = null;
while (!OK) {
Console.Write("Etes-vous marié(e) (O/N) ? ");
reponse = Console.ReadLine().Trim().ToLower();
if (reponse != "o" && reponse != "n")
Console.Error.WriteLine("Réponse incorrecte. Recommencez");
else OK = true;
}//while
bool marie = reponse == "o";
// número de filhos
OK = false;
int nbEnfants = 0;
while (!OK) {
Console.Write("Nombre d'enfants : ");
try {
nbEnfants = int.Parse(Console.ReadLine());
OK = nbEnfants >= 0;
} catch {
}// try
if (!OK) {
Console.WriteLine("Réponse incorrecte. Recommencez");
}
}// enquanto
// salário
OK = false;
int salaire = 0;
while (!OK) {
Console.Write("Salaire annuel : ");
try {
salaire = int.Parse(Console.ReadLine());
OK = salaire >= 0;
} catch {
}// try
if (!OK) {
Console.WriteLine("Réponse incorrecte. Recommencez");
}
}// while
// cálculo do número de cotas
decimal nbParts;
if (marie) nbParts = (decimal)nbEnfants / 2 + 2;
else nbParts = (decimal)nbEnfants / 2 + 1;
if (nbEnfants >= 3) nbParts += 0.5M;
// renda tributável
decimal revenu = 0.72M * salaire;
// quociente familiar
decimal QF = revenu / nbParts;
// busca da faixa de imposto correspondente a QF
int i;
int nbTranches = limites.Length;
limites[nbTranches - 1] = QF;
i = 0;
while (QF > limites[i]) i++;
// o imposto
int impots = (int)(coeffR[i] * revenu - coeffN[i] * nbParts);
// exibe-se o resultado
Console.WriteLine("Impôt à payer : {0} euros", impots);
}
}
}
- linhas 7-9: os valores numéricos são precedidos pelo sufixo M (Money) para que sejam do tipo decimal.
- linha 16:
- Console.ReadLine() transforma a string C1 digitada no teclado
- C1.Trim() remove os espaços no início e no final de C1 — retorna a string C2
- C2.ToLower() retorna a string C3, que é a string C2 convertida para letras minúsculas.
- linha 21: o booleano marie recebe o valor true ou false da relação reponse=="o"
- linha 29: a string digitada no teclado é convertida para o tipo int. Se a conversão falhar, uma exceção é lançada.
- linha 30: o booleano OK recebe o valor true ou false da relação nbEnfants>=0
- linhas 55-56: não é possível escrever simplesmente nbEnfants/2. Se nbEnfants fosse igual a 3, teríamos 3/2, uma divisão inteira que resultaria em 1 e não em 1,5. Portanto, escreve-se (decimal)nbEnfants para tornar real um dos operandos da divisão e, assim, obter uma divisão entre números reais.
Aqui estão alguns exemplos de execução:
Etes-vous marié(e) (O/N) ? oui
Réponse incorrecte. Recommencez
Etes-vous marié(e) (O/N) ? o
Nombre d'enfants : trois
Réponse incorrecte. Recommencez
Nombre d'enfants : 3
Salaire annuel : 60000 euros
Réponse incorrecte. Recommencez
Salaire annuel : 60000
Impôt à payer : 2959 euros
3.7. Argumentos do programa principal
A função principal Main pode aceitar como parâmetro uma matriz de strings: String[] (ou string[]). Essa matriz contém os argumentos da linha de comando utilizada para executar o aplicativo. Assim, se executarmos o programa P com o seguinte comando (DOS):
P arg0 arg1 … argn
e se a função Main for declarada da seguinte forma:
teremos args[0]="arg0", args[1]="arg1" … Veja um exemplo:
using System;
namespace Chap1 {
class P10 {
static void Main(string[] args) {
// listam-se os parâmetros recebidos
Console.WriteLine("Il y a " + args.Length + " arguments");
for (int i = 0; i < args.Length; i++) {
Console.Out.WriteLine("arguments[" + i + "]=" + args[i]);
}
}
}
}
Para passar argumentos ao código executado, proceda da seguinte forma:
![]() |
- em [1]: clique com o botão direito do mouse no projeto / Propriedades
- em [2]: guia [Debug]
- em [3]: inserir os argumentos
A execução produz os seguintes resultados:
Observe-se que a assinatura
é válida se a função Main não esperar parâmetros.
3.8. As enumerações
Uma enumeração é um tipo de dados cujo domínio de valores é um conjunto de constantes inteiras. Consideremos um programa que precisa gerenciar notas de uma prova. Seria um total de cinco: Passable, AssezBien, Bien, TrèsBien, Excellent.
Seria possível, então, definir uma enumeração para essas cinco constantes:
enum Mentions { Passable, AssezBien, Bien, TrèsBien, Excellent };
Internamente, essas cinco constantes são codificadas por números inteiros consecutivos, começando por 0 para a primeira constante, 1 para a seguinte, e assim por diante. Uma variável pode ser declarada para assumir esses valores na enumeração:
// uma variável que obtém seus valores na enumeração “Mentions”
Mentions maMention = Mentions.Passable;
É possível comparar uma variável com os diferentes valores possíveis da enumeração:
if (maMention == Mentions.Passable) {
Console.WriteLine("Peut mieux faire");
}
É possível obter todos os valores da enumeração:
// lista de menções na forma de cadeias de caracteres
foreach (Mentions m in Enum.GetValues(maMention.GetType())) {
Console.WriteLine(m);
}
Da mesma forma que o tipo simples int é equivalente à estrutura System.Int32, o tipo simples enum é equivalente à estrutura System.Enum. Essa estrutura possui um método estático GetValues que permite obter todos os valores de um tipo enumerado passado como parâmetro. Esse parâmetro deve ser um objeto do tipo **Type, que é uma classe de informações sobre o tipo de um dado. O tipo de uma variável v é obtido por meio de v.GetType(). O tipo de um tipo T é obtido por meio de typeof(T). Portanto, aqui maMention.GetType() retorna o objeto Type da enumeração Mentions e Enum.GetValues(maMention.GetType()) a lista de valores da enumeração Mentions.
Se escrevermos agora
//lista das menções na forma de números inteiros
foreach (int m in Enum.GetValues(typeof(Mentions))) {
Console.WriteLine(m);
}
Linha 2, a variável do loop é do tipo inteiro. Obtém-se, então, a lista de valores da enumeração na forma de inteiros. O objeto do tipo System.Type, correspondente ao tipo de dados Mentions, é obtido por typeof(Mentions). Poderíamos ter escrito, como anteriormente, maMention.GetType().
O programa a seguir ilustra o que acabou de ser explicado:
using System;
namespace Chap1 {
class P11 {
enum Mentions { Passable, AssezBien, Bien, TrèsBien, Excellent };
static void Main(string[] args) {
// uma variável cujos valores são obtidos da enumeração “Mentions”
Mentions maMention = Mentions.Passable;
// exibição do valor da variável
Console.WriteLine("mention=" + maMention);
// teste com valor da enumeração
if (maMention == Mentions.Passable) {
Console.WriteLine("Peut mieux faire");
}
// lista de menções na forma de cadeias de caracteres
foreach (Mentions m in Enum.GetValues(maMention.GetType())) {
Console.WriteLine(m);
}
//lista de menções na forma de números inteiros
foreach (int m in Enum.GetValues(typeof(Mentions))) {
Console.WriteLine(m);
}
}
}
}
Os resultados da execução são os seguintes:
3.9. Passagem de parâmetros para uma função
Aqui, vamos nos concentrar na forma como os parâmetros são passados para uma função. Consideremos a seguinte função estática:
private static void ChangeInt(int a) {
a = 30;
Console.WriteLine("Paramètre formel a=" + a);
}
Na definição da função, linha 1, a é chamado de parâmetro formal. Ele existe apenas para fins da definição da função changeInt. Ele poderia muito bem ter sido chamado de b. Consideremos agora uma utilização dessa função:
public static void Main() {
int age = 20;
ChangeInt(age);
Console.WriteLine("Paramètre effectif age=" + age);
}
Aqui, na instrução da linha 3, ChangeInt(idade), age é o parâmetro efetivo que transmitirá seu valor ao parâmetro formal a. Estamos interessados em saber como um parâmetro formal recupera o valor de um parâmetro efetivo.
3.9.1. Passagem por valor
O exemplo a seguir mostra que os parâmetros de uma função são, por padrão, passados por valor, ou seja, o valor do parâmetro efetivo é copiado para o parâmetro formal correspondente. Temos duas entidades distintas. Se a função modificar o parâmetro formal, o parâmetro efetivo não sofre nenhuma alteração.
using System;
namespace Chap1 {
class P12 {
public static void Main() {
int age = 20;
ChangeInt(age);
Console.WriteLine("Paramètre effectif age=" + age);
}
private static void ChangeInt(int a) {
a = 30;
Console.WriteLine("Paramètre formel a=" + a);
}
}
}
Os resultados obtidos são os seguintes:
O valor 20 do parâmetro efetivo age foi copiado para o parâmetro formal a (linha 10). Este foi, em seguida, modificado (linha 11). O parâmetro efetivo, por sua vez, permaneceu inalterado. Esse modo de passagem é adequado para parâmetros de entrada de uma função.
3.9.2. Passagem por referência
Em uma passagem por referência, o parâmetro efetivo e o parâmetro formal são uma única e mesma entidade. Se a função modificar o parâmetro formal, o parâmetro efetivo também será modificado. Em C#, ambos devem ser precedidos pela palavra-chave ref:
Veja um exemplo:
using System;
namespace Chap1 {
class P12 {
public static void Main() {
// exemplo 2
int age2 = 20;
ChangeInt2(ref age2);
Console.WriteLine("Paramètre effectif age2=" + age2);
}
private static void ChangeInt2(ref int a2) {
a2 = 30;
Console.WriteLine("Paramètre formel a2=" + a2);
}
}
}
e os resultados da execução:
O parâmetro efetivo acompanhou a modificação do parâmetro formal. Esse modo de passagem é adequado para parâmetros de saída de uma função.
3.9.3. Passagem por referência com a palavra-chave out
Consideremos o exemplo anterior, no qual a variável age2 não seria inicializada antes da chamada à função changeInt:
using System;
namespace Chap1 {
class P12 {
public static void Main() {
// exemplo 2
int age2;
ChangeInt2(ref age2);
Console.WriteLine("Paramètre effectif age2=" + age2);
}
private static void ChangeInt2(ref int a2) {
a2 = 30;
Console.WriteLine("Paramètre formel a2=" + a2);
}
}
}
Ao compilar esse programa, ocorre um erro:
É possível contornar esse problema atribuindo um valor inicial a age2. Também é possível substituir a palavra-chave ref pela palavra-chave out. Assim, indica-se que o parâmetro é exclusivamente um parâmetro de saída e, portanto, não precisa de valor inicial:
using System;
namespace Chap1 {
class P12 {
public static void Main() {
// exemplo 3
int age3;
ChangeInt3(out age3);
Console.WriteLine("Paramètre effectif age3=" + age3);
}
private static void ChangeInt3(out int a3) {
a3 = 30;
Console.WriteLine("Paramètre formel a3=" + a3);
}
}
}
Os resultados da execução são os seguintes:





