Category: VB.NET

  • Om aktörer

    Om aktörer

    Det finns tre olika typer av aktörer i Akka .NET. Nedan använder jag en ReceiveActor som utmärker sig genom att den använder funktionen Receive för att ta emot ett meddelande. Exemplen som följer förutsätter att namnrymden Akka.Actor är inläst. Alla aktörer har en sökväg som är oberoende av vilken CLR-klass som beskriver dem. Givet att system är ett initierat aktörssystem med namnet MittSystem och att vi har två aktörer…

    public class EnRootActor : ReceiveActor
    {
    }
    
    public class EnAnnanActor : ReceiveActor
    {
    }

    …så kommer denna kod att fallera eftersom båda aktörerna, trots sina olika klasser, gör anspråk på samma sökväg:

    var a = system.ActorOf<EnRootActor>("MinRoot");
    Console.WriteLine(a.Path);
    var b = system.ActorOf<EnAnnanActor>("MinRoot");
    Console.WriteLine(b.Path);

    Jag använder Akka .NET lokalt, vilket syns på sökvägen som är akka://MittSystem/user/MinRoot vilket innebär protokollet akka, aktörssystemet MittSystem följt av sökvägen user/MinRoot. Alla användardefinierade aktörer ligger under user.

    Både a och b kan samexistera givet att vi ger dem unika namn.

    Aktörer behöver inte skapas på rootnivå. Om en aktör skapar en aktör organiseras den nya aktören under den som skapar aktören.

    public class EnRootActor : ReceiveActor
    {
       public EnRootActor()
       {
          this.Receive<Message>(m =>
          {
             var c = Context.ActorOf<EnAnnanActor>("Child");
             Console.WriteLine(c.Path);
          });
       }
    }

    I ovanstående fall har c sökvägen akka://MittSystem/user/MinRoot/MinChild, och eftersom vi har ett villkorslöst skapande så stöter vi på problem nästa gång vi skickar ett meddelande till EnRootActor eftersom MinChild inte kan skapas mer än en gång.

    Akka .NET erbjuder möjligheten att erhålla referenser till existerande aktörer genom funktionen ActorSelection som kan agera antingen på systemet eller i kontexten för någon existerande aktör.

    I detta exempel (en Console Application) är skapas en tredje aktör när aktör a hanterar meddelandet som skickas till den. Den tredje aktören får sedan ett meddelande skickat till sig från Main, vilket är en annan tråd.

    class Program
    {
       private static ActorSystem system;
    
       static void Main(string[] args)
       {
          var system = ActorSystem.Create("MittSystem");
          var a = system.ActorOf<EnRootActor>("MinRoot");
          Console.WriteLine(a.Path);
          var b = system.ActorOf<EnAnnanActor>("EnTillRoot");
          Console.WriteLine(b.Path);
          a.Tell(new Message("Helloooooo!"));
    
          //Godtycklig paus eftersom childaktören
          //skapas på en annan tråd.
          System.Threading.Thread.Sleep(1000);
    
          //Erhåll en referens till childaktören
          //och skicka ett meddelande.
          system.ActorSelection(
             "akka://MittSystem/user/MinRoot/Child").Tell(
             new Message("Snodd ref."));
    
          Console.ReadLine();
       }
    }
    
    public class EnRootActor : ReceiveActor
    {
       public EnRootActor()
       {
          this.Receive<Message>(m =>
          {
             Console.WriteLine(m.Text);
             //Skapa en ny.
             var c = Context.ActorOf<EnAnnanActor>("Child");
             Console.WriteLine(c.Path);
          });
       }
    }
       
    public class EnAnnanActor : ReceiveActor
    {
       public EnAnnanActor()
       {
          this.Receive<Message>(m => Console.WriteLine(m.Text));
       }
    }
    
    public class Message
    {
       public string Text { get; private set; }
       public Message(string text)
       {
          this.Text = text;
       }
    }

    För att inte programmet ska krascha nästa gång EnRootActor tar emot meddelandet, kan man kort och gott kolla om childaktören redan är skapad.

    public class EnRootActor : ReceiveActor
    {
       public EnRootActor()
       {
          this.Receive<Message>(m =>
          {
             Console.WriteLine(m.Text);
    
             //Skapa en ny om det behövs.
             if (Context.GetChildren().Count() <= 0)
             {
                var c = Context.ActorOf<EnAnnanActor>("Child");
                Console.WriteLine(c.Path);
             }
          });
       }
    }

    Skulle man till äventyrs vara intresserad av vilka aktörer som är childaktörer går det bra att läsa av deras sökväg eller kanske bara deras namn:

    foreach (var x in Context.GetChildren())
       Console.WriteLine(x.Path.Name);

    Så här ser samma exempel ut i Visual Basic (förutsatt att du lagt på Akka .NET med NuGet):

    Imports Akka.Actor
    
    Module Module1
    
       Private sys As ActorSystem
    
       Sub Main()
          sys = ActorSystem.Create("MittSystem")
          Dim a = sys.ActorOf(Of EnRootActor)("MinRoot")
          Console.WriteLine(a.Path)
          Dim B = sys.ActorOf(Of EnAnnanActor)("EnTillRoot")
          a.Tell(New Message("Helloooooo!"))
          System.Threading.Thread.Sleep(1000)
          sys.ActorSelection( _
             "akka://MittSystem/user/MinRoot/Child").Tell( _
             New Message("Snodd ref."))
          a.Tell(New Message("Tjo!"))
          Console.ReadLine()
       End Sub
    
    End Module
    
    Public Class EnRootActor
       Inherits ReceiveActor
    
       Public Sub New()
          Me.Receive(Of Message)(AddressOf Me.ReceiveMessage)
       End Sub
    
       Private Sub ReceiveMessage(m As Message)
          Console.WriteLine(m.Text)
          If Context.GetChildren().Count() <= 0 Then
             Dim c = Context.ActorOf(Of EnAnnanActor)("Child")
             Console.WriteLine(c.Path)
          End If
       End Sub
    
    End Class
    
    Public Class EnAnnanActor
       Inherits ReceiveActor
    
       Public Sub New()
          Me.Receive(Of Message)(AddressOf Me.ReceiveMessage)
       End Sub
    
       Private Sub ReceiveMessage(m As Message)
          Console.WriteLine(m.Text)
       End Sub
    
    End Class
    
    Public Class Message
    
       Public Property Text() As String
    
       Public Sub New(Text As String)
          Me.Text = Text
       End Sub
    
    End Class

    Förutom ReceiveActor som alltså anropar funktionen Receive för att ta emot meddelanden, så har vi även TypedActor och UntypedActor.

    En TypedActor anropar inte funktionen Receive för att ta emot meddelanden, utan har en överlagring av Handle för varje meddelandetyp som aktören kan hantera. En TypedActor ärver därför inte bara från basklassen TypedActor, utan även från det generiska interfacet IHandle<> för varje meddelandetyp som aktören ska hantera. Om man jobbar med klassen TypedActor betalar man med lite minskad flexibilitet, men vinner i ökad struktur. I detta exempel kan aktören ta emot både meddelande av typen A och B (där A och B är vanliga CLR-klasser).

    class Program
    {
       private static ActorSystem system;
    
       static void Main(string[] args)
       {
          var system = ActorSystem.Create("MittSystem");
          var actor = system.ActorOf<MyTypedActor>("MinActor");
          actor.Tell(new A());
          actor.Tell(new B());
          Console.ReadLine();
       }
    }
    
    public class MyTypedActor : TypedActor, IHandle<A>, IHandle<B>
    {
       public void Handle(A message)
       {
          Console.WriteLine("Tack för A!");
       }
    
       public void Handle(B message)
       {
          Console.WriteLine("Tack för B!");
       }
    }
    
    public class A
    {
    }
    
    public class B
    {
    }

    En UntypedActor fungerar enligt samma princip, fast utan typer. Den har en enda funktion som blir anropad vid inkommande meddelande, och den funktionen tar emot meddelandet i object-format. Jag personligen kan inte se någon speciell poäng i att välja att ärva från UntypedActor, för om man inte har behov av typlåsningen så är ReceiveActor ett bättre val. Notera att UntypedActor skriver över funktionen OnReceive.

    class Program
    {
       private static ActorSystem system;
    
       static void Main(string[] args)
       {
          var system = ActorSystem.Create("MittSystem");
          var actor = system.ActorOf("MinActor");
          actor.Tell(new A());
          actor.Tell(new B());
          Console.ReadLine();
       }
    }
    
    public class MyUntypedActor : UntypedActor
    {
       protected override void OnReceive(object message)
       {
          if (message.GetType() == typeof(A))
             Console.WriteLine("Tack för A!");
          else if (message.GetType() == typeof(B))
             Console.WriteLine("Tack för B!");
       }
    }
    
    public class A
    {
    }
    
    public class B
    {
    }
  • Fem nyheter i VB.NET 2015

    Fem nyheter i VB.NET 2015

    Nu när Visual Studio 2015 CTP finns att ladda hem, går det bra att testa vad som är nytt i senaste versionen av Visual Basic. Och i ärlighetens namn är det inte speciellt mycket som hänt i språket, men här är fem stycken guldkorn.

    Enterslag i strängar
    I tidigare versioner av VB.NET fanns inget sätt att ange att en sträng skulle innehålla radbryten. För att skapa en sträng med radbryten var man tvungen att t.ex. konkatenera in ControlChars.CrLf eller använda StringBuilder-objektets AppendLine. Men nu är det fullt tillåtet att skriva enterslagen direkt i källkoden. Detta exempel skapar en sträng innehållande tre textrader:

    Dim X = "Jag
    är en
    sträng med tre rader!"

    Stränginterpolering
    Och apropå strängar så stöder nu VB.NET stränginterpolering. Tidigare när man infogade variabla värden i en sträng använde man funktionen String.Format. I detta exempel är X och Y flyttal:

    Dim S =
       String.Format("Värdet av X är {0:n1} och y är {1:n1}",
       X, Y)

    Notera hur jag först anger mina platshållare i strängen, för att sedan låta övriga parametrar fylla dessa platshållare. Med prefixet $ kan jag stoppa in referenserna direkt in i strängen. Så istället för att först ange plats 0 och sedan fylla ut den med X, så kan jag direkt säga att jag vill ha in värdet av X i strängen. Och editorns IntelliSense hänger med!

    Dim S = $"Värdet av X är {X:n1} och y är {Y:n1}"

    Null propagation
    Tänk dig att du har en variabel som representerar en anställd (klassen Employee). Du vill läsa av dess property FirstName, så här:

    Dim N = E.FirstName

    Problemet är bara att variabeln E kan vara oinitierad, och i så fall uppstår NullReferenceException i programmet. Men du kanske vill ha ut förnamnet om det finns ett Employee-objekt i E, annars en tom sträng. Detta brukar VB-programmerare hantera så här:

    Dim N = If(E Is Nothing, "", E.FirstName)

    Med hjälp av null propagation kan man nu skriva så här:

    Dim N = E?.FirstName

    Om E är oinitierad (Nothing i VB), så är nu N en oinitierad strängvariabel.

    NameOf
    Igen, tänk dig att jag har en klass som heter Employee och att den har en property som heter FirstName. Om jag vill att en strängvariabel ska innehålla namnet på propertyn FirstName, kanske för att jag vill använda reflection mot ett objekt av typen Employee, skulle jag kunna göra så här:

    Dim S = "FirstName"

    Problemet med ovanstående kod är givetvis att om jag ger min property ett nytt namn så slutar koden att fungera, eftersom strängkonstanten “FirstName” inte längre delar namn med propertyn i fråga. Det har inte funnits något bra sätt att göra detta på i VB. Om jag t.ex. vet att FirstName är den första propertyn, kan jag förvisso skriva så här:

    Dim S = GetType(Employee).GetProperties()(0).Name

    Men detta är precis lika instabilt eftersom koden nu rasar om någon lägger till en ny property före FirstName. Men i VB.NET 2015 kan jag kort och gott skriva så här:

    Dim S = NameOf(E.FirstName)

    Nu kommer värdet “FirstName” lagras i S, och om vi refaktorerar koden, kommer ovanstående rad att följa efter.

    Kommentarer i LINQ
    Den sista språkliga förbättringen ger oss möjlighet att skapa kommentarer i en LINQ-fråga. Tänk att vi har en lista av Employee-objekt enligt följande:

    Dim X As New List(Of Employee)()
    X.Add(New Employee("Sven"))
    X.Add(New Employee("Gunnar"))

    Om jag vill ha en fråga som hämtar de objekt vars förnamn börjar på G, kan jag uttrycka mig så här:

    Dim Svar = From E In X
               Where E.FirstName.StartsWith("G")
               Select E

    Men om jag skulle få för mig att kommentera mitt i programsatsen, t.ex. efter X på rad 1, efter (“G”) på rad två eller efter E på rad 3, skulle programmet rasa.

    Dim Svar = From E In X                       'Men nu kan jag
               Where E.FirstName.StartsWith("G") 'kommentera
               Select E                          'vart jag vill!

    Avslutningsvis vill jag kommentera att kodeditorn fått sig ett rejält lyft, där det som sticker ut mest är en referensräknare på alla objekt man skapar – gissningsvis ett mycket efterfrågat tillägg.

  • Enkel trådning i .NET med Akka

    Enkel trådning i .NET med Akka

    Historiskt sett har det varit ganska komplicerat att bygga multitrådade applikationer i .NET. Rent tekniskt var det enkelt att sjösätta en tråd med hjälp av klassen System.Threading.Thread, men om mycket skulle göras började koden ganska snabbt likna spaghetti. För att minska ner koden kan man använda multitrådade delegater istället för objekt av typen Thread, men det löste inte problemet med spaghettikod. Nyckelorden asynk och await löste både komplexiteten med hopp och returvärde, men var ganska hårt knutet till modellen TAP.

    Om jag väljer att jobba med Akka .NET blir trådningen ännu mer hanterad, fast kring en mycket friare modell än TAP, nämligen Actor Model. (I alla nedanstående exempel jobbar jag i Visual Studio 2013.) Tänk dig att jobbar i Windows Forms och att du vill utföra något som kräver ungefär fem sekunders arbete och sedan visar ett meddelande när arbetet är utfört. Min erfarenhet är att Akka .NET fortfarande kan utsättas för förändringar i sitt publika API, så den exakta kodens utseende kan variera – jag kör Akka 1.0.0. För att visa detta skapar jag ett helt nytt Windows Forms-projekt (C# med .NET Framework 4.5.1) och installerar Akka .NET från Package Manager Console genom att skriva:

    Install-Package Akka

    Om du inte ser Package Manager Console, Klicka Tools, NuGet Package Manager och Package Manager Console.

    Akka .NET använder meddelanden för att kommunicera. Meddelanden är kort och gott godtyckliga objekt som initieras och skickas i samband med att ett asynkront anrop görs. Meddelandet bör vara immutable, alltså meddelandet ska inte kunna modifieras, vilket kan implementeras genom att objektets data inte har några publika setters.

    class AkkaMessage
    {
       public string MyValue { get; private set; }
    
       public AkkaMessage(string myValue)
       {
          this.MyValue = myValue;
       }
    }

    En actor (aktör) är den t.ex. det objekt som kan ta emot ett meddelande och agera på det. Det som utmärker en sådan aktör är basklassen Akka.Actor.ReceiveActor samt konstruktorn som tar emot meddelandet som skickas till den. För att ta emot meddelandet används den generiska metoden Receive vars argument är det som ska göras med meddelandet. Den generiska metodens typparameter är meddelandeklassen. Så i följande kod anropar vi Receive och skickar med kod jobbar i fem sekunder för att sedan visa en meddelanderuta.

    class AkkaActor : Akka.Actor.ReceiveActor
    {
       public AkkaActor()
       {
          this.Receive(x =>
             {
                System.Threading.Thread.Sleep(5000);
                System.Windows.Forms.MessageBox.Show(x.MyValue);
             }
          );
       }
    }

    Nu återstår bara initieringen och själva anropet. Följande kod har jag skrivit i mitt programs huvudformulär. Först har jag ett privat fält av typen Akka.Actor.ActorSystem som representerar motorn i Akka – det s.k. actorsystemet. I Load-eventet för formuläret initierar jag systemet med funktionen Create. Create vill ha ett namn på systemet i sin kostruktor. Slutligen, under Click-eventet på en knapp på formuläret så gör jag anropet som ska hanteras av aktören ovan.

    Den första raden anropar funktionen ActorOf för att erhålla en referens till en aktör. Den andra raden utför utför anropet med hjälp av funktionen Tell, och låter ett meddelande skickas med som argument. Meddelandet måste vara av typen AkkaMessage, eftersom jag hårdkodade aktören att hantera den typen.

    public partial class Form1 : Form
    {
       private Akka.Actor.ActorSystem actorSystem;
    
       public Form1()
       {
          InitializeComponent();
       }
    
       private void Form1_Load(object sender, EventArgs e)
       {
          this.actorSystem =
             Akka.Actor.ActorSystem.Create("ActorSystem");
       }
    
       private void button1_Click(object sender, EventArgs e)
       {
          var m = this.actorSystem.ActorOf(new
             Akka.Actor.Props(typeof(AkkaActor)));
          m.Tell(new AkkaMessage("Hello!"), m);
       }
    }

    När du kör programmet, notera att programmets fönster är svarsbenäget under den tid programmet pausar pausar (anropet på Sleep), vilket säger oss att aktören och formuläret körs på olika trådar.

    Om du istället hade velat implementera detta i Visual Basic, skulle ditt meddelande kunna se ut så här:

    Public Class AkkaMessage
       Public Property MyValue() As String
       Public Sub New(MyValue As String)
          Me.MyValue = MyValue
       End Sub
    End Class

    Aktören skulle kunna se ut så här:

    Public Class AkkaActor
       Inherits Akka.Actor.ReceiveActor
    
       Public Sub New()
          Me.Receive(Of AkkaMessage)(AddressOf MyMessageHandler)
       End Sub
    
       Private Sub MyMessageHandler(X As AkkaMessage)
          System.Threading.Thread.Sleep(5000)
          MessageBox.Show(X.MyValue)
       End Sub
    
    End Class

    Och slutligen, formuläret skulle kunna se ut så här:

    Public Class Form1
    
       Private ActorSystem As Akka.Actor.ActorSystem
    
       Private Sub Form1_Load(sender As Object, _
          e As EventArgs) Handles MyBase.Load
          Me.ActorSystem = Akka.Actor.ActorSystem.Create("ActorSystem")
       End Sub
    
       Private Sub Button1_Click(sender As Object, _
          e As EventArgs) Handles Button1.Click
          Dim M = Me.ActorSystem.ActorOf( _
             New Akka.Actor.Props(GetType(AkkaActor)))
          M.Tell(New AkkaMessage("Hello!"), M)
       End Sub
    
    End Class
  • Five cool features in Visual Basic

    1. Case tests

    Test a value against a limit, a range or constants.

    2. Typedefs

    Create custom names for any type. Demo:

    3. Object templates

    Create an enumeration of any type. Demo:

    4. Conditional exception handling

    Exception handlers that combine type checking and logical tests. Demo:

    5. XML literals

    Built in support for XML. Demo:

  • Processor information

    Beginners tend to ask how they can write a program that displays the processor model of a computer. There are a few different methods that can be used for this. You can call the SET command or you can query the registry. To call the SET command, connect to the output from a process and find the processor information in the output data.

    'Load processor information.
    Dim ProcessorIdentifier = ""
    Dim Ps = New ProcessStartInfo("cmd", "/c set")
    Dim Raw As String
    Ps.RedirectStandardOutput = True
    Ps.UseShellExecute = False
    Using P = Process.Start(Ps)
       P.WaitForExit()
       Raw = P.StandardOutput.ReadToEnd()
    End Using
    Dim Rows() = Raw.Split(ControlChars.CrLf.ToCharArray(),
                 StringSplitOptions.RemoveEmptyEntries)
    For Each Row As String In Rows
       If Row.StartsWith("PROCESSOR_IDENTIFIER=") Then
          ProcessorIdentifier = Row.Substring(21)
          Exit For
       End If
    Next
    
    'Display processor information.
    MessageBox.Show(ProcessorIdentifier)

    Another method is to check the computer registry in HKEY_LOCAL_MACHINE at HARDWARE\DESCRIPTION\System\CentralProcessor\0. The key is Identifier.

    'Load processor information.
    Dim ProcessorIdentifier = ""
    Dim Hardware = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("HARDWARE")
    If Not Hardware Is Nothing Then
       Dim CP = Hardware.OpenSubKey("DESCRIPTION\System\CentralProcessor\0")
       ProcessorIdentifier = If(Not CP Is Nothing,
                    CP.GetValue("Identifier").ToString(), "")
    End If
    
    'Display processor information.
    MessageBox.Show(ProcessorIdentifier)
  • What’s all this then?

    What’s all this then?

    This might look strange to a C programmer:

    Dim I(10) As Integer
    Console.WriteLine(I.Length)
    Dim J(I.Length - 1) As Integer
    Console.Write(J.Length)

    This will give the output 11 and 11. The array named I has 11 elements because the last index (10) is given when the array is created. The array named J has 11 elements bacause Length (11) – 1 equals 10, and a 0-based array with last element 10 gives you 11 elements. Confusing.

  • Non-related implicit type casting

    Non-related implicit type casting

    As always, I use Visual Basic in strict mode. I have this function that expects a Foo array:

    Sub DoSomething(X() As Foo)
       Console.WriteLine(X.Length)
    End Sub

    What I’m showing here is true for single variables too, but I am showing this using arrays. This cannot be called using an Integer array as follows:

    DoSomething({1, 2, 3})

    Adding a Integer constructor to the Foo class does not help. However, you can add a widening operator to the Foo class, and define how an implicit conversion is done, like so:

    Public Class Foo
    
       Private mX As Integer
    
       Public Sub New(X As Integer)
          Me.mX = X
       End Sub
    
       Shared Widening Operator CType(X As Integer) As Foo
          Return New Foo(X)
       End Operator
    
    End Class

    Now, both the above method call is accepted. And because this also works on arrays, this simple line constructs three Foo objects:

    Dim X() As Foo = {4, 5, 6}

    The equivalent with a single (non arrayed) object would look like this:

    Dim Y As Foo = 7

    The opposite to Widening is called Narrowing and is used to define explicit type casts.

  • Stealing keys

    Stealing keys

    Events and everything you might need to develop a user control, will tell you about mouse and keyboard activity so that you can respond the user correctly. If you want to be able to catch arrow keys or tab, you must overload this function:

    Protected Overrides Function ProcessCmdKey(ByRef msg
       As System.Windows.Forms.Message, ByVal keyData
       As System.Windows.Forms.Keys) As Boolean

    This method gets called no matter what key is pressed. Also, if you act on the key, and don’t want the original functionality, make the function return True. If you want the framework to act on the key press, make the function return False.

  • Extension methods in Visual Basic

    Extension methods is a well known concept in C#. The idea is that you can add methods to a type without inheritance, as described here.

    Visual Basic doesn’t have any linguistic support for extension methods, but it can be done thanks to a method attribute. There are some rules for doing this. You must declare your sub or function in a standard module, not a class. You must have at least one parameter, and that parameter is the type you are extending. Any other parameters will be parameters in your function. Finally, your sub or function must have the System.Runtime.CompilerServices.Extension attribute.

    So if you want to add a function that takes no parameters to the Integer type, you will have to define a function that takes one parameter, an Integer. If you want to add a function that takes one parameter, you will have to define a function that takes two parameter, the first must be an Integer and the second (who will serve as the first parameter in your function) can be of any type you like.

    Public Function DoubleValue(ByVal I As Integer) As Integer
       Return I * 2
    End Function

    Remember, the code must be written in a standard module.

    The last thing that you have to do, is to add the attribute.

    <System.Runtime.CompilerServices.Extension()>
    Public Function DoubleValue(ByVal I As Integer) As Integer
       Return I * 2
    End Function

    (The above code is VBx syntax. For older Visual Basic versions, add a blank followed by an underscore to the attribute line.)

    Now you can call the DoubleValue function from any Integer, in this case 5:

    Console.WriteLine(5.DoubleValue())

    The output will of course be 10.

  • Visual Basic: Importing namespaces

    In C# you can use the using directive to import the classes in a given namespace to your current scope. If you want to use the classes in System.Data.SqlClient, you could refer to them using their full path, like so:

    var r = new System.Data.SqlClient.SqlDataAdapter();

    You can add namespaces to your scope by adding a using directive in the beginning of your source file. If you add this directive:

    using System.Data.SqlClient;

    You could then access the SqlDataAdapter class in your code, without typing the full path, like so:

    var r = new SqlDataAdapter();

    Visual Basic does not have the using directive, but it has a more powerful directive called Imports. Imports can import both classes and other namespaces. The code that creates a SqlDataAdapter in Visual Basic looks like this:

    Dim R As New System.Data.SqlClient.SqlDataAdapter()

    If you use the Imports directive to import System.Data in the beginning of your file, like so:

    Imports System.Data

    You can access both the namespaces (like SqlClient) and the classes in System.Data without providing the full path.

    Dim R As New SqlClient.SqlDataAdapter()

    If course, you can go on and import the SqlClient namespace to access the SqlDataAdapter class without specifying it’s full path, just as you can do in C#. So, the using directive in C# can import classes, but the Imports directive in VB imports both classes and namespaces.

  • A multithreaded sockets server, just a few lines of code

    This small piece of Visual Basic code (VBx) shows how you can do a multithreaded sockets server in just a few lines of code. I write all of this code in a console application. The main module will only hold an implementation of the Main function. Like so:

    Module Module1
    
        Sub Main()
          Dim L As New System.Net.Sockets.TcpListener(
             New System.Net.IPAddress({192, 168, 1, 100}), 80)
          L.Start()
          Dim ThreadCounter = 0
          Do
             ThreadCounter += 1
             Dim C = L.AcceptTcpClient()
             Dim S As New NetSession(C, ThreadCounter)
             Dim T As New Threading.Thread(AddressOf S.SessionLoop)
             Console.WriteLine("Starting thread " & ThreadCounter.ToString() & ".")
             T.Start()
          Loop
        End Sub
    
    End Module

    Note that I have hard coded my local IP address, and passed it as an argument to the TcpListener constructor, but there are easy ways collect this information using the .NET Framework. The variable C is a System.Net.Sockets.TcpClient, because that is what the AcceptTcpClient function returns. The ThreadCounter is just so that different connections will get a unique session identifier.

    The rest of the code takes place in the NetSession class.

    Public Class NetSession
    
       Private mC As System.Net.Sockets.TcpClient
       Private mS As System.Net.Sockets.NetworkStream
       Private mThreadCounter As Integer
       Private mIndex As String
    
       Public Sub New(ByVal C As System.Net.Sockets.TcpClient,
             ByVal ThreadCounter As Integer)
          Me.mC = C
          Me.mThreadCounter = ThreadCounter
          Me.mIndex = ""
       End Sub
    
       Public Sub SessionLoop()
          Me.mS = Me.mC.GetStream()
          Me.mC.LingerState.Enabled = False
          Me.WriteLine("Connected.")
          While Me.mC.Connected
             If Me.mS.DataAvailable Then
                Dim GotData As New System.Text.StringBuilder()
                While Me.mS.DataAvailable
                   Dim bytes(Me.mC.ReceiveBufferSize - 1) As Byte
                   Dim Len = Me.mS.Read(bytes, 0, Me.mC.ReceiveBufferSize)
                   If Len > 0 Then
                      GotData.Append(System.Text.Encoding.UTF8.GetString(bytes, 0, Len))
                   End If
                End While
                Dim D = GotData.ToString().Trim()
                If Not D = "" Then
                   If Not Me.Handle(D) Then
                      Exit While
                   End If
                End If
             End If
          End While
          Me.mS.Close()
          Me.mS.Dispose()
          Me.mC.Close()
          Console.WriteLine("Thread " & Me.mThreadCounter.ToString() & " is closing.")
       End Sub
    
       Private Sub WriteLine(ByVal T As String)
          Dim Bytes() As Byte = System.Text.Encoding.UTF8.GetBytes(T & ControlChars.CrLf)
          Me.mS.Write(Bytes, 0, Bytes.Count)
       End Sub
    
       Private Function Handle(ByVal D As String) As Boolean
          Console.WriteLine("Thread " & Me.mThreadCounter.ToString() & ": " & D)
          If D.ToLower() = "quit" Then
             Me.WriteLine("Bye.")
             Return False
          Else
    
             'Do parsing here! Return True no matter what.
             Me.WriteLine("Thanks!")
    
             Return True
          End If
       End Function
    
    End Class

    The constructor accepts the TCP client (representing the connected application) and the ThreadCounter variable. The SessionLoop function is the main loop for each thread.

    The WriteLine function sends responses to the connected client application and the Handle function is the place where you implement your parsing (or invokes some existing parser). Note that the Handle function must return True, even if your parsing fails. Failed parsing should render in an error message to the client application, but the function should still return True. The returning of the value False, is a signal that the client disconnects.

    When you run this code, you will get a warning from your Windows Firewall (I run a Swedish Windows 7 installation). Accept that this program can use your local network.

    To make this program actually do something, start a sockets client, like PuTTY. Configure it like so:

    Host IP: [Your local IP]
    Port: 80
    Encoding: UTF-8

    Note that we use UTF-8 encoding to convert between byte arrays and strings in the source code, so the TCP client must also use UTF-8.

    Since our server is multithreaded and have multiple connection awareness, you can connect more than one sockets client to your application.

    Send Quit from your client application to quit.

  • Environment variables

    Assuming that you are running Windows 7, the environment variables can be manually accessed if you right click on Computer, and select Properties. From there, you click Advanced system settings and on the Advanced tab, you click the Environment variables button. You can check your current variables, edit them and add new variables.

    I am running a Swedish installation of Windows 7.

    To read them from Visual Basic, you just use the GetEnvironmentVariables function to retrieve the collection of registered variables. This example is written in VBx (Visual Basic 10):

    Console.WriteLine(
       Environment.GetEnvironmentVariables().
          Item("PROCESSOR_REVISION").ToString())

    Remember, when you write code to manipulate this collection, adding or changing values, you would normally want your code to run as a custom step in an MSI file, and not in a regular EXE.

  • Getting started with Silverlight in an existing web application

    To get things started, I will show how to enhance a web application with a Silverlight object, and how to pass parameters to that object. To be able to follow, make sure that you have the Silverlight runtime installed. Also, you must have an existing ASP.NET Web Application loaded in Visual Studio 2010. If you’re using Visual Studio 2008, there is an Silverlight addon available for download from Microsoft.

    Adding a Silverlight object and passing data to it
    I want to have a Silverlight object on a sub page, so I add a new ASPX page to my web application called sl.aspx. Also, I create a link to sl.aspx from my default page. I want my Silverlight object to accept a QueryString argument, so I pass one in my link:

    <a href="sl.aspx?param=hello">Click here!</a>

    1. Add a new Silverlight project to your solution. I use the name MySL for mine. Default, a test page is added. I unchecked the option to make the test page my start page.

    2. The test page is a good help to copy the HTML code that is required to show the object from. The two JavaScript blocks goes in the head section and the OBJECT tag goes where you want your Silverlight object. Grab the DIV tag that contains the OBJECT tag and the IFRAME tag.

    Now you’re done with adding the object. If you test your application, and click on your link, your (empty) Silverlight object.

    3. I want to show the QueryString parameter in a textbox, so I add one to my Silverlight object (TextBox1). This is the code to grab the QueryString parameter and putting it in the textbox:

    Private Sub MainPage_Loaded(ByVal sender As Object, _
    ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
       TextBox1.Text = _
       System.Windows.Browser.HtmlPage.Document.QueryString("param")
    End Sub

    The result of course, is the word “hello” in the textbox.

  • Big integers in .NET 4.0

    The BigInteger structure becomes available if you add a reference to the System.Numerics namespace. BigInteger represents a positive or negative integer of any size.  This is great for doing arithmetic calculations with very large numbers, and is one of the problems you had to solve on your own in previous versions of .NET Framework.

    After the reference is added, you can create a BigInteger using the New keyword, and an initial value can be passed to the constructor, like so:

    Dim X As New System.Numerics.BigInteger(Long.MaxValue)
    Console.WriteLine(X.ToString())

    To do arithmetic operations, create the BigIntegers you need for the operation, and then call the static (shared) functions of the BigInteger structure to do the calculations. In this case, I call the static function Multiply.

    Dim X As New System.Numerics.BigInteger(Long.MaxValue)
    Dim Y As New System.Numerics.BigInteger(Long.MaxValue)
    Dim Z As System.Numerics.BigInteger = _
         System.Numerics.BigInteger.Multiply(X, Y)
    Console.WriteLine(Z.ToString())

    Just by adding a few of these lines to the above code, will give you one insanely large number.

    Z = System.Numerics.BigInteger.Multiply(Z, Z)
    Z = System.Numerics.BigInteger.Multiply(Z, Z)
    Z = System.Numerics.BigInteger.Multiply(Z, Z)
    Z = System.Numerics.BigInteger.Multiply(Z, Z)

    One way to serialize this number in SQL Server could be to store the underlying bytes of the number that the BigInteger instance represents. The BigInteger structure has a member function that returns these bytes as a byte array called GetByteArray. An existing byte array can be passed to the constructor of the BigInteger to reconstruct the number.

  • Hardware accelerated graphics through XNA: Getting started

    There are some features of the XNA Framework that is unavailable from Visual Basic, but this should not stop you from writing descent games in Visual Basic. On my machine, I have installed XNA Game Studio 3.1 (a game developing environment from Microsoft) and I also have a beta of Visual Studio 2010 that I am going to use. This example will just contain the code necessary to get something on the screen, a sprite floating across.

    From VS2010, I am using a regular console application and the target platform for the project is .NET Framework 3.5.

    Now I must add two references: Microsoft.Xna.Framework and Microsoft.Xna.Framework.Game. I use version 3.1, the version that got installed when I installed XNA Game Studio 3.1.

    The next step is to create the game class. I call my class TestGame. TestGame should inherit from the Microsoft.Xna.Framework.Game class. In here I create a Main method to get the program started, and I select that method to be the starting point for the program in the Project Settings window. This is the code so far:

    Public Class TestGame
        Inherits Microsoft.Xna.Framework.Game
    
        Public Shared Sub Main()
    
        End Sub
    
    End Class

    In the Main method, I create my game (the TestGame class) and from the constructor, a graphics device manager for the game. I use the graphics device manager to set my preferred resolution (800×600) and to switch to fullscreen mode. Note that I want to keep the reference to the graphics device manager as a member of my game class.

    Public Class TestGame
        Inherits Microsoft.Xna.Framework.Game
    
        Private Gfx As Microsoft.Xna.Framework.GraphicsDeviceManager
    
        Public Shared Sub Main()
            Dim Game As New TestGame()
            Game.Run()
        End Sub
    
        Public Sub New()
            Me.Gfx = New Microsoft.Xna.Framework.GraphicsDeviceManager(Me)
            Me.Gfx.PreferredBackBufferWidth = 800
            Me.Gfx.PreferredBackBufferHeight = 600
            If Not Me.Gfx.IsFullScreen Then
                Me.Gfx.ToggleFullScreen()
            End If
        End Sub
    
    End Class

    The next thing to do is some overrides from the base class. These methods will be overloaded:

    Protected Overrides Sub Initialize()
        MyBase.Initialize()
    End Sub
    
    Protected Overrides Sub LoadContent()
        MyBase.LoadContent()
    End Sub
    
    Protected Overrides Sub UnloadContent()
        MyBase.UnloadContent()
    End Sub
    
    Protected Overrides Sub Update(ByVal gameTime As Microsoft.Xna.Framework.GameTime)
        MyBase.Update(gameTime)
    End Sub
    
    Protected Overrides Sub Draw(ByVal gameTime As Microsoft.Xna.Framework.GameTime)
        MyBase.Draw(gameTime)
    End Sub

    Just to make something happen on the screen, I am adding these members:

    Private Sb As Microsoft.Xna.Framework.Graphics.SpriteBatch
    Private SpriteTexture As Microsoft.Xna.Framework.Graphics.Texture2D
    Private SpriteX As Integer = 0
    Private SpriteY As Integer = 0

    The SpriteBatch will manage my sprites and the Texture2D is the sprite graphics. In the LoadContent function, I will load a sprite from my hard drive.

    Protected Overrides Sub LoadContent()
        Me.Sb = New Microsoft.Xna.Framework.Graphics.SpriteBatch(Me.Gfx.GraphicsDevice)
        Me.SpriteTexture = Microsoft.Xna.Framework.Graphics.Texture2D.FromFile(Me.Gfx.GraphicsDevice, _
             "mysprite.png")
        MyBase.LoadContent()
    End Sub

    The Update function is for changing the game scenery.

    Protected Overrides Sub Update(ByVal gameTime As Microsoft.Xna.Framework.GameTime)
        SpriteX += 1
        SpriteY += 1
        MyBase.Update(gameTime)
    End Sub

    And the Draw function is for screen rendering.

    Protected Overrides Sub Draw(ByVal gameTime As Microsoft.Xna.Framework.GameTime)
        Me.Gfx.GraphicsDevice.Clear(Microsoft.Xna.Framework.Graphics.Color.Black)
        Me.Sb.Begin(Microsoft.Xna.Framework.Graphics.SpriteBlendMode.AlphaBlend)
        Me.Sb.Draw(Me.SpriteTexture, New Microsoft.Xna.Framework.Rectangle(Me.SpriteX, Me.SpriteY, 32, 32), _
             Microsoft.Xna.Framework.Graphics.Color.Red)
        Me.Sb.End()
        MyBase.Draw(gameTime)
    End Sub

    This is the complete code that produces a sprite that floats over the screen in Visual Basic using XNA:

    Public Class TestGame
        Inherits Microsoft.Xna.Framework.Game
    
        Private Gfx As Microsoft.Xna.Framework.GraphicsDeviceManager
    
        Private Sb As Microsoft.Xna.Framework.Graphics.SpriteBatch
        Private SpriteTexture As Microsoft.Xna.Framework.Graphics.Texture2D
        Private SpriteX As Integer = 0
        Private SpriteY As Integer = 0
    
        Public Shared Sub Main()
            Dim Game As New TestGame()
            Game.Run()
        End Sub
    
        Public Sub New()
            Me.Gfx = New Microsoft.Xna.Framework.GraphicsDeviceManager(Me)
            Me.Gfx.PreferredBackBufferWidth = 800
            Me.Gfx.PreferredBackBufferHeight = 600
            If Not Me.Gfx.IsFullScreen Then
                Me.Gfx.ToggleFullScreen()
            End If
        End Sub
    
        Protected Overrides Sub Initialize()
            MyBase.Initialize()
        End Sub
    
        Protected Overrides Sub LoadContent()
            Me.Sb = New Microsoft.Xna.Framework.Graphics.SpriteBatch(Me.Gfx.GraphicsDevice)
            Me.SpriteTexture = Microsoft.Xna.Framework.Graphics.Texture2D.FromFile(Me.Gfx.GraphicsDevice, _
                  "mysprite.png")
            MyBase.LoadContent()
        End Sub
    
        Protected Overrides Sub UnloadContent()
            MyBase.UnloadContent()
        End Sub
    
        Protected Overrides Sub Update(ByVal gameTime As Microsoft.Xna.Framework.GameTime)
            SpriteX += 1
            SpriteY += 1
            MyBase.Update(gameTime)
        End Sub
    
        Protected Overrides Sub Draw(ByVal gameTime As Microsoft.Xna.Framework.GameTime)
            Me.Gfx.GraphicsDevice.Clear(Microsoft.Xna.Framework.Graphics.Color.Black)
            Me.Sb.Begin(Microsoft.Xna.Framework.Graphics.SpriteBlendMode.AlphaBlend)
            Me.Sb.Draw(Me.SpriteTexture, New Microsoft.Xna.Framework.Rectangle(Me.SpriteX, Me.SpriteY, 32, 32), _
                    Microsoft.Xna.Framework.Graphics.Color.Red)
            Me.Sb.End()
            MyBase.Draw(gameTime)
        End Sub
    
    End Class
    
  • The simplest query tool ever

    A colleague wanted to do a database query from a computer without any database client. He needed a tool that allowed him to type in a SQL query, and receive a sortable grid with the result set. All of these features are built-in in the .NET Framework, and it didn’t take me more than 2 minutes to do an exe file with these features using Visual Studio 2010. I used .NET 2.0 because these basic features are available in that version, and he did not want to install a newer version of the .NET Framework.

    This is the user interface: A tab strip with three tabs. One for a connection string, one for the query and one for the result grid. The first two contains a textbox each, and the third contains a DataGridView control. Also, the program has a status bar with a label and a toolstrip with two buttons; one for testing the connectionstring that the user enters in the textbox of the first tab, and one for executing the query and presenting the result in the grid.

    The program consist one variable and four event handlers in one form. The variable holds the result set.

    Private Ds As DataSet

    The first function responds to the Load event of the form. This function restores the last values that from the textboxes. This is just for user convenience.

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles MyBase.Load
        txtCn.Text = CType(Application.UserAppDataRegistry.GetValue("Cn", ""), String)
        txtQuery.Text = CType(Application.UserAppDataRegistry.GetValue("Query", ""), String)
    End Sub

    The second function responds to the Close event of the form. This function saves the values from the textboxes so that they can be restored (in the Load event) in the next session. Also, if needed, it disposes the dataset variable.

    Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) _
    Handles Me.FormClosed
        Application.UserAppDataRegistry.SetValue("Cn", txtCn.Text)
        Application.UserAppDataRegistry.SetValue("Query", txtQuery.Text)
        If Not Ds Is Nothing Then
            Ds.Dispose()
        End If
    End Sub

    This is the handler for the test button. It simply connects to the given data source, and tells if it succeeds or fails.

    Private Sub btnTestConnection_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles btnTestConnection.Click
        'Jump to the connection tab (the first one).
        TabControl1.SelectedTab = tabConnection
    
        'Do the test.
        Me.Cursor = Cursors.WaitCursor
        Dim Success As Boolean = False
        Try
            Using Cn As New SqlClient.SqlConnection(txtCn.Text)
                Cn.Open()
                Success = (Cn.State = ConnectionState.Open)
                Cn.Close()
            End Using
        Catch ex As Exception
        End Try
        Me.Cursor = Cursors.Default
        If Success Then
            lblStatus.Text = "Connection test succeeded."
            MessageBox.Show("Connection test succeeded.", Me.Text, _
                MessageBoxButtons.OK, MessageBoxIcon.Information)
        Else
            lblStatus.Text = "Connection test failed."
            MessageBox.Show("Connection test failed.", Me.Text, MessageBoxButtons.OK, _
                MessageBoxIcon.Error)
        End If
    End Sub

    Finally, this is the handler for the execute button.

    Private Sub btnExecute_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles btnExecute.Click
        'Remove the current data table as data source of the grid.
        DataGridView1.DataSource = Nothing
    
        If Not Me.Ds Is Nothing Then
            'If there is an old data set in memory, dispose that.
            Me.Ds.Dispose()
            'To remember that no data is present, set the variable to NULL.
            Me.Ds = Nothing
        End If
      
        'Jump to the result tab (the third one).
        TabControl1.SelectedTab = tabResult
    
        'Do the query and bind the result. Quick and dirty error "handling".
        Me.Cursor = Cursors.WaitCursor
        Try
            Using Cn As New SqlClient.SqlConnection(txtCn.Text)
                Cn.Open()
                Using Cmd As New SqlClient.SqlCommand(txtQuery.Text, Cn)
                    Using Da As New SqlClient.SqlDataAdapter(Cmd)
                        Ds = New DataSet()
                        Da.Fill(Ds)
                        If Ds.Tables.Count > 0 Then
                            DataGridView1.DataSource = Ds.Tables(0)
                            If Ds.Tables.Count > 1 Then
                                Me.Cursor = Cursors.Default
                                MessageBox.Show("More than one dataset was returned.", "Query", _
                                    MessageBoxButtons.OK, MessageBoxIcon.Information)
                            End If
                        Else
                            Me.Cursor = Cursors.Default
                            MessageBox.Show("No dataset was returned.", "Query", _
                                MessageBoxButtons.OK, MessageBoxIcon.Information)
                        End If
                    End Using
                End Using
                Cn.Close()
            End Using
            lblStatus.Text = "Success."
        Catch ex As Exception
            lblStatus.Text = "Failed. " & ex.Message
                Me.Cursor = Cursors.Default
            MessageBox.Show(ex.Message, "Failed", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
        Me.Cursor = Cursors.Default
    End Sub

    So, making a personal query tool doesn’t have to take more than a couple of minutes in .NET.

  • ASP.NET 4.0: Diagrams

    To get your hands on this new and cool feature, create an ASP.NET 4.0 application or web site from within Visual Studio 2010. Check the Data section of the Toolbox window for a new control called Chart. Dragging out a Chart onto a web page creates the following code:

    <asp:Chart ID="Chart1" runat="server">
      <Series>
        <asp:Series Name="Series1">
      </asp:Series>
      </Series>
        <ChartAreas>
          <asp:ChartArea Name="ChartArea1">
        </asp:ChartArea>
      </ChartAreas>
    </asp:Chart>

    There is a huge amount of properties available for the chart. They control the look of the chart, the chart type among other things. I don’t have any databases installed on this computer, so to get some data to do a chart from; I connect the control to a data source that represents the system database master. I use the following query to get a bunch of large numbers:

    SELECT [name],[number] FROM dbo.spt_values WHERE [number]>17000

    I use number as Y value member.

    This is what I have to do to get the fully functional chart! Fooling around with chart types and visual properties is dangerously amusing.

    If you haven’t got a database server at all, you can quickly create an object data source in Visual Basic, and assign that to your chart. Connection this code…

    Public Class MyChartData
    
        Private mValue As Integer
    
        Public Sub New(ByVal Value As Integer)
            Me.mValue = Value
        End Sub
    
        Public ReadOnly Property Value() As Integer
            Get
                Return Me.mValue
            End Get
        End Property
    
        Public Shared Function GetItems() As MyChartData()
            Dim Ret As New List(Of MyChartData)()
            Ret.Add(New MyChartData(10))
            Ret.Add(New MyChartData(20))
            Ret.Add(New MyChartData(15))
            Ret.Add(New MyChartData(25))
            Return Ret.ToArray()
        End Function
    
    End Class

    …gives you this beautiful result:

    Ah, the simplicity! Aaaaah, the power!

  • Collection initializers

    Now I have had the opportunity to try out the new collection initializers of Visual Basic 10. This is good stuff and I am very happy about being able to do line breaks when I use this (just as C programmers can do whenever they want). Examine this simple example that consist of a ContactPerson class, and a generic collection that I initialize using the collection initializer.

    Module Module1
    
        Public Class ContactPerson
            Public FirstName As String
            Public LastName As String
            Public EMail As String
    
            Public Overrides Function ToString() As String
                Return Me.FirstName & " " & Me.LastName & " " & Me.EMail
            End Function
        End Class
    
        Sub Main()
    
            Dim L As New List(Of ContactPerson) From {
             {New ContactPerson With {.FirstName = "Anders", .LastName = "Andersson", .EMail = "anders@some.thing"}},
             {New ContactPerson With {.FirstName = "Bertil", .LastName = "Bengtsson", .EMail = "bertil@some.thing"}},
             {New ContactPerson With {.FirstName = "Calle", .LastName = "Ceder", .EMail = "calle@some.thing"}}
             }
    
            For Each P As ContactPerson In L
                Console.WriteLine(P.ToString())
            Next
    
        End Sub
    
    End Module

    Note that the element type does not require any special constructor, I can provide a constructor and target that, but here I am using the object initializer that was introduced in Visual Basic 9.

    Instead of the parenthesis that I would use to call the List constructor, I use the From keyword with a curly bracket, and each element is surrounded by curly brackets, separated by a comma. Finally, I have to use another set of curly brackets because I use the object initializer for each element, instead of a regular constructor.

    If I adapt this code to use a constructor for each element, this is how it could look. This uses the new collection initializer and traditional constructors (and therefor less curly brackets).

    Module Module1
    
        Public Class ContactPerson
            Public Sub New(ByVal FirstName As String, ByVal LastName As String, ByVal EMail As String)
                Me.FirstName = FirstName
                Me.LastName = LastName
                Me.EMail = EMail
            End Sub
            Public FirstName As String
            Public LastName As String
            Public EMail As String
    
            Public Overrides Function ToString() As String
                Return Me.FirstName & " " & Me.LastName & " " & Me.EMail
            End Function
        End Class
    
        Sub Main()
    
            Dim L As New List(Of ContactPerson) From {
             {New ContactPerson("Anders", "Andersson", "anders@some.thing")},
             {New ContactPerson("Bertil", "Bengtsson", "bertil@some.thing")},
             {New ContactPerson("Calle", "Ceder", "calle@some.thing")}
             }
    
            For Each P As ContactPerson In L
                Console.WriteLine(P.ToString())
            Next
    
        End Sub
    
    End Module
  • Collection initializers

    There are only a few new changes and additions made to the language Visual Basic in version 10. Changes in version 7, 8 and 9 were huge, and from version 9, I really like what it has become, and all that is needed in version 10 is some polishing. I will spend a few posts on some of the features.

    In version 9, we got object initializers and in Visual Basic 10 the initializers will work for arrays and collections. Microsoft call them Collection Initializers. This is a good example of polishing, because the object initializers did not help that much, especially not if you have implemented the constructors that you desire.

    A Collection Initializer is a different story. Here, we actually accomplish something useful.

    Now, we can create a collection and initialize it with some elements that also are initialized, all in one single line!

    The syntax is:

    Dim Variable = New CollectionClass = {{element initialization}, {element initialization}, …}

    If you want to initialize a collection of objects, and each object is initialized with two integer values (this could be a polygon with points), the could would look like this (if I use the generic collection):

    Dim C = New List(Of MyPoint) From {{10, 20}, {15, 25}, {30, 40}}

    This would require 4 lines in Visual Basic 9. One to create the collection, and one for each element. If you like, you can break the line at a comma in Visual Basic 10. Good for formatting.