Académique Documents
Professionnel Documents
Culture Documents
objectivo principal deste capitulo introduzir os conceitos centrais da linguagem C#, incluindo os tipos de dados intrnsecos ( value-based e reference-based); construes de deciso e iterao; mecanismos de boxing e unboxing; e, tcnicas bsicas para construo de classes. Ao longo da exposio, aprenderemos tambm como se manipulam strings, arrays, enumeraes e estruturas. Para ilustrar aqueles fundamentos da linguagem, iremos introduzindo a biblioteca de classes base do .NET, e construiremos um conjunto de aplicaes utilizando os vrios namespaces. Uma vez tendo explicado como se integram os namespaces pr-definidos, este captulo termina mostrando como se organizam tipos especficos em namespaces definidos pelo utilizador.
1 de 96
Definimos um tipo (HelloClass) que suporta um nico mtodo designado Main. Toda as aplicaes C# devem conter uma classe que defina um mtodo Main, considerado pelo CLR como entry point da aplicao. Embora seja tecnicamente possvel que um projecto C# contenha mltiplas classes definindo mtodos Main, necessrio explicitar ao compilador de C# qual o mtodo a usar como entry point da aplicao para que este no emita um erro de compilao.
2 de 96
Uma vez que o mtodo Main foi definido como devolvendo um inteiro, deve devolver zero (sucesso) antes de terminar. Finalmente, podemos constatar que o C# mantm o formato dos comentrios usado em C e C++.
3 de 96
A forma como se define Main tem a ver com a forma como respondemos s duas perguntas seguintes: primeira, necessrio processar argumentos da linha de comando? Em caso afirmativo, estes sero armazenados no array de strings. A seguir, necessrio devolver um valor ao sistema quando Main termina? Em caso afirmativo, aquele mtodo devolve int e no void.
C# and the .NET Platform, 2. C# Language Fundamentals
4 de 96
Neste programa usamos a propriedade Length do tipo System.Array (como veremos adiante todos os arrays C# so alias do tipo System.Array) para determinar se foram passado argumentos na linha de comando. Se tiver sido passado pelo menos um argumento, o programa itera sobre cada item e faz output do contedo do array. Como alternativa, podemos iterar sobre o array de strings usando a construo C# foreach:
5 de 96
O programador o nico responsvel por determinar quais os parmetros da linha de comando a que a aplicao dever responder, e o que deve fazer com cada parmetro.
6 de 96
A palavra chave new tem a responsabilidade de reservar o nmero de bytes necessrio para o objecto especificado, e adquirir memria necessria no managed heap. No exemplo, reservmos dois objectos (c1 e c2) cada um dos quais aponta para uma instncia do tipo HelloClass. Tenha em ateno que as variveis classe C# so na realidade uma referncia para o objecto em memria, no o prprio objecto. C# and the .NET Platform, 2. C# Language Fundamentals
7 de 96
8 de 96
Examinando o output do programa podemos ver que o construtor por omisso inicia a informao de estado com os valores por omisso (zero), enquanto que o construtor especfico inicia os membros dados com os valores especificados pelo utilizador.
9 de 96
No se trata de uma omisso grave, mas sim da forma do .NET fazer as coisas. Tal como os programadores de Visual Basic e de Java, os programadores de C# no tm que fazer a destruio explcita dos objectos. O garbage collector do .NET libertar automaticamente a memria, razo pela qual o C# no usa a palavra chave delete.
10 de 96
Muitos dos nossos exemplo iniciais seguem este padro, porque o nosso objectivo manter o foco no tpico em discusso. No entanto, seria mais natural dividir a classe HelloClass em duas classes distintas: HelloClass e HelloApp. Usando a terminologia da programao orientada por objectos, este diviso designa-se separao de funes. Assim, podemos rescrever a aplicao do seguinte modo:
11 de 96
Quando se constrem aplicaes, comum definir um tipo que funciona como objecto aplicao (o tipo que define o mtodo Main) e outros tipos que constituem a prpria aplicao. Ainda, para manter o cdigo manejvel, cada tipo colocado num ficheiro *.cs prprio.
12 de 96
Outras linguagens que suportam a programao orientada por objectos, como o C++, no permitem esta forma de iniciao dos membros. Nesse caso, os programadores podem optar por escrever funes auxiliares privadas que so chamadas nos construtores. Outra soluo ser encaminhar as chamadas a um construtor para um outro que funcione como master (analisaremos esta tcnica no Captulo 3 durante a discusso da palavra chave this). Embora qualquer uma daquelas construes seja tambm vlida em C#, a iniciao explcita dos membros dados mais uma alternativa.
13 de 96
14 de 96
O primeiro parmetro de WriteLine representa o string de formatao que contm placeholders opcionais designados por {0}, {1}, {2}, e assim sucessivamente. Os restantes parmetros de WriteLine so os valores que pretendemos ver inseridos no respectivo placeholder (neste caso, um inteiro, um float e um string). Tenha tambm em considerao que o mtodo WriteLine tem uma definio que permite especificar os valores a atribuir aos placeholders como array de objectos. Assim, podemos representar qualquer numero de itens, do seguinte modo: C# and the .NET Platform, 2. C# Language Fundamentals
15 de 96
Cada placeholder poder conter vrios caracteres de formatao, como se mostra na Tabela 2-1. Estes caracteres de formatao so colocados dentro dos placeholders usando o caracter : como separador (por exemplo, {0:C}, {1:d}, {2:X}, e assim sucessivamente). Para ilustrar, analise o seguinte cdigo:
// Now make use of some format tags. public static void Main() { ... Console.WriteLine("C format: {0:C}", 99989.987); Console.WriteLine("D9 format: {0:D9}", 99999); Console.WriteLine("E format: {0:E}", 99999.76543); Console.WriteLine("F format: {0:F3}", 99999.9999); Console.WriteLine("N format: {0:N}", 99999); Console.WriteLine("X format: {0:X}", 99999); Console.WriteLine("x format: {0:x}", 99999); }
16 de 96
17 de 96
18 de 96
19 de 96
Foi criada uma varivel do tipo FOO (f1) que foi depois atribuda a outra varivel do tipo FOO (f2). Uma vez que FOO um tipo valor, ficamos com duas cpias do tipo FOO no stack, que podem ser manipuladas de forma independente. Por isso, quando se altera o valor de f2.x, o valor de f1.x no alterado. Os tipos referncia (que incluem as classes e as interfaces) so alojadas no managed heap. As cpias de tipos referncia so cpias superficiais, tendo por consequncia que mltiplas referncias fiquem a apontar a mesma localizao na memria. Para ilustrar altere a definio do tipo FOO de uma estrutura C# para uma classe:
class FOO { // Classes are always reference types. public int x, y; }
20 de 96
21 de 96
22 de 96
23 de 96
Como qualquer outra classe C#, System.Object define um conjunto de membros de instncia. Repare que alguns desses itens so declarados virtual e podem, por isso, ser sobrepostos pelas subclasses:
// The top-most class in the .NET world: System.Object. namespace System { public class Object { public Object(); public virtual Boolean Equals(Object obj); public virtual Int32 GetHashCode(); public Type GetType(); public virtual String ToString(); protected virtual void Finalize(); protected Object MemberwiseClone(); ... } ... }
24 de 96
Primeiro, repare que a implementao por omisso de ToString devolve simplesmente o nome do tipo (ObjTest). Em muitas situaes, as classes derivadas redefinem este mtodo para devolver um string que represente os valores dos seus dados internos. C# and the .NET Platform, 2. C# Language Fundamentals
25 de 96
Meaning in Life
By default this method returns true only if the items being compared refer to the same exact item in memory. Thus, Equals() is used to compare object references, not the state of the object. Typically, this method is overridden to return "true" only if the objects being compared have the same internal state values (that is, value-based semantics). Be aware that if you override Equals(), you should also override GetHashCode(). Returns an integer that identifies a specific object instance. This method returns a Type object that fully describes the object you are currently referencing. In short, this is a Runtime Type Identification (RTTI) method available to all objects (discussed in greater detail in Chapter 7). Returns a string representation of this object, using the <namespace>.<class name> format (termed the "fully qualified name"). If the type has not been defined within a namespace <class name> is returned. For the time being, you can understand this method (when overridden) is called to free any allocated resources before the object is destroyed. We talk more about the CLR garbage collector services in Chapter 3.
GetHashCode GetType
ToString
Finalize
26 de 96
27 de 96
28 de 96
Para comear, vamos redefinir o mtodo Object.ToString para devolver uma representao textual do estado do objecto. Examinaremos os detalhes da redefinio de mtodos no prximo captulo. Por agora, considere apenas que estamos a alterar o comportamento do mtodo ToString para a classe Person.
// Need to reference this namespace to access the StringBuilder type. using System.Text; // A Person class implements ToString() as so: class Person { ... // Overriding a method inherited from System.Object. public override string ToString() { StringBuilder sb = new StringBuilder(); sb.Append("[First Name = " + this.firstName); sb.Append(" LastName = " + this.lastName); sb.Append(" SSN = " + this.SSN); sb.Append(" Age = " + this.age + "]"); return sb.ToString(); } ... }
29 de 96
30 de 96
31 de 96
Static Members of System.Object Alm dos membros de instncia que acabmos de analisar, a classe System.Object define
dois membros estticos (Object.Equals e Object.ReferenceEquals)que testam tambm a igualdade com semntica de valor e com semntica de referncia. Considere o seguinte cdigo:
// Static members of System.Object. Person p3 = new Person("Sally", "Jones", "333", 4); Person p4 = new Person("Sally", "Jones", "333", 4); // Do P3 and P4 have the same state? TRUE! Console.WriteLine("P3 and P4 have same state: {0}", object. Equals(p3, p4)); // Are they the same object in memory? FALSE! Console.WriteLine("P3 and P4 are pointing to the same object: {0}", object.ReferenceEquals(p3, p4));
32 de 96
int
uint long
Yes
No Yes
Int32
UInt32 Int64
-2,147,483,648 to +2,147,483,647
0 to 4,294,967,295 -9,223,372,036,854,775,808 to +9,223,372,036,854,775,807
33 de 96
ulong
char float double bool decimal string object
No
Yes Yes Yes Yes Yes Yes Yes
UInt64
Char Single Double Boolean Decimal String Object
0 to 18,446,744,073,709,551,615
U+0000 to U+ffff 1.5x10-45 to 3.4x1038 5.0x10-324 to 1.7x10308 true or false ((-296 to 296)/ 10 (0 to 28)) Limited by system memory Anything at all. All classes derive from object. Therefore, everything is an object.
34 de 96
Repare tambm, que nem todos os tipos suportados pelo C# so compatveis com as regras do CLS. Quando se criam tipos especficos para funcionar de forma transparente em todas as linguagens, apenas se devem usar os tipos compatveis CLS. A maioria dos tipos de dados intrnsecos do C# correspondem a uma estrutura derivada de System.ValueType. O tipo System.ValueType existe para redefinir, com semntica baseada em valor, os mtodos virtuais definidos por System.Object. Na realidade, as assinaturas dos mtodos definidos por ValueType so iguais s definidas por Object. No entanto, tenha em considerao que quando so comparadas duas instncias, estamos a utilizar uma semntica baseada no valor:
35 de 96
MultiCastDelegate
Enum
36 de 96
Todos os tipos derivados de System.ValueType (e.g., Int32, Char, Boolean, etc.) tm alguns membros utilitrios semelhantes. Por exemplo, as propriedades MaxValue e MinValue permitem determinar o valor mximo e o valor mnimo que cada tipo pode armazenar. Assuma que criou uma varivel do tipo System.UInt16, e que a utiliza do seguinte modo:
// Note que an implicit data type (ushort) has the same methods available // as the corresponding wrapper (System.Uint16). class MyDataTypes { public static void Main() { // Working with UInt16 as a structure. System.UInt16 myUInt16 = 30000; Console.WriteLine("Max for an UInt16 is: {0}", UInt16.MaxValue); Console.WriteLine("Min for an UInt16 is: {0}", UInt16.MinValue); Console.WriteLine("I am a: {0}", myUInt16.GetType().ToString()); // Now in UInt16 shorthand (e.g., a ushort). ushort myOtherUInt16 = 12000; Console.WriteLine("\nYour value is: {0}", myOtherUInt16.ToString()); Console.WriteLine("I am a: {0}", myOtherUInt16.GetType().ToString()); } }
37 de 96
importante tambm realar que em C# a representao de texto usa os tipos string e char. Desaparecem finalmente os tipos char*, wchar_t*, LPSTR, LPCSTR, BSTR e OLECHAR. Todos estamos de acordo que a manipulao de texto em COM e Win32 horrvel. O C# oferece uma forma simplificada de manipulao de strings, na medida em que todas as linguagens .NET usam os tipos System.String e System.Char que representam o texto com caracteres Unicode.
38 de 96
Moving Between Value Types and Reference Types: Boxing and Unboxing (1)
O C# providencia um mecanismo para fazer a converso entre tipos referncia e tipos valor, designado por boxing. Assuma que crimos um tipo valor simples:
// Make a simple value data point. short s = 25;
Se, durante o curso da sua aplicao, for necessrio converter este tipo valor para a referncia para objecto correspondente, deve fazer box do valor do seguinte modo:
// Box the value into an object reference. object objShort = s;
O boxing pode ser definido como o processo de converter explicitamente um tipo valor no tipo referncia correspondente. Quando se faz box de um valor, o que estamos essencialmente a fazer reservar um novo objecto no managed heap e a copiar o valor interno (25) para esse objecto. A operao inversa tambm permitida e designa-se unboxing. Unboxing o termo dado ao processo de converter uma referncia para um objecto para o tipo valor correspondente. A operao unboxing comea por verificar se o tipo de dados receptor equivalente ao tipo boxed e, em caso afirmativo, copia o valor. Por exemplo, a seguinte operao de unboxing correcta, dado que o tipo subjacente a objShort short:
// Now, unbox the reference back into a corresponding short. short anotherShort = (short)objShort;
39 de 96
Moving Between Value Types and Reference Types: Boxing and Unboxing (2)
Contudo, a seguinte operao de unboxing gera uma excepo InvalidCastException:
// Bad unboxing! public static int Main() { ... try { // The type contained in the box is NOT a string, but a short! string str = (string)objShort; } catch (InvalidCastException e) { Console.WriteLine("OOPS!\n{0}", e.ToString()); } ... }
Poder perguntar em que circunstncias ser necessrio fazer box (ou unbox) de um tipo? A resposta quase nunca. Na realidade, na maior parte das circunstncias o compilador de C# faz box e unbox quando necessrio. Por exemplo, se passar um tipo valor para um mtodo que receba como parmetro um objecto, ocorre uma operao de boxing:
// Assume the following method: public void Foo(object o) int x = 99; Foo(x); // Automatic boxing.
Por vezes o boxing e unboxing poder ser usado explicitamente para melhorar o desempenho das aplicaes. Voltaremos ao boxing adiante neste captulo aquando da discusso formal das estruturas C#. C# and the .NET Platform, 2. C# Language Fundamentals
40 de 96
41 de 96
42 de 96
Existe uma excepo obrigatoriedade de afectar inicialmente uma varivel local. Se a varivel funcionar como parmetro de sada (discutido a seguir neste captulo) no necessria a afectao inicial. Os mtodos que definem parmetros [out] assumem que as variveis que chegam aos mtodos so afectadas pelo mtodo invocado.
43 de 96
Quando definir uma classe utilitria que apenas contenha membros constantes, dever definir um construtor privado por forma a que a mesma nunca possa ser instanciada.
44 de 96
Podemos obter o mesmo resultado marcando a classe "constant only" como classe abstracta. Analisaremos a utilizao desta keyword no prximo captulo. Um exemplo da sua utilizao o seguinte:
// Abstract definition also prevents the creation of a given type. abstract class MyConstants { // Some const data. public const int myIntConst = 5; public const string myStringConst = "Im a const"; }
Em qualquer dos casos, se for tentada a criao de uma instncia de MyConstants, gerado um erro de compilao. Estas tcnicas podem ser bastante teis na medida em que o C# no permite definir constantes ao nvel global. Como nota final, retenha que, ao contrrio do C++, o C# no permite a utilizao da palavra chave const na definio dos mtodos.
45 de 96
46 de 96
47 de 96
48 de 96
49 de 96
As instrues if tambm podem envolver expresses complexas com uma sintaxe idntica do C++ e do Java. Para construir essas expresses, o C# dispe dos operadores condicionais presentes na Tabela 2-6.
50 de 96
51 de 96
52 de 96
Operators
+ - ! ~ ++x x++ --x x-* / % + << >> < > <= >= is as == != & ^ |
Conditional AND
Conditional OR Commotional Assignment
&&
|| ?: = *= %= += -= <<= >>= &= ^= |=
53 de 96
54 de 96
55 de 96
Os mtodos que so declarados pblicos so acessveis directamente a partir de uma instncia do objecto. Os mtodos privados no podem ser acedidos a partir de uma instncia de um objecto, mas apenas chamados internamente para ajudar a instncia a fazer o seu trabalho (isto , funes auxiliares privadas). Para ilustrar, a classe Teenager mostrada a seguir define dois mtodos pblicos, Complain e BeAgreeable, cada um dos quais devolve um string ao utilizador do objecto.
C# and the .NET Platform, 2. C# Language Fundamentals
56 de 96
57 de 96
58 de 96
Para invocar um mtodo esttico basta preceder o nome do membro do nome da classe:
// Call the static Complain method of the Teenager class. public static void Main () { for (int i = 0; i < 10; i++) Console.WriteLine(Teenager.Complain()); }
Os mtodos no estticos (de instncia) so mtodos do nvel objecto. Se Complain no for marcado com esttico, ser necessrio criar uma instncia da classe Teenager para ser possvel a respectiva invocao:
// Must make an instance of Teenager class to call instance methods. Teenager joe = new Teenager(); joe.Complain();
59 de 96
podemos criar alguns objectos do tipo Foo e definir o campo intFoo com valores diferentes:
// Each Foo reference contains a copy of the intFoo field. Foo f1 = new Foo(); f1.intFoo = 100; Foo f2 = new Foo(); f2.intFoo = 993; Foo f3 = new Foo(); f3.intFoo = 6;
Os dados estticos, por outro lado, so partilhados por todas as instncias da classe. Em vez de cada objecto armazenar uma cpia de um dado campo, existe apenas uma cpia partilhada por todas as instncias da classe.
60 de 96
Nesta classe so definidos dois mtodos: o mtodo esttico GetNumber devolve o nmero corrente de objectos aeroplano que foram reservados pela aplicao; o mtodo de instncia GetNumberFromObject tambm devolve o valor do membro esttico. Contudo, uma vez que o segundo mtodo no definido como esttico, o utilizador do objecto ter que o invocar a partir de uma instncia de Airplane. Para ilustrar, observe a seguinte utilizao:
61 de 96
Como podemos observar executando este programa, todas as instncias da classe Airplane partilham a mesma varivel. Esta a razo de ser dos dados estticos: permitir a partilha do membro por todas as instncias da classe.
62 de 96
63 de 96
64 de 96
Invocando um mtodo com parmetros de sada tambm requer a utilizao da palavra chave out. Por exemplo:
// Assume the Add() method is defined in a class named Methods. public static void Main() { ... Methods m = new Methods(); int ans; // No need to assign before when a variable is used ala out. // Note the use of out keyword is calling syntax. m.Add(90, 90, out ans); Console.WriteLine("90 + 90 = {0}", ans); }
Os parmetros referncia so necessrios quando pretendemos que um mtodo opere (e altere os valores) de vrios parmetros (como por exemplo, uma rotina de ordenao). Recorde e distino entre parmetros de entrada e parmetros de sada: Os parmetros de sada no necessitam de ser iniciados antes de serem enviados ao callee. Razo: assume-se que o mtodo preenche os valores antes de retornar. C# and the .NET Platform, 2. C# Language Fundamentals
65 de 96
O ltimo modificador dos parmetros a palavra chave params, que sendo algo invulgar, bastante conveniente na medida em que permite passar um nmero varivel de parmetros sobre um nico parmetro. Assuma que escrevemos um mtodo do seguinte modo:
66 de 96
Este mtodo foi definido de forma a tomar dois parmetros fsicos: um do tipo string e outra como um array de inteiros parametrizado. O que este mtodo est a dizer de facto : enviem-me um string como primeiro parmetro e qualquer nmero de inteiros como segundo parmetro. Podemos invocar o mtodo de vrias formas:
// Use 'params' keyword. int[] intArray = new int[3]{10, 11, 12}; m.DisplayArrayOfInts("Here is an array of ints", intArray); m.DisplayArrayOfInts("Enjoy these 3 ints", 1, 2, 3); m.DisplayArrayOfInts("Take some more!", 55, 4, 983, 10432, 99, 33);
Analisando o cdigo anterior podemos constatar que os itens a cheio em cada invocao do mtodo correspondem ao segundo parmetro (o array de inteiros). Naturalmente que no obrigatrio usar apenas valores numricos quando utiliza a palavra chave params. Considere a classe Person com a seguinte definio:
67 de 96
Assuma tambm que a classe Methods define outro mtodo que utiliza a palavra chave params, mas desta vez especificando um array de objectos. O cdigo deste mtodo o seguinte::
// What did they send to me this time? public void DisplayArrayOfObjects(params object[] list) { for (int i = 0; i < list.Length; i++) if (list[i] is Person) // Is the current item a Person type? ((Person)list[i]).PrintInfo(); // If so, call some method. else Console.WriteLine(list[i]); Console.WriteLine(); }
68 de 96
O primeiro exemplo declara o tipo e a dimenso do array em duas linhas separadas. Nos dois ltimos exemplos, mostra-se que possvel declarar e construir o array numa nica linha (assim como qualquer outro objecto). Em qualquer dos casos, repare que necessrio usar a palavra chave new quando se constri o array com uma dimenso inicial fixa. Assim, a seguinte declarao ilegal:
// Need 'new' keyword when you define a fixed size array. int[4] ages = {30, 54, 8, 10}; // Error!
69 de 96
Na iniciao dos membros de um array possvel usar a notao das chavetas ({}) em alternativa atribuio de valores a cada membro a membro, pelo que os dois arrays seguintes so idnticos:
// Initialize each member at declaration OR... string[] firstNames = new string[5]{"Steve", "Gina", "Swallow", "Baldy", "Gunner"}; // ...assign values member by member. string[] firstNames = new string[5]; firstNames[0] = "Steve"; firstNames[1] = "Gina"; firstNames[2] = "Swallow"; firstNames[3] = "Baldy"; firstNames[4] = "Gunner";
Um ltima diferena entre os arrays C++ e os arrays C# que todos os seus elementos so afectados automaticamente com o valor por omisso. Por exemplo, num array de tipos numricos, todos os elementos so iniciados com 0, nos arrays de objectos, os elementos so iniciados com null, e assim sucessivamente. C# and the .NET Platform, 2. C# Language Fundamentals
70 de 96
O segundo tipo de arrays multidimensionais so designados por jagged array. Como o prprio nome indica, os jagged arrays contm arrays internos, em que cada um poder ter um limite superior prprio. Por exemplo:
71 de 96
Aps perceber como se constrem e preenchem os arrays C#, vamos analisar a classe base de todos os arrays, System.Array. A diferena mais importante entre os arrays C++ e os arrays C# o facto de que estes ltimos derivarem automaticamente da classe System.Array. Esta classe define um conjunto de mtodos que tornam a manipulao dos arrays mais agradvel. A Tabela 210 enumera alguns dos membros mais interessantes. Vamos exemplificar a utilizao de alguns destes membros. O seguinte cdigo utiliza os mtodos estticos Reverse, Clear e a propriedade Length para mostrar na consola informao acerca do array firstName: C# and the .NET Platform, 2. C# Language Fundamentals
72 de 96
73 de 96
A possibilidade de tratar os arrays como objectos foi um luxo permitido aos programadores Java (entre outros). O C# e a plataforma .NET permite agora aos projectistas tradicionais de Windows dispem dos mesmos benefcios (podendo esquecer de vez o SAFEARRAY COM).
C# and the .NET Platform, 2. C# Language Fundamentals
74 de 96
75 de 96
Quando se executar este programa,verificar que os objectos string (s e strObj) no contm os mesmos valores e, por isso, o teste pela igualdade falha. Quando examinar o contedo de newString ver This is another TESTThis is a TEST. Finalmente, demonstra-se o acesso aos caracteres do string usando o operador ndice ([]).
Escape Characters and Verbatim Strings Assim como em C++ e em Java, os strings C# podem conter caracteres escape.
// Escape characters (\t, \\, \n, et al.) string anotherString; anotherString = "Every programming book needs \"Hello World\""; Console.WriteLine("\t" + anotherString); anotherString = "c:\\CSharpProjects\\Strings\\string.cs"; Console.WriteLine("\t" + anotherString);
Para quem estiver um pouco esquecido dos caracteres escape, a Tabela 2-12 ajuda a refrescar a memria.
76 de 96
77 de 96
78 de 96
Ao verificar o output desta ltima instruo pode constatar com os caracteres de escape no so processados nos strings definidos com o prefixo @. Using System.Text.StringBuilder Existe um facto que devemos ter sempre presente quando usamos strings C#: aps estabelecido o valor de um string este no pode ser modificado (o que tambm acontece na linguagem Java). Ao analisar os mtodos da classe System.String, constatamos que os mtodos que parecem modificar internamente o string, na realidade devolvem uma cpia do string original com as alteraes. Por exemplo, quando se enviar a mensagem ToUpper para o objecto string, este no modificar o contedo do seu buffer; constri uma cpia do string em letras maisculas:
// Make changes to this string? Not really... System.String strFixed = "This is how I began life"; Console.WriteLine(strFixed); string upperVersion = strFixed.ToUpper(); // Returns an uppercase copy of Console.WriteLine(strFixed); Console.WriteLine(upperVersion);
79 de 96
Alm de permitir acrescentar informao ao buffer interno, a classe StringBuilder permite substituir e remover caracteres. Aps ter sido estabelecido o contedo pretendido com um objecto StringBuilder, podemos invocar ToString para armazenar o respectivo contedo num objecto do tipo System.String.
80 de 96
Como deve adivinhar, a classe StringBuilder contm mtodos e propriedades que aqui no foram referidas. Deixamos ao cuidado do leitor o aprofundamento do conhecimento sobre esta classe.
81 de 96
C# Enumerations (1)
frequente a necessidade de criar um conjunto de nomes a que esto associados valores numricos. Por exemplo, se estivermos a criar uma sistema para pagamento de vencimentos, podemos necessitar das constantes VP, Manager, Grunt e Contractor em vez de valores numricos puros {0, 1, 2, 3}. Tal como o C++, C# suporta a noo de enumerao especfica. Por exemplo, a definio da enumerao EmpType:
// A custom enumeration. enum EmpType { Manager, // = 0 Grunt, // = 1 Contractor, // = 2 VP // = 3 }
A enumerao EmpType define quatro constantes com nome, correspondendo a valores numricos discretos. Em C#, o esquema de numerao atribui zero ao primeiro elemento, seguida por uma progresso de n+1. Podemos alterar esse comportamento do seguinte modo:
// Begin with 102. enum EmpType { Manager = 102, Grunt, // = 103 Contractor, // = 104 VP // = 105 }
82 de 96
C# Enumerations (2)
As enumeraes no necessitam de seguir uma numerao sequencial. Se, por alguma razo, fizer sentido estabelecer o tipo EmpType como se mostra a seguir, isso possvel.
// Elements of an enumeration need not be sequential! enum EmpType { Manager = 10, Grunt = 1, Contractor = 100, VP = 9 }
O tipo usado para armazena o enumerado por omisso um inteiro, embora seja possvel escolher outro tipo. Por exemplo, se pretender armazenar o enumerador num byte em vez de um int, dever escrever o seguinte:
// This time, EmpType maps to an underlying byte. enum EmpType : byte { Manager = 10, Grunt = 1, Contractor = 100, VP = 9 }
Os enumeradores podem ser definidos de forma semelhante para usar qualquer outro tipo base (byte, sbyte, short, int, etc.).
83 de 96
C# Enumerations (3)
Uma vez estabelecida a gama e o tipo para armazenamento da enumerao, esta poder ser usada como magic-number. Assuma que tem uma classe que define um mtodo pblico esttica que toma EmpType como parmetro:
using System; class EnumClass { public static void AskForBonus(EmpType e) { switch (e) { case EmpType.Contractor: Console.WriteLine("You already get enough cash..."); break; case EmpType.Grunt: Console.WriteLine("You have got to be kidding..."); break; case EmpType.Manager: Console.WriteLine("How about stock options instead..."); break; case EmpType.VP: Console.WriteLine("VERY GOOD, Sir!"); break; default: } } public static void Main() { // Make a contractor type EmpType fred; fred = EmpType.Contractor; AskForBonus(fred); } }
84 de 96
C# Enumerations (4)
System.Enum Base Class
Todas as enumeraes C# derivam implicitamente da classe System.Enum. Esta classe base define um conjunto de mtodos que permitem a interrogao e a transformao das enumeraes. O mtodo esttico GetUnderlyingType devolve o tipo de dados usado para armazenar a enumerao:
// Get underlying type (System.Byte for the current example) Console.WriteLine(Enum.GetUnderlyingType(typeof(EmpType)));
A possibilidade de obter o nome das constantes a partir dos valores numricos tem, por vezes, interesse. frequente ser necessrio transformar uma enumerao C++ nos strings subjacentes. Em C# isso torna-se trivial usando o mtodo esttico Enum.Format. No exemplo anterior, a varivel fred foi estabelecida como Contractor (que corresponde ao valor de 100). Para extrair o string correspondente, invoca-se Enum.Format especificando o tipo da enumerao a investigar e uma flag que especifica o formato. Neste contexto, G define um valor do tipo string. possvel obter tambm um valor hexadecimal (x) ou decimal (d).
// The following pumps the string "You are a Contractor" to the console. EmpType fred; fred = EmpType.Contractor; Console.WriteLine("You are a {0}", Enum. Format(typeof(EmpType), fred, "G");
85 de 96
C# Enumerations (5)
System.Enum define tambm o mtodo esttico GetValues que devolve uma instncia de System.Array. Cada item do array corresponde a um membro da enumerao:
// Get all statistics for the EmpTYpe enumeration. Array obj = Enum.GetValues(typeof(EmpType)); Console.WriteLine("This enum as {0} members.", obj.Length); // Now show the string name and associated value foreach(EmpType e in obj) { Console.Write("String name: {0}", Enum.Format(typeof(EmpType), e, "G")); Console.Write(" ({0})", Enum.Format(typeof(EmpType), e, "D")); Console.Write(" hex: {0}\n", Enum.Format(typeof(EmpType), e, "X")); }
Vamos agora explorar o mtodo IsDefined. Este mtodo permite determinar se um dado string define, ou no, um nome na enumerao especificada:
// Does EmpType have a SalePerson value? if (Enum.IsDefined(typeof(EmpType), "SalesPerson")) Console.WriteLine("Yep, we have sales people.") else Console.WriteLine("No, we have no profits...");
Por ltimo, vale a pena realar que as enumeraes C# suportam a utilizao de vrios operadores sobrecarregados que testam os valores associados:
// Which of these two EmpType variables has the greatest numerical value? EmpType Joe = EmpType.VP; EmpType Fran = EmpType.Grunt; if (Joe < Fran) Console.WriteLine("Joe's value is less that Fran's"); else Console.WriteLine("Fran's value is less than Joe's");
86 de 96
87 de 96
Dispondo da definio desta estrutura, podemos criar um novo empregado do seguinte modo:
class StructTester { public static void Main() { // Must use 'new' to call a custom constructor. EMPLOYEE mary = new EMPLOYEE(EmpType.VP, "Mary", 10); ... } }
88 de 96
89 de 96
(Un)Boxing Revisited
Como foi referido anteriormente neste captulo, o mecanismo de boxing e unboxing providencia uma forma eficiente de comutar entre tipos valor e tipos referncia. Em geral, as estruturas permitem obter os benefcios da orientao por objectos (i.e., encapsulamento), mantendo a eficincia associada aos dados alojados no stack. Para converter uma estrutura numa referncia para um objecto, basta fazer boxing do valor:
// Create and box a new employee. EMPLYEE stan = new EMPLOYEE(EmpType.Grunt, "Stan", 10); object standInBox = stan;
Uma vez que stanInBox um tipo referncia que mantm os valores internos do tipo original EMPLOYEE, podemos usar stanInBox quando necessrio um objecto e fazer unboxing sempre que seja necessrio:
// Because we have boxed our value data type into a structure, we can // unbox and manipulate the contents. public void UnboxThisEmployee(object o) { // Unbox into EMPLOYEE structure to get at the fields. EMPLOYEE temp = (EMPLOYEE)o; Console.WriteLine(temp.name + " is alive!"); }
Recorde que o compilador de C# faz automaticamente boxing quando necessrio. Por isso, seria permitido passar directamente o tipo EMPLOYEE para UnboxThisEmployee:
// Stan is boxed automatically. t.UnboxThisEmployee(stan);
90 de 96
Repare que o namespace MyShapes funciona como contentor de tipos. Em alternativa, possvel partir o mesmo namespace em mltiplos ficheiros C#. Para fazer isso, basta envolver as definies das classes no mesmo namespace.
91 de 96
Quando outra aplicao desejar usar estes objectos dentro do seu namespace, basta usar a directiva using:
// Make use of objects defined in another namespace. namespace MyApp { using System; using MyShapes; class ShapeTester { ... } }
92 de 96
Se a classe ShapesTester for alterada da forma como se como se mostra a seguir, so assinalados erros de compilao, porque ambos os namespaces definem nomes iguais.
// Ambiguities abound! namespace MyApp { using System; using MyShapes; using My3DShapes;
93 de 96
94 de 96
Nested Namespaces
A ltima questo com interesse acerca dos namespaces a possibilidade de aninhar um namespace dentro de outro namespace. A biblioteca de classes base do .NET usam esta tcnica para obter um nvel de organizao em profundidade. Por exemplo, se pretender criar um namespace de alto nvel que contenha o namespace My3DShapes pode proceder do seguinte modo:
// The Chapter2Types.My3DShapes namespace contains 3 classes. namespace Chapter2Types { namespace My3DShapes { using System; class Circle { ... } // 3D Circle class class Hexagon { ... } // 3D Hexagon class class Square { ... } // 3D Square class } }
95 de 96
Summary
Este captulo exps os mecanismos nucleares da linguagem de programao C#. Foi dado foco anlise das vrias construes sintcticas que ocorrem em qualquer aplicao. Primeiro, cada programa C# dever definir um mtodo esttico chamado Main, que funciona como entry point do programa. No mbito de Main so tipicamente criados os objectos que funcionam em conjunto para implementar a aplicao. Tambm vimos, que todos os tipos intrnsecos do C# correspondem a um tipo definido no namespace System. Cada tipo definido no .NET tem alguns mtodos e propriedades que permitem, de forma programtica, obter a gama de cada tipo. Foram tambm analisados programas que serviram para estudo das construes e tipos comuns, como arrays, strings, enumeraes etc.. Neste captulo tambm se discutiu o mecanismo de boxing e de unboxing. Este mecanismo permite converter tipos valor em tipos referncia e vice-versa. Finalmente, este captulo termina com a explicao de como se definem namespaces especficos, e discute as razes porque os mesmos devem ser definidos.
96 de 96