Access Violation

sexta-feira, outubro 07, 2005

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:



  1. Uma vez armazenado na coleção, o objeto perde sua identidade, ou seja, tudo é tratado como object.

  2. Isto obriga o desenvolvedor a fazer casts

  3. Não é possível ao compilador verificar o correto uso da coleção quanto ao tipo de dado incluído na mesma.

  4. 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:    .entrypoint
   4:    // Code size       175 (0xaf)
   5:    .maxstack  2
   6:    .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:  nop
  14:    IL_0001:  newobj     instance void [mscorlib]System.Collections.ArrayList::.ctor()
  15:    IL_0006:  stloc.0
  16:    IL_0007:  ldloc.0
  17:    IL_0008:  ldstr      "Adriano"
  18:    IL_000d:  callvirt   instance int32 [mscorlib]System.Collections.IList::Add(object)
  19:    IL_0012:  pop
  20:    IL_0013:  ldloc.0
  21:    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.0
  26:    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.0

 Observe 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:    .entrypoint
   4:    // Code size       47 (0x2f)
   5:    .maxstack  2
   6:    .locals init (class [mscorlib]System.Collections.Generic.IList`1<string> V_0,
   7:             string V_1)
   8:    IL_0000:  nop
   9:    IL_0001:  newobj     instance void class [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
  10:    IL_0006:  stloc.0
  11:    IL_0007:  ldloc.0
  12:    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.0
  16:    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.0
  21:    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.1
  24:    IL_0028:  call       void [mscorlib]System.Console::WriteLine(string)
  25:    IL_002d:  nop
  26:    IL_002e:  ret
  27:  } // 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