Como comentei neste post, pretendo falar um pouco mais sobre as melhorias introduzidas na versão 2.0 do C#.
Na realidade, muitas destas melhorias só são suportadas pois foram introduzidas também no .Net Framework.
Vou começar pelo assunto Generics, uma vez que venho do "mundo C++" e gosto muito do conceito de templates (que é semelhante)
Em primeiro lugar, Generics é uma tecnologia que nos permite tratar um tipo de forma genérica (ok, sem gozação). Um caso clássico é a implementação de coleções (por exemplo listas encadeadas, filas, pilhas, etc). Até a versão 2.0 do .Net Framework coleções eram implementadas de forma a tratar os dados como Object. Funciona, mas esta abordagem traz vários problemas:
- Uma vez armazenado na coleção, o objeto perde sua identidade, ou seja, tudo é tratado como object.
- Isto obriga o desenvolvedor a fazer casts
- Não é possível ao compilador verificar o correto uso da coleção quanto ao tipo de dado incluído na mesma.
- Performance (basicamente boxing/unboxing)
Vamos apresentar um exemplo:
1: using System.Collections;
2: using System;
3: 4: class TestCollection
5: {6: public static void Main()
7: {8: IList col = new ArrayList();
9: 10: col.Add("Adriano");
11: col.Add("Verona");
12: 13: String s = (String) col[0];14: foreach (string str in col)
15: { 16: System.Console.WriteLine(str); 17: } 18: 19: col.Add(10);20: foreach (string str in col)
21: { 22: System.Console.WriteLine(str); 23: } 24: } 25: }Os problemas começam na linha 13 (que ilustra o ponto 2). Porque o compilador não permite que eu atribua col[0] para a variável s sem um cast explícito? Bom, porque o indexador [] da interface IList foi declarado como retornando um object e não poderia ser diferente! O único tipo de dados genérico disponível era o object!
Outro problema pode ser observado na linha 19. Como o método Add() espera um object o compilador não tem como verificar que esta operação não é válida. Provavelmente o desenvolvedor que escreveu este código ficará surpreso ao ser gerada uma exceção na linha 20.
Agora observer o próximo exemplo:
1: using System.Collections.Generic;
2: using System;
3: 4: class TestGenericCollection
5: {6: public static void Main()
7: {8: IList<String> strCol = new List<String>();
9: 10: strCol.Add("Str1");
11: strCol.Add("Str1");
12: 13: String s = strCol[0]; // Agora sim!!
14: Console.WriteLine(s); 15: 16: // Tentar compilar com o código abaixo descomentado gera um erro de compilação
17: // strCol.Add(42);
18: } 19: }A primeira diferença que observamos é quanto ao namespace utilizado. Neste exemplo passamos a utilizar o namespace System.Collections.Generic ao invés do tradicional System.Collections.
Na linha 8 declaramos uma lista para armazenar strings (denotado pela sintaxe <String> que segue a interface IList e a classe List).
Na linha 13 observamos que podemos recuperar elementos da lista sem necessidade de casts. Você pode estar pensando: "ummm, o compilador deu uma de esperto e fez o cast implicito!! ou seja, sintaxe suggar"; você esta enganado... observe o código IL gerado no primeiro exemplo:
1: .method public hidebysig static void Main() cil managed
2: { 3: .entrypoint4: // Code size 175 (0xaf)
5: .maxstack 26: .locals init (class [mscorlib]System.Collections.IList V_0,
7: string V_1,
8: string V_2,
9: class [mscorlib]System.Collections.Generic.IList`1<string> V_3,
10: class [mscorlib]System.Collections.IEnumerator V_4,
11: bool V_5,
12: class [mscorlib]System.IDisposable V_6)
13: IL_0000: nop14: IL_0001: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor()
15: IL_0006: stloc.0 16: IL_0007: ldloc.017: IL_0008: ldstr "Adriano"
18: IL_000d: callvirt instance int32 [mscorlib]System.Collections.IList::Add(object)
19: IL_0012: pop 20: IL_0013: ldloc.021: IL_0014: ldstr "Verona"
22: IL_0019: callvirt instance int32 [mscorlib]System.Collections.IList::Add(object)
23: IL_001e: pop 24: IL_001f: ldloc.0 25: IL_0020: ldc.i4.026: IL_0021: callvirt instance object [mscorlib]System.Collections.IList::get_Item(int32)
27: IL_0026: castclass [mscorlib]System.String 28: IL_002b: stloc.1 29: IL_002c: nop 30: IL_002d: ldloc.0Observe que na linha 27 existe uma instrunção castclass (que realiza o cast)
Agora veja o IL gerado no segundo exemplo (usando generics) :
1: .method public hidebysig static void Main() cil managed
2: { 3: .entrypoint4: // Code size 47 (0x2f)
5: .maxstack 26: .locals init (class [mscorlib]System.Collections.Generic.IList`1<string> V_0,
7: string V_1)
8: IL_0000: nop9: IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
10: IL_0006: stloc.0 11: IL_0007: ldloc.012: IL_0008: ldstr "Str1"
13: IL_000d: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1<string>::Add(!0)
14: IL_0012: nop 15: IL_0013: ldloc.016: IL_0014: ldstr "Str1"
17: IL_0019: callvirt instance void class [mscorlib]System.Collections.Generic.ICollection`1<string>::Add(!0)
18: IL_001e: nop 19: IL_001f: ldloc.0 20: IL_0020: ldc.i4.021: IL_0021: callvirt instance !0 class [mscorlib]System.Collections.Generic.IList`1<string>::get_Item(int32)
22: IL_0026: stloc.1 23: IL_0027: ldloc.124: IL_0028: call void [mscorlib]System.Console::WriteLine(string)
25: IL_002d: nop 26: IL_002e: ret27: } // end of method TestGenericCollection::Main
Note (linhas 21, 22) que o compilador não usou a instrução castclass, ou seja, nenhum cast foi realizado!
No próximo post pretendo falar um pouco mais sobre generics e apresentar um exemplo.
Até mais.
[+/-] mostrar/esconder este post

0 Comments:
Postar um comentário
<< Home