Resposta ao Desafio da Semana #3 [Performance – Comparando Expressões em VB.NET]



 


Por: Roberto Alexis Farah


 


Oi pessoal!


 


Eis a resposta do Desafio da Semana #3 http://blogs.technet.com/latam/archive/2006/05/05/427415.aspx .


O desafio ilustrou uma situação que pode ocorrer quando projetos são migrados de VB.NET para C# ou vice-versa.


 


 


PROBLEMA


 


O C# faz o que chamamos de curto-circuito ao resolver expressões lógicas. Ou seja a expressão pode não ser inteiramente analizada se parte da expressão for suficiente para se concluir o resultado.


Essa é uma herança do C/C++ e funciona assim:


 


AND                                    OR                                 XOR


1  0   = 0                           1   0   = 1                        1   0    = 1


0  1   = 0                           0   1   = 1                        0   1    = 1


1  1   = 1                           1   1   = 1                        1   1    = 0


0  0   = 0                           0   0   = 0                        0   0    = 0


 


Portanto em C# (ou C/C++) em situações como no exemplo abaixo resolver a primeira expressão é suficiente para se chegar a correta conclusão lógica. Se a primeira expressão (10 == numA) for falsa (False, 0) não é necessário se analisar a segunda expressão pois, independente do resultado da segunda expressão, a expressão toda será falsa, de acordo com a tabela acima.


 


If((10 == numA) && (20 == numB))


{


        // Executa…


}


 


O mesmo ocorre no exemplo abaixo. Se a primeira expressão for verdadeira a segunda não é analizada pois não vai influenciar no resultado final:


 


If((10 == numA) || (20 == numB))


{


        // Executa…


}


 


Pois bem, VB.NET por default mantém compatibilidade com VB 6.0 portanto, não faz esse tipo de análise otimizada de expressões booleanas. Entretanto, há operadores especiais do VB.NET usados justamente para isso.


 


Há um artigo que explica isso:


 


Description of “short-circuit” evaluation in Visual Basic


http://support.microsoft.com/kb/817250/en-us#EKACAAA


 


Portanto, VB.NET age como Visual Basic 6 quando usando os operadores herdados do Visual Basic 6, logo, a expressão é sempre completamente analizada.


E é esse comportamento que faz com que o resultado das expressões seja diferente no desafio proposto.


Entretanto, o VB.NET tem operadores especiais que fazem a análise da expressão do mesmo modo que o C#, conforme explicado no artigo acima.


 


Usando a ferramenta ILDASM que vem com o Visual Studio é possível ver o código Intermediate Language que é onde deveriam haver diferenças.


 


Código VB.NET original:


 


.method /*06000012*/ public static void  Main() cil managed


// SIG: 00 00 01


{


  .entrypoint


  .custom /*0C000045:0A00001F*/ instance void [mscorlib/*23000001*/]System.STAThreadAttribute/*0100001A*/::.ctor() /* 0A00001F */ = ( 01 00 00 00 )


  // Method begins at RVA 0x22d4


  // Code size       138 (0x8a)


  .maxstack  3


  .locals /*1100000C*/ init ([0] bool VB$CG$t_bool$S0)


  .language ‘{3A12D0B8-C26C-11D0-B442-00A0244A1DD2}’, ‘{994B45C4-E6E9-11D2-903F-00C04FA302A1}’, ‘{5A869D0B-6611-11D3-BD2A-0000F80849BD}’


// Source File ‘C:\Development\My Tools\BLOG Articles\Article #7\CSharp\VB\Module1.vb’


  .line 5,5 : 5,15 ‘C:\\Development\\My Tools\\BLOG Articles\\Article #7\\CSharp\\VB\\Module1.vb’


//000005:     Sub Main()


  IL_0000:  /* 00   |                  */ nop


  .line 6,6 : 3,49 ”


//000006:         If (DoSubtraction() > 0) Or (DoSum() > 0) Then


  IL_0001:  /* 28   | (06)000013       */ call       int32 VB.Module1/*02000007*/::DoSubtraction() /* 06000013 */


  IL_0006:  /* 16   |                  */ ldc.i4.0


  IL_0007:  /* FE02 |                  */ cgt


  IL_0009:  /* 28   | (06)000014       */ call       int32 VB.Module1/*02000007*/::DoSum() /* 06000014 */


  IL_000e:  /* 16   |                  */ ldc.i4.0


  IL_000f:  /* FE02 |                  */ cgt


  IL_0011:  /* 60   |                  */ or


  IL_0012:  /* 0A   |                  */ stloc.0


  IL_0013:  /* 06   |                  */ ldloc.0


  IL_0014:  /* 2C   | 1B               */ brfalse.s  IL_0031


  .line 7,7 : 7,82 ”


//000007:                  Console.WriteLine(“Valor da soma das variaveis = {0:d}”, sum + subtraction)


  IL_0016:  /* 72   | (70)000001       */ ldstr      “Valor da soma das variaveis = {0:d}” /* 70000001 */


  IL_001b:  /* 7E   | (04)000006       */ ldsfld     int32 VB.Module1/*02000007*/::sum /* 04000006 */


  IL_0020:  /* 7E   | (04)000007       */ ldsfld     int32 VB.Module1/*02000007*/::subtraction /* 04000007 */


  IL_0025:  /* D6   |                  */ add.ovf


  IL_0026:  /* 8C   | (01)000018       */ box        [mscorlib/*23000001*/]System.Int32/*01000018*/


  IL_002b:  /* 28   | (0A)00001E       */ call       void [mscorlib/*23000001*/]System.Console/*01000019*/::WriteLine(string,


                                                                                                                      object) /* 0A00001E */


  IL_0030:  /* 00   |                  */ nop


  .line 8,8 : 3,9 ”


//000008:         End If


  IL_0031:  /* 00   |                  */ nop


  .line 10,10 : 3,11 ”


//000009:


//000010:         sum = -5


  IL_0032:  /* 1F   | FB               */ ldc.i4.s   -5


  IL_0034:  /* 80   | (04)000006       */ stsfld     int32 VB.Module1/*02000007*/::sum /* 04000006 */


  .line 12,12 : 3,50 ”


//000011:


//000012:         If (DoSubtraction() < 0) And (DoSum() < 0) Then


  IL_0039:  /* 28   | (06)000013       */ call       int32 VB.Module1/*02000007*/::DoSubtraction() /* 06000013 */


  IL_003e:  /* 16   |                  */ ldc.i4.0


  IL_003f:  /* FE04 |                  */ clt


  IL_0041:  /* 28   | (06)000014       */ call       int32 VB.Module1/*02000007*/::DoSum() /* 06000014 */


  IL_0046:  /* 16   |                  */ ldc.i4.0


  IL_0047:  /* FE04 |                  */ clt


  IL_0049:  /* 5F   |                  */ and


  IL_004a:  /* 0A   |                  */ stloc.0


  IL_004b:  /* 06   |                  */ ldloc.0


  IL_004c:  /* 2C   | 1D               */ brfalse.s  IL_006b


  .line 13,13 : 5,80 ”


//000013:           Console.WriteLine(“Valor da soma das variaveis = {0:d}”, sum + subtraction)


  IL_004e:  /* 72   | (70)000001       */ ldstr      “Valor da soma das variaveis = {0:d}” /* 70000001 */


  IL_0053:  /* 7E   | (04)000006       */ ldsfld     int32 VB.Module1/*02000007*/::sum /* 04000006 */


  IL_0058:  /* 7E   | (04)000007       */ ldsfld     int32 VB.Module1/*02000007*/::subtraction /* 04000007 */


  IL_005d:  /* D6   |                  */ add.ovf


  IL_005e:  /* 8C   | (01)000018       */ box        [mscorlib/*23000001*/]System.Int32/*01000018*/


  IL_0063:  /* 28   | (0A)00001E       */ call       void [mscorlib/*23000001*/]System.Console/*01000019*/::WriteLine(string,


                                                                                                                      object) /* 0A00001E */


  IL_0068:  /* 00   |                  */ nop


  IL_0069:  /* 2B   | 1C               */ br.s       IL_0087


  .line 14,14 : 3,7 ”


//000014:         Else


  IL_006b:  /* 00   |                  */ nop


  .line 15,15 : 4,84 ”


//000015:          Console.WriteLine(“Valor da subtracao das variaveis = {0:d}”, sum – subtraction)


  IL_006c:  /* 72   | (70)000049       */ ldstr      “Valor da subtracao das variaveis = {0:d}” /* 70000049 */


  IL_0071:  /* 7E   | (04)000006       */ ldsfld     int32 VB.Module1/*02000007*/::sum /* 04000006 */


  IL_0076:  /* 7E   | (04)000007       */ ldsfld     int32 VB.Module1/*02000007*/::subtraction /* 04000007 */


  IL_007b:  /* DA   |                  */ sub.ovf


  IL_007c:  /* 8C   | (01)000018       */ box        [mscorlib/*23000001*/]System.Int32/*01000018*/


  IL_0081:  /* 28   | (0A)00001E       */ call       void [mscorlib/*23000001*/]System.Console/*01000019*/::WriteLine(string,


                                                                                                                      object) /* 0A00001E */


  IL_0086:  /* 00   |                  */ nop


  .line 16,16 : 3,9 ”


//000016:         End If


  IL_0087:  /* 00   |                  */ nop


  .line 18,18 : 5,12 ”


//000017:


//000018:     End Sub


  IL_0088:  /* 00   |                  */ nop


  IL_0089:  /* 2A   |                  */ ret


} // end of method Module1::Main


 


 


Código C#:


 


.method /*06000001*/ private hidebysig static


        void  Main(string[] args) cil managed


// SIG: 00 01 01 1D 0E


{


  .entrypoint


  // Method begins at RVA 0x2050


  // Code size       119 (0x77)


  .maxstack  3


  .language ‘{3F5162F8-07C6-11D3-9053-00C04FA302A1}’, ‘{994B45C4-E6E9-11D2-903F-00C04FA302A1}’, ‘{5A869D0B-6611-11D3-BD2A-0000F80849BD}’


// Source File ‘C:\Development\My Tools\BLOG Articles\Article #7\CSharp\CSharp\Program.cs’


  .line 15,15 : 12,54 ‘C:\\Development\\My Tools\\BLOG Articles\\Article #7\\CSharp\\CSharp\\Program.cs’


//000015:            if((DoSubtraction() > 0) || (DoSum() > 0))


  IL_0000:  /* 28   | (06)000002       */ call       int32 CSharp.Program/*02000002*/::DoSubtraction() /* 06000002 */


  IL_0005:  /* 16   |                  */ ldc.i4.0


  IL_0006:  /* 30   | 08               */ bgt.s      IL_0010


  IL_0008:  /* 28   | (06)000003       */ call       int32 CSharp.Program/*02000002*/::DoSum() /* 06000003 */


  IL_000d:  /* 16   |                  */ ldc.i4.0


  IL_000e:  /* 31   | 1A               */ ble.s      IL_002a


  .line 17,17 : 7,85 ”


//000016:            {


//000017:                  Console.WriteLine(“Valor da soma das variaveis = {0:d}\n”, sum + subtraction);              


  IL_0010:  /* 72   | (70)000001       */ ldstr      “Valor da soma das variaveis = {0:d}\n” /* 70000001 */


  IL_0015:  /* 7E   | (04)000001       */ ldsfld     int32 CSharp.Program/*02000002*/::sum /* 04000001 */


  IL_001a:  /* 7E   | (04)000002       */ ldsfld     int32 CSharp.Program/*02000002*/::subtraction /* 04000002 */


  IL_001f:  /* 58   |                  */ add


  IL_0020:  /* 8C   | (01)000012       */ box        [mscorlib/*23000001*/]System.Int32/*01000012*/


  IL_0025:  /* 28   | (0A)000010       */ call       void [mscorlib/*23000001*/]System.Console/*01000013*/::WriteLine(string,


                                                                                                                      object) /* 0A000010 */


  .line 20,20 : 10,19 ”


//000018:            }


//000019:          


//000020:          sum = -5;


  IL_002a:  /* 1F   | FB               */ ldc.i4.s   -5


  IL_002c:  /* 80   | (04)000001       */ stsfld     int32 CSharp.Program/*02000002*/::sum /* 04000001 */


  .line 22,22 : 11,53 ”


//000021:            


//000022:           if((DoSubtraction() < 0) && (DoSum() < 0))


  IL_0031:  /* 28   | (06)000002       */ call       int32 CSharp.Program/*02000002*/::DoSubtraction() /* 06000002 */


  IL_0036:  /* 16   |                  */ ldc.i4.0


  IL_0037:  /* 2F   | 23               */ bge.s      IL_005c


  IL_0039:  /* 28   | (06)000003       */ call       int32 CSharp.Program/*02000002*/::DoSum() /* 06000003 */


  IL_003e:  /* 16   |                  */ ldc.i4.0


  IL_003f:  /* 2F   | 1B               */ bge.s      IL_005c


  .line 24,24 : 6,84 ”


//000023:           {


//000024:                 Console.WriteLine(“Valor da soma das variaveis = {0:d}\n”, sum + subtraction);             


  IL_0041:  /* 72   | (70)000001       */ ldstr      “Valor da soma das variaveis = {0:d}\n” /* 70000001 */


  IL_0046:  /* 7E   | (04)000001       */ ldsfld     int32 CSharp.Program/*02000002*/::sum /* 04000001 */


  IL_004b:  /* 7E   | (04)000002       */ ldsfld     int32 CSharp.Program/*02000002*/::subtraction /* 04000002 */


  IL_0050:  /* 58   |                  */ add


  IL_0051:  /* 8C   | (01)000012       */ box        [mscorlib/*23000001*/]System.Int32/*01000012*/


  IL_0056:  /* 28   | (0A)000010       */ call       void [mscorlib/*23000001*/]System.Console/*01000013*/::WriteLine(string,


                                                                                                                      object) /* 0A000010 */


  IL_005b:  /* 2A   |                  */ ret


  .line 28,28 : 5,88 ”


//000025:           } 


//000026:           else


//000027:          {


//000028:                Console.WriteLine(“Valor da subtracao das variaveis = {0:d}\n”, sum – subtraction);                      


  IL_005c:  /* 72   | (70)00004B       */ ldstr      “Valor da subtracao das variaveis = {0:d}\n” /* 7000004B */


  IL_0061:  /* 7E   | (04)000001       */ ldsfld     int32 CSharp.Program/*02000002*/::sum /* 04000001 */


  IL_0066:  /* 7E   | (04)000002       */ ldsfld     int32 CSharp.Program/*02000002*/::subtraction /* 04000002 */


  IL_006b:  /* 59   |                  */ sub


  IL_006c:  /* 8C   | (01)000012       */ box        [mscorlib/*23000001*/]System.Int32/*01000012*/


  IL_0071:  /* 28   | (0A)000010       */ call       void [mscorlib/*23000001*/]System.Console/*01000013*/::WriteLine(string,


                                                                                                                      object) /* 0A000010 */


  .line 31,31 : 9,10 ”


//000029:          } 


//000030:             


//000031:         }


  IL_0076:  /* 2A   |                  */ ret


} // end of method Program::Main


 


 


 


 


 


SOLUÇÃO


 


Module Module1


            Dim sum As Integer = 50


            Dim subtraction As Integer = 100


 


            Sub Main()


                        If (DoSubtraction() > 0) OrElse (DoSum() > 0) Then


                                       Console.WriteLine(“Valor da soma das variaveis = {0:d}”, sum + subtraction)


                        End If


 


                        sum = -5


 


                        If (DoSubtraction() < 0) AndAlso (DoSum() < 0) Then


                          Console.WriteLine(“Valor da soma das variaveis = {0:d}”, sum + subtraction)


                        Else


                         Console.WriteLine(“Valor da subtracao das variaveis = {0:d}”, sum – subtraction)


                        End If


 


            End Sub


            Function DoSubtraction() As Integer


                        subtraction -= sum


                        DoSubtraction = subtraction


                        Exit Function


            End Function


 


            Function DoSum() As Integer


                        sum += subtraction


                        DoSum = sum


                        Exit Function


            End Function


End Module


 


Intermediate Language do código VB.NET usando os operadores que resolvem a expressão como em C#:


 


.method /*06000012*/ public static void  Main() cil managed


// SIG: 00 00 01


{


  .entrypoint


  .custom /*0C000045:0A00001F*/ instance void [mscorlib/*23000001*/]System.STAThreadAttribute/*0100001A*/::.ctor() /* 0A00001F */ = ( 01 00 00 00 )


  // Method begins at RVA 0x22d4


  // Code size       144 (0x90)


  .maxstack  3


  .locals /*1100000C*/ init ([0] bool VB$CG$t_bool$S0)


  .language ‘{3A12D0B8-C26C-11D0-B442-00A0244A1DD2}’, ‘{994B45C4-E6E9-11D2-903F-00C04FA302A1}’, ‘{5A869D0B-6611-11D3-BD2A-0000F80849BD}’


// Source File ‘C:\Development\My Tools\BLOG Articles\Article #7\CSharp\Solution\Module1.vb’


  .line 5,5 : 2,12 ‘C:\\Development\\My Tools\\BLOG Articles\\Article #7\\CSharp\\Solution\\Module1.vb’


//000005:   Sub Main()


  IL_0000:  /* 00   |                  */ nop


  .line 6,6 : 3,53 ”


//000006:         If (DoSubtraction() > 0) OrElse (DoSum() > 0) Then


  IL_0001:  /* 28   | (06)000013       */ call       int32 Solution.Module1/*02000007*/::DoSubtraction() /* 06000013 */


  IL_0006:  /* 16   |                  */ ldc.i4.0


  IL_0007:  /* 30   | 0B               */ bgt.s      IL_0014


  IL_0009:  /* 28   | (06)000014       */ call       int32 Solution.Module1/*02000007*/::DoSum() /* 06000014 */


  IL_000e:  /* 16   |                  */ ldc.i4.0


  IL_000f:  /* 30   | 03               */ bgt.s      IL_0014


  IL_0011:  /* 16   |                  */ ldc.i4.0


  IL_0012:  /* 2B   | 01               */ br.s       IL_0015


  IL_0014:  /* 17   |                  */ ldc.i4.1


  IL_0015:  /* 0A   |                  */ stloc.0


  IL_0016:  /* 06   |                  */ ldloc.0


  IL_0017:  /* 2C   | 1B               */ brfalse.s  IL_0034


  .line 7,7 : 7,82 ”


//000007:                  Console.WriteLine(“Valor da soma das variaveis = {0:d}”, sum + subtraction)


  IL_0019:  /* 72   | (70)000001       */ ldstr      “Valor da soma das variaveis = {0:d}” /* 70000001 */


  IL_001e:  /* 7E   | (04)000006       */ ldsfld     int32 Solution.Module1/*02000007*/::sum /* 04000006 */


  IL_0023:  /* 7E   | (04)000007       */ ldsfld     int32 Solution.Module1/*02000007*/::subtraction /* 04000007 */


  IL_0028:  /* D6   |                  */ add.ovf


  IL_0029:  /* 8C   | (01)000018       */ box        [mscorlib/*23000001*/]System.Int32/*01000018*/


  IL_002e:  /* 28   | (0A)00001E       */ call       void [mscorlib/*23000001*/]System.Console/*01000019*/::WriteLine(string,


                                                                                                                      object) /* 0A00001E */


  IL_0033:  /* 00   |                  */ nop


  .line 8,8 : 3,9 ”


//000008:         End If


  IL_0034:  /* 00   |                  */ nop


  .line 10,10 : 3,11 ”


//000009:


//000010:         sum = -5


  IL_0035:  /* 1F   | FB               */ ldc.i4.s   -5


  IL_0037:  /* 80   | (04)000006       */ stsfld     int32 Solution.Module1/*02000007*/::sum /* 04000006 */


  .line 12,12 : 3,54 ”


//000011:


//000012:         If (DoSubtraction() < 0) AndAlso (DoSum() < 0) Then


  IL_003c:  /* 28   | (06)000013       */ call       int32 Solution.Module1/*02000007*/::DoSubtraction() /* 06000013 */


  IL_0041:  /* 16   |                  */ ldc.i4.0


  IL_0042:  /* 2F   | 08               */ bge.s      IL_004c


  IL_0044:  /* 28   | (06)000014       */ call       int32 Solution.Module1/*02000007*/::DoSum() /* 06000014 */


  IL_0049:  /* 16   |                  */ ldc.i4.0


  IL_004a:  /* 32   | 03               */ blt.s      IL_004f


  IL_004c:  /* 16   |                  */ ldc.i4.0


  IL_004d:  /* 2B   | 01               */ br.s       IL_0050


  IL_004f:  /* 17   |                  */ ldc.i4.1


  IL_0050:  /* 0A   |                  */ stloc.0


  IL_0051:  /* 06   |                  */ ldloc.0


  IL_0052:  /* 2C   | 1D               */ brfalse.s  IL_0071


  .line 13,13 : 5,80 ”


//000013:           Console.WriteLine(“Valor da soma das variaveis = {0:d}”, sum + subtraction)


  IL_0054:  /* 72   | (70)000001       */ ldstr      “Valor da soma das variaveis = {0:d}” /* 70000001 */


  IL_0059:  /* 7E   | (04)000006       */ ldsfld     int32 Solution.Module1/*02000007*/::sum /* 04000006 */


  IL_005e:  /* 7E   | (04)000007       */ ldsfld     int32 Solution.Module1/*02000007*/::subtraction /* 04000007 */


  IL_0063:  /* D6   |                  */ add.ovf


  IL_0064:  /* 8C   | (01)000018       */ box        [mscorlib/*23000001*/]System.Int32/*01000018*/


  IL_0069:  /* 28   | (0A)00001E       */ call       void [mscorlib/*23000001*/]System.Console/*01000019*/::WriteLine(string,


                                                                                                                      object) /* 0A00001E */


  IL_006e:  /* 00   |                  */ nop


  IL_006f:  /* 2B   | 1C               */ br.s       IL_008d


  .line 14,14 : 3,7 ”


//000014:         Else


  IL_0071:  /* 00   |                  */ nop


  .line 15,15 : 4,84 ”


//000015:          Console.WriteLine(“Valor da subtracao das variaveis = {0:d}”, sum – subtraction)


  IL_0072:  /* 72   | (70)000049       */ ldstr      “Valor da subtracao das variaveis = {0:d}” /* 70000049 */


  IL_0077:  /* 7E   | (04)000006       */ ldsfld     int32 Solution.Module1/*02000007*/::sum /* 04000006 */


  IL_007c:  /* 7E   | (04)000007       */ ldsfld     int32 Solution.Module1/*02000007*/::subtraction /* 04000007 */


  IL_0081:  /* DA   |                  */ sub.ovf


  IL_0082:  /* 8C   | (01)000018       */ box        [mscorlib/*23000001*/]System.Int32/*01000018*/


  IL_0087:  /* 28   | (0A)00001E       */ call       void [mscorlib/*23000001*/]System.Console/*01000019*/::WriteLine(string,


                                                                                                                      object) /* 0A00001E */


  IL_008c:  /* 00   |                  */ nop


  .line 16,16 : 3,9 ”


//000016:         End If


  IL_008d:  /* 00   |                  */ nop


  .line 18,18 : 2,9 ”


//000017:


//000018:   End Sub


  IL_008e:  /* 00   |                  */ nop


  IL_008f:  /* 2A   |                  */ ret


} // end of method Module1::Main


 


 


Comparando o código IL da solução em VB.NET ou do código C# contra o código original VB.NET notamos, em vermelho acima, os seguintes mnemônicos:


 


OR         VB.NET original         C#         Solução em VB.NET


 


              cgt                         bgt          bgt


              or


 


 


AND        VB.NET original         C#         Solução em VB.NET


 


              clt                          bge         bge


              and


 


A principal diferença é que o código C# e a solução em VB.NET usam instruções de branch, ou seja, instruções IL que mudam o fluxo de execução de acordo com uma condição. Os comandos de branch são identificados pela letra b inicial. No caso temos:


 


bgt = branch if greater than


bge = branch if greater or equal


 


Ainda olhando o IL notamos que há desvios baseados em condições que fazem com que a segunda comparação dos if() possa ou não ser executada.


E olhando o código VB.NET da solução original, tanto no fragmento usando OR quanto no fragmento usando AND notamos que não há desvios e que ambas condições de cada comparação são sempre executadas!


Note que o comando clt e cgt iniciam com c, são comandos de check, ou seja, eles não desviam o fluxo de execução como um branch apenas colocam a condição da checagem na pilha.


 


Se você quiser entender o código disassemblado em IL, eis uma boa referência:


http://msdn2.microsoft.com/en-us/library/system.reflection.emit(VS.80).aspx


 


 


 


 


Embora esse seja um problema simples com uma solução conhecida e documentada, ter conhecimento dessa particularidade dos operadores pode salvá-lo de bugs difíceis de serem detectados.


 


Além disso, AndAlso e OrElse geram um código mais otimizado, portanto, é preferível usar eles ao invés dos correspondentes And e Or em VB.NET.


 


 

Comments (0)