Março 3, 2010

tech.days Portugal 2010

Posted in .NET Framework às 13:44 por Henrry Pires

Como já é habito a Microsoft vai organizar cá no nosso pais mais uma vez o evento tech.days

Como também não podia deixar de ser a comunidade NetPonto vai-se fazer representar. O Caio Proite, Paulo Correia e o Bruno Lopes (entre outros) vão ser alguns dos oradores. Inscreva-se e participe neste grande evento

Novembro 16, 2009

Concatenando Strings

Posted in .NET Framework às 14:17 por Henrry Pires

string1 & string2 & string3 & … string100 = Comprar hardware mais potente.

É uma prática muito comum concatenar várias string, por exemplo dentro dum ciclo, mas sendo uma prática comum não propriamente quer dizer que a melhor forma de atingir o objectivo, porque?

Vou fazer uma aplicação muito pequena para demonstrar o ‘porquê’ da questão.

Module Module1

    Sub Main()

        Dim resultado As String = String.Empty

        For i = 0 To 100
            resultado &= i.ToString()
        Next

    End Sub

End Module

Esta console application, muito simples, o único que vai fazer é, concatenar 100 vezes a variável ‘resultado’ com com o ToString da variável ‘i’.

Após compilado para release, se formos ver o IL gerado, recorrendo para tal ao ildasm.exe, (que vem instalado com a .NET Framework SDK) vamos obter o seguinte resultado:

.method public static void  Main() cil managed
{
  .entrypoint
  .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 )
  // Code size       32 (0x20)
  .maxstack  2
  .locals init ([0] string resultado,
           [1] int32 i)
  IL_0000:  ldsfld     string [mscorlib]System.String::Empty
  IL_0005:  stloc.0
  IL_0006:  ldc.i4.0
  IL_0007:  stloc.1
  IL_0008:  ldloc.0
  IL_0009:  ldloca.s   i
  IL_000b:  call       instance string [mscorlib]System.Int32::ToString()
  IL_0010:  call       string [mscorlib]System.String::Concat(string, string)
  IL_0015:  stloc.0
  IL_0016:  ldloc.1
  IL_0017:  ldc.i4.1
  IL_0018:  add.ovf
  IL_0019:  stloc.1
  IL_001a:  ldloc.1
  IL_001b:  ldc.i4.s   100
  IL_001d:  ble.s      IL_0008
  IL_001f:  ret
} // end of method Module1::Main

Vou somente abordar a linha que está sublinhada, visto não ser o propósito deste post analisar, o IL gerado na sua totalidade.

Na linha sublinhada está a ser invocado o método Shared ‘Concat’ da classe string. Vamos agora recorrer ao .Net Reflector para ver o código executado neste método

Public Shared Function Concat(ByVal str0 As String, _
                              ByVal str1 As String) As String
    If String.IsNullOrEmpty(str0) Then
        If String.IsNullOrEmpty(str1) Then
            Return String.Empty
        End If
        Return str1
    End If
    If String.IsNullOrEmpty(str1) Then
        Return str0
    End If
    Dim length As Integer = str0.Length
    Dim dest As String = String.FastAllocateString((length + str1.Length))
    String.FillStringChecked(dest, 0, str0)
    String.FillStringChecked(dest, length, str1)
    Return dest
End Function

Pelo código que o Reflector nos devolve, podemos ver que está ser retornada no fim da função a variável ‘dest’, que mais não é do que uma nova instância da classe string, contendo as duas strings que lhe foram passadas por parâmetro, ‘str0′ e ‘str1’

Portanto, por cada concatenação de duas strings, é gerada uma terceira string. Se por exemplo, incluirmos isto dentro de um ciclo, vamos ter uma enorme quantidade de instâncias da classe string.

Como então evitar este problema?
Vou refazer o exemplo acima, de modo optimizar a concatenação das strings.

Module Module1

    Sub Main()

        Dim resultado As new System.Text.StringBuilder()

        For i = 0 To 100
            resultado.Append(i.ToString())
        Next

    End Sub

End Module

A classe StringBuilder é excelente para esta finalidade, pois independentemente do número de vezes que o método Append(…) for invocado,  vai criar uma única string (recorrendo ao .Net Reflector poderá ser constatado), e para obtermos a o resultado final, basta-nos invocar o método ToString(), exemplo: resultado.ToString().

Conclusão

Sempre que tivermos mais do que quatro concatenações para fazer vale a pena usar a classe StringBuilder, pois vai-nos poupar instâncias “desnecessárias” da classe string.

Outubro 30, 2009

Comparando Strings

Posted in .NET Framework tagged às 22:50 por Henrry Pires

O que há de especial em comparar duas strings? Uma rápida vista de olhos na classe string pode-nos ajudar a perceber.

A classe string ao contrário da classe Int32 (por exemplo) é uma reference type. Mas em que é que isto afecta a comparação? Vejamos um exemplo de outra classe reference type. No código a seguir vou usar a classe DataTable, para demonstrar o problema existente na avaliação da igualdade dos reference type.

VB.Net

Dim dataTable1 = New DataTable()
Dim values(1) As Object

values(0) = "value1"
dataTable1.Columns.Add("Column1")
dataTable1.Rows.Add(values)

Dim dataTable2 As DataTable = dataTable1.Copy()

If dataTable1 = dataTable2 Then

C#

DataTable dataTable1 = new DataTable();

dataTable1.Columns.Add("Column1");
dataTable1.Rows.Add(new object[1] { "value1" });

DataTable dataTable2 = dataTable1.Copy();

if (dataTable1 == dataTable2)

Em VB.Net da erro de compilação.

“Operator ‘=’ is not defined for types ‘System.Data.DataTable’ and ‘System.Data.DataTable’.”

Em C# funciona, mas na realidade o que o compilador está a fazer é comparar as referências para o endereço de memória, ou seja estaríamos a fazer qualquer coisa como:

C#

if (object.ReferenceEquals(dataTable1, dataTable2))

Mas devido ao facto de a classe string fazer override do operador “=”,

a comparação entre duas variáveis do tipo string funciona, e funciona bem.

Ok, mas se a comparação funciona bem então, “O que há de especial em comparar duas strings?

Vamos então fazer algumas mudanças no código, desta vez só para os programadores de VB.Net

VB.Net

Option Compare Binary

Imports System.Windows.Forms

Module Module1

    Sub Main()

        Dim stringA, stringB As String

        stringA = “A"
        stringB = “a”

        If stringA = stringB Then
            MessageBox.Show(“As strings são iguais”, “Teste strings”, _
			MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
        Else
            MessageBox.Show(“As strings não são iguais”, “Teste strings”, _
			MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
        End If

    End Sub

End Module

Neste caso, a comparação vai dar sempre falso porque estamos a definir o “Option Compare” como sendo “Binary” (que é o predefinido no C#). Então se não quisermos fazer distinção entre maiúsculas e minúsculas, o que podemos fazer?

A opção mais comum é por ambas as strings em maiúsculas (ou minúsculas) usando os métodos “ToUpper” ou “ToLower” da classe string.

Funciona? A resposta é sim, mas em termos de desempenho não será a mais adequada. Aqui entra um método da string, o “Compare”. Então teríamos o código da seguinte forma:

VB.Net

If String.Compare(stringA, stringB, True) = 0 Then

C#

if(string.Compare(stringA, stringB, true)== 0)

O que significa o parâmetro “True” e porque comparar o resultado com zero? Como neste caso queremos que ambas as strings (“A”, “a”) sejam iguais, vamos definir que deverá ser feita uma comparação Case Insensitive. O retorno com valor zero indica-nos que ambas as strings são iguais. No entanto existe ainda uma maneira mais correcta de isto ser feito.

VB.Net

If String.Compare(stringA, stringB, True, _
			 CultureInfo.GetCultureInfo("PT-pt")) = 0 Then

C#

if (string.Compare(stringA, stringB, true,
			 CultureInfo.GetCultureInfo("PT-pt")) == 0)

Conclusão

De modo a evitarmos problemas, ao comparar duas strings, convêm sempre usar o método “Compare” assim serão evitados os tais erros, “que só dão as vezes”, os tais muito difíceis de apanhar.