Blog

  • SQL Server Dependency Browser

    SQL Server Dependency Browser

    SQL Server Dependency Browser for 2012 or later is made open source here.

    Installation: Sql2012DepSetup.exe

  • Lite nyheter i C# 7: Lokala funktioner

    Lite nyheter i C# 7: Lokala funktioner

    Till en viss grad har man kunnat skapa lokala funktioner i C# under en tid.

    public void DoSomething()
    {
        Func<int, int> d = (x) => x * 2;
        for (var i = 1; i <= 8; i++)
            Console.WriteLine(d(i));
    }

    I C# 7 kan man skapa fullvärdiga funktioner i en annan funktion. Det enda som egentligen skiljer en lokal funktion från en medlemsfunktion (eller en static) är att lokala funktioner inte har någon angiven synlighet, eftersom det inte är relevant.

    public void DoSomething()
    {
        int d(int x) => x * 2;
        for (var i = 1; i <= 8; i++)
            Console.WriteLine(d(i));
    }

    Fördelen med riktiga lokala funktioner är att det ger tillgång till parameterarrayer, referensparametrar, utparametrar och generics, alltså sådant som inte kunde användas i gamla inline-funktioner.

    Closure-hanteringen är lite intressant. Följande exempel använder bara gammal C# och kompilerar inte, eftersom variabeln MinFunktion som håller inlinefunktionen inte finns än.

    public void DoSomething()
    {
        MinFunktion();
        int x = 10;
        Action MinFunktion = () => x++;
        Console.WriteLine(x);
    }

    Om vi istället flyttar ner anropet, skrivs 11 ut på skärmen, eftersom x ökas med 1 i MinFunktion.

    public void DoSomething()
    {
        int x = 10;
        Action MinFunktion = () => x++;
        MinFunktion();
        Console.WriteLine(x);
    }

    Resultatet blir detsamma om jag ersätter MinFunktion med en fullvärdig lokal funktion. Det x som ökas i MinFunktion är en closure.

    public void DoSomething()
    {
        int x = 10;
        void MinFunktion() => x++;
        MinFunktion();
        Console.WriteLine(x);
    }

    Men detta är riktigt intressant. Eftersom MinFunktion nu är en fullvärdig funktion, så skapas den i den yttre funktionen DoSomething vid kompilering. Så nu kan jag anropa den innan variabeln x finns.

    public void DoSomething()
    {
        MinFunktion();
        int x = 10;
        void MinFunktion() => x++;
        Console.WriteLine(x);
    }

    Svaret blir då 10, eftersom det inte är closure-variabeln x som blir ökad, utan en x som är lokal för MinFunktion.

    Slutligen, eftersom lokala funktioner är fullvärdiga funktioner, kan de innehålla egna lokala funktioner. Snyggt!

    Så vad fungerar inte?

    Generellt sett kan en värdetyp inte innehålla null, men en referenstyp kan innehålla null. Detta är vettigt, eftersom en referenstyp innehåller just en referens som kan vara eller inte vara satt.

    Vi har haft nullable types i C# en tid nu. Det är en struktur som ger värdetyper en null-flagga, så att dessa kan vara null.

    public int X { get; set; } //Ej initierad - innehåller 0.
    public int? Y { get; set; } //Ej initierad - innehåller null.
    

    Snart ska man kunna förhindra referenstyper från att vara null, vilket är praktiskt t.ex. om man vill att en parameter ska vara obligatorisk. Troligen kommer det att se ut ungefär så här:

    public MyType A { get; set; } //Kan innehålla null
    public MyType! B { get; set; } = new MyType(); //Kan inte innehålla null
    

    C# 7 ska ha visst stöd för pattern matching, men det fungerar inte (än).

    Inte heller record types fungerar. Record types är klasser som endast innehåller properties, som deklareras med en enda rad kod.

    Det finns alltså anledning att titta med på C# 7 längre fram.

  • Lite nyheter i C# 7: Tuples

    Lite nyheter i C# 7: Tuples

    Om jag vill skapa en funktion som skickar tillbaka två värden, kan det lösas med en tuple. I .NET Framework är tuples implementerat som en generisk klass. Denna funktion skickar tillbaka en int och en string:

    public static Tuple<int, string> DoSomething()
    {
        return Tuple.Create(10, "Hello");
    }

    Ett anrop som tar emot svaren skulle kunna se ut så här. I detta fall är response av typen Tuple<int, string>, vilket innebär att Item1 är en int och Item2 en string.

    var response = DoSomething();
    Console.WriteLine(response.Item1); //10
    Console.WriteLine(response.Item2); //"Hello"

    Eftersom C# 7 har inbyggt stöd för tuples, behöver jag inte hänvisa till klassen Tuple. Det går helt enkelt att deklarera fler returer på en funktion.

    Samma funktion i C# 7 kan se ut så här (givet att NuGet-paketet System.ValueTuple är på plats):

    public static (int, string) DoSomething()
    {
        return (10, "Hello");
    }

    Koden som utför anropet kan se lika dan ut. Variabeln response är nu en (int, string) istället för en Tuple<int, string>.

    Funktionens returvärden kan dessutom ha specificerade namn, vilket gör koden mer självdokumenterande.

    public static (int i, string s) DoSomething()
    {
        return (10, "Hello");
    }

    Anropet:

    var response = DoSomething();
    Console.WriteLine(response.i); //10
    Console.WriteLine(response.s); //"Hello"

    Tuples kan skapas inline (x nedan), inline med namngivna medlemmar (y nedan) eller inline med type inference (z nedan).

    (int, int, int) x = (10, 20, 30);
    Console.WriteLine(x.Item1); //10
    
    (int kalle, int pelle, int sven) y = (10, 20, 30);
    Console.WriteLine(y.kalle); //10
    
    var z = (10, 20, 30);
    Console.WriteLine(z.Item1); //10
  • Föreläsning om Commodore 64

    Föreläsning om Commodore 64

    Tisdagen den 27 september kl. 18:00 bjuder ABF i Örebro på en föreläsning om Commodore 64, i ABF:s lokaler på Fredsgatan 18 i Örebro.

    Commodore 64 är fortfarande världens mest sålda hemdator, och trots att det gått 34 år sedan den introducerades på marknaden, släpps fortfarande nya spel till systemet, som fortfarande imponerar när det gäller grafik och ljud. Commodore 64 slog inte bara konkurrenterna Sinclair, Texas Instruments och Atari på fingrarna under 80-talet, utan lyckades överleva dem alla. Vad var det som gjorde att just Commodore 64 engagerade så många användare, programmerare, grafiker och musiker? Vad kunde datorn som inga andra system klarade av? Och vad är det som fortfarande lockar med just Commodore 64?

    Föreläsare är Anders Hesselbom som har varit hemdatoranvändare sedan 1982 och idag är en inbiten programmerare och Commodore-entusiast.
    I denna föreläsning berättar han varför just Commodore 64 sticker ut i jämförelse med andra system.

    Hörselslinga finns.
    Fika finns till försäljning

    Facebook-event finns här. Föreläsningen är gratis och öppen för allmänheten. Välkommen!

  • Commoflage 69

    Commoflage 69

    Jag hade den stora äran att bli inbjuden till podcasten Commoflage avsnitt 69, där vi bl.a. pratar Ghostbusters. Och som vanligt spelas det en hel del bra C64-musik. Klicka här för att komma till avsnittet.

    12116295_1507265992904073_867537746_o

    Jag kommenterar även den infekterade Ghostbusters-debatten på min blogg.

  • Captured (Lars Hård)

    Captured (Lars Hård)

    An attempt to cover the C64 classic Captured soundtrack by Lars Hård.

    Om du vill höra mer musik, mitt album från 2004 (tillsammans med Tommy Deile) finns på nätet här: Single Point of Failure – This time we are both

    Mina C64-covers finns här: http://www.remix64.com/act/anders-hesselbom/

    Dessutom, om du vill lyssna på mitt sommarprat i podcasten Kvack, om bl.a. min musik, så finns den här: http://kvackyou.se/2016/07/kvacksnack-sommar-2016-anders-hesselbom/

  • Freddie Mercury – Mr. Bad Guy (1985)

    Freddie Mercury – Mr. Bad Guy (1985)

    En kort recension av Freddie Mercurys “Mr. Bad Guy” från 1985.

  • Hur kan vi utnyttja evolutionen?

    Hur kan vi utnyttja evolutionen?

    Tack vare den ofrånkomliga utveckling som följer från förändring över tid, med selektion, och tack vare att vem som helst idag har tillgång till starka datorer, kan vi tämja evolutionen till att göra lite vad vi vill. Låt säga att jag t.ex. vill skapa ett korsord från en lista av ord. Antingen sätter jag mig ner och flätar orden manuellt, vilket kan vara nog så svårt. Eller så använder jag min PC för att skapa en algoritm som beskriver hur orden ska flätas ihop, vilket inte heller är speciellt enkelt.

    Fördelen med evolutionära algoritmer är att jag bara behöver veta vad jag vill åstadkomma – jag behöver inte tänka på hur något görs. Om det är ett korsord som ska byggas, så skulle jag kunna tänka mig att mitt indata är t.ex. 20 – de ord som ska flätas ihop till ett korsord. Om jag kan definiera vad ett bra korsord är, så kan jag göra ett urval. Och kan jag göra ett urval, så kan jag låta slumpen göra jobbet. Jag behöver även ha två regler: en regel som säger att t.ex. ordet ASKA inte får korsas med ordet PILBÅGE, och en regel som säger att om ordet ASKA ska korsas t.ex. med ordet MATROS, så måste det ske på någon av de gemensamma bokstäverna (vilket är S eller A).

    En tänkt initiering av korsordet skulle kunna vara att skapa 15 giltiga korsord genom att låta slumpen avgöra om ett ord ska vara vågrätt eller lodrätt, och sedan placera ut ordet på en tänkt matris, på en slumpvis utvald plats. Om ordet inte kan placeras på den plats som tilldelas, så slumpar vi fram en ny plats, så att reglerna upprätthålls. Detta upprepas för varje ord till dess att alla ord ligger på en giltig plats, vilket innebär att vi har ett giltigt korsord, om än förmodligen ett väldigt spretigt sådant.

    Dessa 15 korsord kan vi betrakta som en lista av föräldrar. Jag brukar jobba med två generationer – en föräldrageneration och en barngeneration. Och det är från någon av dessa 15 korsord som det perfekta korsordet ska komma.

    Nästa steg blir att låta varje korsord få 3 barn. Ett barn i detta fall är en kopia av ett korsord, med en slumpmässig förändring. Man kan låta slumpen avgöra om man ska positionera om ett ord, två ord eller tre ord. Man kan låta slumpen avgöra om något eller alla av dessa ord ska förvandlas från vågrätt till lodrätt, eller tvärt om. Vi som bygger evolutionära algoritmer har lånat en biologisk term för detta skeende, nämligen mutation. Barnen, 45 stycken, lagras i listan som representerar barngenerationen.

    Varje barn poängsätts efter hur väl det är anpassat till sin miljö (där miljö egentligen är mina kriterier). En liten area ger högre poäng än en stor area, antal intersektioner mellan ord ger poäng, en mer rektangulär form ger högre poäng än en mer långsmal form, och så vidare. När alla 45 barnkorsord är poängsatta, sparar jag de bästa 15 i föräldragenerationen, tömmer barngenerationen, och upprepar processen.

    Över tid kommer kvalitén (alltså likheten med mina kriterier) att förbättras, men om tillräckligt lång tid får passera så upphör förbättringen. Man kan tänka sig ett tröskelvärde som säger att om inte något korsord har förbättrats på, säg, 10 000 generationer, så är vi nöjda med vårt korsord – det korsord som har högst poäng.

    Och därmed har jag skapat ett korsord utan att själv vara förmögen att varken placera ut orden på egen hand eller beskriva den algoritm som skulle göra detta. Allt samman bygger på att nästan var och varannan person idag, i sin ficka, förvaltar något som för 20 år sedan skulle betraktas som en superdator.

  • .NET Sprite

    .NET Sprite

    Hårdvarurenderade sprites i C#:

    Github: https://github.com/r1sc/Sprite
    Nuget: https://www.nuget.org/packages/Sprite/

    …och så några “bloopers“…

  • Shadow of the Beast

    Shadow of the Beast

    My cover version of the soundtrack to the Amiga version of Shadow of the Beast, written by David Whittaker.

  • Pengar är ingen motivator

    Pengar är ingen motivator

    Arbetslösheten i Sverige har ökat stadigt sedan början av 70-talet, då den var 2%. Idag får vi räkna med att staten kostar pengar, att kommunerna kostar pengar, och att varje medborgare dessutom måste försörja 1/10 av en medmänniskas utgifter. Mycket få arbetslösa blir motiverade av betald utbildning och löfte om anställning – de som finner detta attraktivt återfinns bland den redan arbetande skaran.

    Till en viss grad kan man lösa problem med statens finanser genom att höja skatten. T.ex. anser rikspolischef Dan Eliasson att vi har för få poliser, vilket rimligtvis är ett problem som kan lösas med skattehöjningar, om inte omprioriteringar.

    Jag är själv anhängare av hypotesen att skatt kan vara ett styrmedel, vilket innebär att högre beskattning ger lägre konsumtion. I Sverige praktiseras detta på bensin, tobak och alkohol, men jag vet att idén inte är helt okontroversiell. Det finns dem som anser att hög skatt odiskutabelt leder till ökade inkomster för stat eller kommun. Rimligtvis anser alla att det finns en gräns, om inte annat när man passerar 100% inkomstskatt, och måste betala mer än man tjänar. Men de som kritiserar denna idé anser att man i alla tillstånd under 100% kan öka statens intäkter genom att höja skatten. Hur det än ligger till med denna sak, så är vi tvungna att förhålla oss – pengar är inte alltid en motivatör.

    Som invånare i Mellansverige kan jag konstatera att en arbetssökande “hen” här, har anmält till diskrimineringsombudsmannen (DO) att hen kallats för hon. Jag försvarar hens rätt att bli kränkt till döden, men om en kränkning ska anmälas till DO, har jag svårt att se vilket jobb som är lämpligt för hen. Det ligger i sakens natur att en anställning följer till att göra saker man kanske inte alltid vill göra, och att man utsätter sig för saker man inte alltid vill utsätta sig för. Även jag själv kan ibland sitta och skriva kod som visualiserar affärslogik medan jag drömmer om att bygga ett actionspel där rymdskepp jagas av laserkanoner. Och jag tänker inte låta mig övertalas att det finns någon hen i hela världen som har haft en mindre komplicerad uppväxt än vad jag själv har haft – jag råkar bara ha en omodern könsidentitet.

    Aftonbladet: Kallade “hen” för “hon” – anmäls för diskriminering

  • Hur hittar spöket Pac-Man?

    Hur hittar spöket Pac-Man?

    En mycket enkel men mycket effektiv algoritm som används i Pac-Man.

  • List invocations in constructor

    List invocations in constructor

    This example shows how to list the method invocations in a C# file. This is the file that will be read:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication1
    {
      public class MyClass
      {
        public MyClass()
        {
          Console.WriteLine("Hello.");
          DoSomething();
          DoSomethingElse();
        }
    
        private void DoSomething()
        {
        }
    
        private void DoSomethingElse()
        {
        }
      }
    }

    To install CodeSearchTree in your Visual Studio project, type Install-Package CodeSearchTree in the package manager console. Your project must use .NET Framework 4.6 or higher.

    The path to the first method invocation is ns/cls/constructor/block/expression/invocation, but since each invocation is located under an expression node, we will iterate through those. The code will list the three calls made in the constructor of the example class above.

    //Load the C# class file.
    var tree = CodeSearchTree.Node.CreateTreeFromFile(@"MyClass.cs");
    
    //Get the first expression in the constructor.
    var exp = tree.GetChild("ns/cls/constructor/block/expression");
    
    while (exp != null)
    {
      //Get the invocation and type out the source code.
      var invocation = exp.GetChild("invocation");
      if (invocation != null)
        Console.WriteLine(invocation.Source);
    
      //Get the next node. Note that the next node might be of any kind.
      exp = exp.GetNextSibling();
    }

    If you like to use the typed version of the CodeSearchTree API, your code should look like this:

    //Load the C# class file.
    var tree = CodeSearchTree.Node.CreateTreeFromFile(@"MyClass.cs");
    
    //Get the first expression in the constructor.
    var exp = tree.Ns.Cls.Constructor.Block?.Expression?.SearchResult;
    
    while (exp != null)
    {
      //Get the invocation and type out the source code.
      var invocation = exp.Invocation?.SearchResult;
      if (invocation != null)
        Console.WriteLine(invocation.Source);
    
      //Get the next node. Note that the next node might be of any kind.
      exp = exp.GetNextSibling();
    }

    The output in both cases will be the source code for the three invocations. But let’s say that you want the function names only. The names will be found in different places for different invocations. The first invocation a memberaccess node and two id nodes. The first is called Console and the second is the name of the function. The two other invocations consists of only one id node. We must look at two places. This code will list the three function names only.

    //Load the C# class file.
    var tree = CodeSearchTree.Node.CreateTreeFromFile(@"MyClass.cs");
    
    //Get the first expression in the constructor.
    var exp = tree.GetChild("ns/cls/constructor/block/expression");
    
    while (exp != null)
    {
      //Get the invocation and type out the source code.
      var invocation = exp.GetChild("invocation");
      if (invocation != null)
      {
        var id = invocation.GetChild("memberaccess/id[1]");
        if (id != null)
          Console.WriteLine(id.Name);
        else
        {
          id = invocation.GetChild("id");
          if (id != null)
            Console.WriteLine(id.Name);
        }
      }
      //Get the next node. Note that the next node might be of any kind.
      exp = exp.GetNextSibling();
    }

    Again, if you like to use the typed version of the CodeSearchTree API, your code should look like this:

    //Load the C# class file.
    var tree = CodeSearchTree.Node.CreateTreeFromFile(@"MyClass.cs");
    
    //Get the first expression in the constructor.
    var exp = tree.Ns.Cls.Constructor.Block?.Expression?.SearchResult;
    
    while (exp != null)
    {
      //Get the invocation and type out the source code.
      var invocation = exp.Invocation?.SearchResult;
      if (invocation != null)
      {
        //No need for SearchResult property since the indexer gives the search result.
        var id = invocation?.MemberAccess?.Id[1];
        if (id != null)
          Console.WriteLine(id.Name);
        else
        {
          id = invocation?.Id?.SearchResult;
          if (id != null)
            Console.WriteLine(id.Name);
        }
      }
    
      //Get the next node. Note that the next node might be of any kind.
      exp = exp.GetNextSibling();
    }

    Output:

    WriteLine
    DoSomething
    DoSomethingElse

    Project website: https://github.com/Anders-H/CodeSearchTree

  • Gosbust 0.9.8

    Gosbust 0.9.8

    I have uploaded the latest version of Gostbust (0.8.9). The next thing to do is to build a proper tokenizer, and I thought I would publish the current version before starting that.

    Download it here.

  • Parse Transact SQL

    Parse Transact SQL

    To parse T-SQL from C#, add a reference to the following libraries (listed under Extensions, not under Framework):

    Microsoft.Data.Schema.ScriptDom
    Microsoft.Data.Schema.ScriptDom.Sql

    My examples imports the following namespaces:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Microsoft.Data.Schema.ScriptDom;
    using Microsoft.Data.Schema.ScriptDom.Sql;
    using System.IO;

    This code shows how to parse a boolean expression:

    //Parse
    var p = new TSql100Parser(false);
    IList<ParseError> err;
    var sql = "(a > 10) and (i < 10)";
    var exp = p.ParseBooleanExpression(new StringReader(sql), out err);
    
    //Errors? Empty if none.
    Console.WriteLine($"{err.Count} errors.");
    err.ToList().ForEach(x =>
       Console.WriteLine(
          $"Error \"{x.Message}\" at line {x.Line}."));
    
    //Result. Null if errors.
    if (!(exp == null))
       exp.ScriptTokenStream.ToList().ForEach(x =>
          Console.WriteLine(
             $"Token type: {x.TokenType}, source: {x.Text}"));

    If you want to parse a complete Transact SQL program, call the ParseStatementList function instead of the ParseBooleanExpression function.

    //Parse
    var p = new TSql100Parser(false);
    IList<ParseError> err;
    var sql = "SELECT EmployeeID, FirstName FROM dbo.Employees WHERE FirstName LIKE 'N%'";
    var exp = p.ParseStatementList(new StringReader(sql), out err);
    
    Console.WriteLine($"{err.Count} errors.");
    err.ToList().ForEach(x =>
       Console.WriteLine(
          $"Error \"{x.Message}\" at line {x.Line}."));
    
    //Result. Null if errors.
    if (!(exp == null))
       exp.ScriptTokenStream.ToList().ForEach(x =>
          Console.WriteLine(
             $"Token type: {x.TokenType}, source: {x.Text}"));

    This is the expected output:

    sqlparse

  • Förberäknade spritebanor på Commodore 128

    Förberäknade spritebanor på Commodore 128

    Med anledning av hur långsam Commodore 128 var på att beräkna multiplikation och division, detta klipp visar hur man kan förberäkna banor för sprites.

  • CodeSearchTree use case: Read constant values

    CodeSearchTree use case: Read constant values

    For this example, I am using Visual Studio 2015 Community edition to create a console application.  Make sure that you are using .NET Framework 4.6 or later. Let’s say that you have a .cs file with constants that looks something like this:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace MySystem
    {
      public class Settings
      {
        public const int Iterations = 56;
        public const string TargetFilename = @"C:\Output\log.txt";
        public const int Length = 128;
        public const bool Repeat = true;
      }
    }

    You want to know the names and values in this file. Let’s start from the beginning. In my console application, I want a reference to the CodeSearchTree – I accept your complements for my choice of name. Just to be on the safe side, I specify the name of the project that use the CodeSearchTree library from when I add the package in the Package Manager Console.

    Get-Project MyProject | Install-Package CodeSearchTree

    In the Main function, first declare the filename variable, then:

    //Load and parse the source file.
    var tree = CodeSearchTree.Node.CreateTreeFromFile(filename);
    //Get the first constant.
    var f = tree.GetChild("ns/cls/field");
    //Display name and value and get the next constant.
    while (!(f == null))
    {
      if (f.NodeType == CodeSearchTree.NodeType.FieldDeclarationSyntaxNode)
      {
        //Display...
        var n = f.Name;
        var x = "vardeclaration/vardeclarator/equalsvalue/literal";
        var v = f.GetChild(x).Source;
        Console.WriteLine($"Name: {f.Name}, Value: {v}");
      }
      //Get next...
      f = f.GetNextSibling();
    }

    If you are unsure about the path strings in your C# tree, try to drop your file on the test client. Happy searching!

  • AND, OR och EOR på MOS 6502

    AND, OR och EOR på MOS 6502

    Eftersom det är fredagskväll så kan jag bjuda på lite “fyllekodning”. AND, OR och EOR på MOS 6502 , mycket nöje!

  • Apple I

    Apple I

    There are an estimated six working Apple 1 machines in the world today. When sold, around 1976, the manual were more detailed on how the chips work than how to update ones Facebook status.

  • Lathund XAML: Reflektera förändringar vid data binding

    Lathund XAML: Reflektera förändringar vid data binding

    En enkel databindning (alltså databindning av en post) åstadkommer man i WPF genom att tilldela ett objekt till DataContext för containern av kontrollerna som ska bindas. Här har jag tilldelat ett namn till Grid-elementet som ligger på huvudfönstret från början, och placerat en TextBox däri som jag binder till propertyn FirstName.

    <Grid Name="theGrid">
       <TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="283,103,0,0"
       TextWrapping="Wrap" VerticalAlignment="Top" Width="120"
       Text="{Binding FirstName}" />
    </Grid>

    Givet att programmet har tillgång till klassen Employee (förra exemplet) och att fönstret har en medlemsvariabeln (emp) av den typen, så kan vi i t.ex. Initialized-eventet göra tilldelningen till DataContext.

    this.emp = new Employee() { FirstName = "Sven" };
    this.theGrid.DataContext = this.emp;

    Om vi under resans gång försöker läsa av propertyn FirstName, så kommer eventuella ändringar som användaren gjort i det bundna textfältet att ha reflekterats tillbaka till propertyn. Om vi är intresserade av att validera inmatat data behöver vi kunna registrera valideringsregler från programmet på kontrollens container. I mitt lilla exempelprojekt heter root-namnrymden WpfApplication1.

    <Grid Name="theGrid" xmlns:val="clr-namespace:WpfApplication1">

    Klassen som sköter valideringen måste ärva från klassen ValidationRule. Notera att jag kallar valideringsklassen för NameValidationRule.

    class NameValidationRule : System.Windows.Controls.ValidationRule
    {
       public int MinLength { get; set; }
       public string ErrorMessage { get; set; }
    
       public override ValidationResult Validate(object value, CultureInfo cultureInfo)
       {
          var s = (value as string == null ? "" : value as string).Trim();
          if (s.Length < this.MinLength)
             return new ValidationResult(false, this.ErrorMessage);
          return new ValidationResult(true, null);
       }
    }

    Därefter använder jag den XML-namnrymd jag tidigare registrerade (val) när jag registrerar klassen NameValidationRule.

    <TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="283,103,0,0"
       TextWrapping="Wrap" VerticalAlignment="Top" Width="120">
       <Binding Path="FirstName">
          <Binding.ValidationRules>
             <val:NameValidationRule MinLength="2" ErrorMessage="Fel!" />
          </Binding.ValidationRules>
       </Binding>
    </TextBox>

    Slutligen, om jag vill använda kod för att detektera att inmatningen inte validerar så finns det flera metoder. Den enklaste är att använda kontrollens funktion GetBindingExpression som ger ett objekt som helt enkelt har en property som heter HasError.

    var binding = textBox.GetBindingExpression(TextBox.TextProperty);
    if (binding.HasError)
       return;

    Detta kan göras t.ex. när användaren klickar OK.

  • Lathund XAML: Databinding

    Lathund XAML: Databinding

    Kontrollen ListView kan användas för att presentera data i listform, och tillsammans med GridView kan man använda den för att presentera tabulär data i listform. Följande exempel fyller ett fönster med en ListView, som har en GridView med tre kolumner.

    <ListView x:Name="listView" Margin="5,5,5,5">
       <ListView.View>
          <GridView>
             <GridViewColumn Header="First name" Width="100" />
             <GridViewColumn Header="Last name" Width="100" />
             <GridViewColumn Header="Birthdate" Width="100" />
          </GridView>
       </ListView.View>
    </ListView>

    Det enklaste sättet att lägga till data till listan är att binda dess kolumner till properties, och sedan binda listan till en samling vars objekt har dessa properties. I XAML uttrycks bindningen med DisplayMemberBinding.

    <ListView x:Name="listView" Margin="5,5,5,5">
       <ListView.View>
          <GridView>
             <GridViewColumn Header="First name"
             Width="100" DisplayMemberBinding="{Binding FirstName}" />
             <GridViewColumn Header="Last name"
             Width="100" DisplayMemberBinding="{Binding LastName}" />
             <GridViewColumn Header="Birthdate"
             Width="100" DisplayMemberBinding="{Binding BirthDate}" />
          </GridView>
       </ListView.View>
    </ListView>

    Så här skulle klassen som beskriver objektet som vi ska placera i listan som ska bindas:

    class Employee
    {
       public string FirstName { get; set; }
       public string LastName { get; set; }
       public DateTime BirthDate { get; set; }
    }

    Slutligen, denna kod utför bindningen. Den kan t.ex. köras i fönstrets Initialized-event:

    var emps = new List<Employee>();
    emps.Add(new Employee() { FirstName = "Sven", LastName = "Svensson", BirthDate = new DateTime(1960, 1, 1) });
    emps.Add(new Employee() { FirstName = "Klas", LastName = "Klasson", BirthDate = new DateTime(1970, 1, 1) });
    emps.Add(new Employee() { FirstName = "Göran", LastName = "Göransson", BirthDate = new DateTime(1980, 1, 1) });
    listView.ItemsSource = emps;

    Förändringar som sker i listan under programmets gång reflekteras först när man anropar metoden Refresh på listans Items-property.

    (listView.ItemsSource as List<Employee>).Add(
       new Employee()
       {
          FirstName = "Pierre",
          LastName = "Fridell",
          BirthDate = new DateTime(1955, 1, 1)
       });
    listView.Items.Refresh();

    Slutligen, om vi vill styra format eller design så kan vi placera en CellTemplate under en GridViewColumn, och placera värdet (eller propertynamnet i detta fall) där. Här använder jag en TextBlock-kontroll. För att centreringen av texten ska fungera, måste jag tala om att alla ListViewItem-objekt ska dras ut till sin fulla storlek.

    <ListView x:Name="listView" Margin="5,5,5,5">
       <ListView.Resources>
          <Style TargetType="ListViewItem">
             <Setter Property="HorizontalContentAlignment" Value="Stretch" />
          </Style>
       </ListView.Resources>
       <ListView.View>
          <GridView>
             <GridViewColumn Header="First name" Width="100" DisplayMemberBinding="{Binding FirstName}" />
             <GridViewColumn Header="Last name" Width="100" DisplayMemberBinding="{Binding LastName}" />
             <GridViewColumn Header="Birthdate" Width="100">
                <GridViewColumn.CellTemplate>
                   <DataTemplate>
                      <TextBlock Text="{Binding BirthDate, StringFormat=\{0:yyyy\}}" TextAlignment="Center"></TextBlock>
                   </DataTemplate>
                </GridViewColumn.CellTemplate>
             </GridViewColumn>
          </GridView>
       </ListView.View>
    </ListView>
  • Lathund XAML: Positionering

    Lathund XAML: Positionering

    För att placera ut en knapp på en absolut position, sätt HorizontalAlignment till “Left” och VerticalAlignment till “Top”. Därefter, använd de första två talen i Margin för X- och Y-position. Exempel:

    <Button x:Name="button" Content="Button"
    HorizontalAlignment="Left" Margin="105,101,0,0"
    VerticalAlignment="Top" Width="75"/>

    Om man istället vill fästa knappen (eller vad det nu må vara för kontroll man jobbar med) över hela det tillgängliga området så används Margin för att ange avstånd från kanten. Här fyller knappen hela området, men lämnar tre pixlars marginal på alla fyra sidor.

    <Button x:Name="button" Content="Button" Margin="3,3,3,3" />

    Det innebär att man ganska enkelt kan bygga ett traditionellt användargränssnitt med en meny högst upp och ett statusfält längst ner enligt följande:

    <Menu x:Name="menu" Height="28" Margin="0" VerticalAlignment="Top" />
    <StatusBar Height="28" Margin="0" VerticalAlignment="Bottom" />

    Om jag nu vill använda det resterande området för t.ex. en knapp, så måste jag öka den övre (andra värdet) och den undre (det fjärde värdet) så att inte knappen skymmer menyn eller statusfältet.

    <Menu x:Name="menu" Height="28" Margin="0" VerticalAlignment="Top" />
    <StatusBar Height="28" Margin="0" VerticalAlignment="Bottom" />
    <Button x:Name="button" Content="Button" Margin="3,31,3,31" />

    Alternativt kan man använda sig av en contaner som hanterar dockningen, en DockPanel. Här anger jag att menyn ska vara fäst högst upp och att statusfältet ska vara längst ner, genom att hänvisa till den. För att knappen ska ta upp det resterande området, räcker det att jag placerar den sist i listan, och sätter attributet LastChildFill till “True”.

    <DockPanel Margin="0,0,0,0" LastChildFill="True">
       <Menu x:Name="menu" DockPanel.Dock="Top" Height="28" />
       <StatusBar DockPanel.Dock="Bottom" Height="29" />
       <Button x:Name="button" Content="Button" Margin="3,3,3,3" />
    </DockPanel>

    En uppenbar fördel med dockningspanelen är att man inte behöver specificera för knappen i mitten (eller vad man nu har för slags kontroll placerad där) hur stor den ovanliggande menyraden är. Och behöver man inte specificera det, så behöver man inte veta det, utan kan låta den ta upp så mycket plats som sitt innehåll, menyerna, kräver.

    <DockPanel Margin="0,0,0,0" LastChildFill="True">
       <Menu x:Name="menu" DockPanel.Dock="Top">
          <MenuItem Header="File" Margin="4,4,4,4">
             <MenuItem Header="Quit" />
          </MenuItem>
       </Menu>
       <StatusBar DockPanel.Dock="Bottom" Height="29" />
       <Button x:Name="button" Content="Button" Margin="3,3,3,3" />
    </DockPanel>
  • Grovhet

    Grovhet

    För många står oregelbundenhet i motsats till regelbundenhet, men i den matematiska världen står regelbundenhet i motsats till grovhet. Tänk dig att du ska mäta omkretsen på denna figur, en kochkurva:

    koch1

    Ju finare passare du använder för att mäta omkretsen, desto längre är omkretsen.

    passare

    En grov inställning på passaren kommer att ge mätvärden vars summa är mycket lägre, vilket innebär att den grova inställningen på mätinstrumentet ger ett resultat som beskriver en mindre grov figur.

    koch2

    En finare inställning på passaren kommer att ge mätvärden vars summa är mycket högre, vilket innebär att den fina inställningen på mätinstrumentet ger ett resultat som beskriver en grövre figur.

    koch3

    Denna egenskap gäller inte bara för kochkurvan utan även för en kustlinje eller en bergssiluett. Se gärna Benoît Mandelbrots föreläsning på TED. Mandelbrot är en fransk matematiker som givit namn åt mandelbrotfraktalen.

  • Snittvärde

    Snittvärde

    Jag fick en fråga om att räkna ut snittvärden. Det finns olika sätt att lösa det på, där det enklaste kanske är att använda Excel för att göra ett enkelt datablad. Jag tänkte visa hur man kan göra i C# för att få en start till ett registerprogram. För detta använder jag Visual Studio 2015 Community Edition som kan laddas hem gratis här. Välj att skapa ett nytt projekt.

    bild1

    Jag kör mot .NET Framework 4.5.2 (1) och har valt kategorin Windows under C# (2) och projekttypen Console Application (3). Se till att välja ett lämpligt projektnamn och en lämplig plats att spara projektet på (4). Sen är det bara att börja skriva.

    Här har vi den grundläggande principen:

             //Några flyttal att beräkna snitt på.
             double[] tal = { 3.0, 4.0, 1.5, 2.0 };
    
             //Summera talen.
             var summa = 0.0;
             for (int i = 0; i < tal.Length; i++)
                summa += tal[i];
    
             //Dividera med antalet.
             var snitt = summa / tal.Length;
    
             //Presentera resultatet.
             Console.WriteLine(snitt);
    
             //Pausa tills användaren trycker Enter.
             Console.ReadLine();

    Vi borde nu se att snittet av 3.0, 4.0, 1.5 och 2.0 är 2.625.

    Eftersom vi har tillgång till namnrymden System.Linq så kan vi använda en inbyggd funktion för att räkna ut snittet. Detta ger samma resultat:

             //Några flyttal att beräkna snitt på.
             double[] tal = { 3.0, 4.0, 1.5, 2.0 };
    
             //Be om snittet.
             var snitt = tal.Average();
    
             //Presentera resultatet.
             Console.WriteLine(snitt);
    
             //Pausa tills användaren trycker Enter.
             Console.ReadLine();

    Detta kan vi applicera om vi bygger en struktur som innehåller ett flyttal. Följande kod beskriver ett musikalbum med låtar. Låtarna har ett betyg mellan 1 och 10, och deras snitt ger oss ett betyg på musikalbumet.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Snitt
    {
       class Program
       {
          static void Main(string[] args)
          {
             //Konstruera ett album.
             var a = new Album() { AlbumName = "A Night at the Opera",
                                   ArtistName = "Queen" };
             //Lägg på låtar.
             a.Songs.Add(new Song() { TrackNumber = 1,
                                      TrackName = "Death on Two Legs",
                                      Rating = 7.0 });
             a.Songs.Add(new Song() { TrackNumber = 2,
                                      TrackName = "Lazing on a Sunday Afternoon",
                                      Rating = 4.0 });
             a.Songs.Add(new Song() { TrackNumber = 3,
                                      TrackName = "I'm in Love with My Car",
                                      Rating = 6.0 });
    
             //Hämta låtarnas betyg.
             var ratings = (from s in a.Songs
                           select s.Rating).ToArray();
    
             //Hämta ut låtarnas snittvärde och avrunda.
             var snitt = Math.Round(ratings.Average(), 2);
    
             //Presentera resultatet.
             Console.WriteLine(snitt);
    
             //Pausa tills användaren trycker Enter.
             Console.ReadLine();
          }
       }
    
       //Definiera en låt.
       public class Song
       {
          public int TrackNumber { get; set; }
          public string TrackName { get; set; }
          public double Rating { get; set; }
       }
    
       //Definiera ett musikalbum
       public class Album
       {
          public string ArtistName { get; set; }
          public string AlbumName { get; set; }
          public List<Song> Songs { get; set; } = new List<Song>();
       }
    }
    

    Detta ger oss svaret 5.67.

    Klasserna Song och Album kan användas som en del av en Windows-applikation med ett grafiskt användargränssnitt. Projekttypen skulle kunna vara en Windows Forms Application. Att sammanställa en sådan applikation är ett ganska stort och tidskrävande projekt. En album-lista ska skapas, serialisering och deserialisering ska implementeras och det grafiska användargränssnittet ska byggas.

  • Delta

    Delta

    This is the result of today’s recording sessions. Delta, originally written and performed by Rob Hubbard, 1987.

  • Stora tal

    Stora tal

    Det finns ett mycket roligt klipp med karaktären Ali G där han ställer sig frågan om en dator någonsin kan räkna ut ett väldigt stort godtyckligt tal. Han får svaret att alla räkneövningar han kan nämna, kan räknas ut av en modern dator. Din miniräknare kommer förmodligen slå i taket när talen blir för stora, men en vanlig PC med Windows kan ge dig precis vilket svar som helst. Det krävs lite trixande under skalet för att få detta att fungera.

    Heltal representeras normalt av en bit-struktur, alltså ett antal ettor och nollor. Ett 32-bitars heltal är ett heltal som beskrivs av 32 bitar (4 bytes). Bitrepresentationen är liknar binärrepresentationen av ett tal, vilket innebär att både bitrepresentationen av talet 8 och binärrepresentationen av talet 8 är 1000. Ju större tal man vill beskriva, desto fler bitar behöver man ha, och förr eller senare slår man i taket.

    Men det finns en struktur i Windows som heter BigInteger som inte har någon begränsning när det kommer till heltalsrepresentationer och heltalsberäkningar. Det beror på att talen internt lagras som en teckensekvens istället för som ett bitmönster, där varje tecken i sin tur representeras av ett begränsat bitmönster. Då blir det hela en fråga om hur många tecken som får plats i minnet, vilket är väldigt många. För att få se vad BigInteger-strukturen kan göra, kan man skriva in en enkel kod i PowerShell som dubblerar ett tal ett antal gånger. Om jag börjar med talet 1 och dubblar det så får jag 2. Dubblar jag det så får jag 4, 8, 16, 32, 64, 128, 256, och så vidare. Ganska snart blir talet väldigt stort. Redan efter blott 100 dubbleringar landar man på talet 2 535 301 200 456 458 802 993 406 410 752 vars binära representation skulle bli väldigt stor. Men i en BigInteger-struktur är detta bara att betrakta som 31 tecken som avkodas till värden i turordning när beräkningar ska göras.

    Så utrustade med BigInteger-strukturen kan vi se vad som händer när vi dubblerar ett tal 500 gånger. För att göra detta, skriv:

    $x = New-Object System.Numerics.BigInteger 1; for ($i = 0; $i -le 500; $i++) { $x *= 2; Write-Output $x }

    PowerShell levererar svaret på ett ögonblick. Talet 1 dubblat 500 gånger ger oss talet 6 546 781 215 792 283 740 026 379 393 655 198 304 433 284 092 086 129 578 966 582 736 192 267 592 809 349 109 766 540 184 651 808 314 301 773 368 255 120 142 018 434 513 091 770 786 106 657 055 178 752, vilket är ett så stort tal att det nästan är omöjligt att läsa ut och förstå. För att underlätta kan man använda scientific notation. Istället för att skriva ut föregående tal i sin fulla längd, kan man skriva:

    6,55 * 10150

    Eller:

    6,55E150

    Båda varianterna säger oss att talet börjar på 6 och innehåller 150 siffror till därefter, varav de första nästkommande är 55 (avrundat). Det totala antalet siffror i talet är alltså antalet siffror till vänster om decimalavgränsaren plus talet till höger om E. 151 stycken.

    Så hur står sig en etta dubblerad femhundra gånger mot andra stora tal?

    Vårt observerbara Universum uppskattas innehålla 1,7 * 1011 (eller 1,7E11) galaxer. Uppskattningsvis finns 1024 (1E24) stjärnor i Universum.

    Båda dessa tal är alltså relativt små i jämförelse med den etta vi dubblade femhundra gånger. Hur är det med antalet atomer i vårt observerbara Universum? Faktum är att även det är ett relativt litet tal i detta sammanhang, nämligen 4 * 1080 (4E80), alltså en fyra följt av åttio nollor. 400 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000.

    Så låt oss dubbla vår etta femtusen gånger. Även det räknas fram kvickt av PowerShell. Svaret är 282 493 406 427 885 207 367 041 933 403 229 466 733 779 235 036 908 223 362 737 617 171 423 633 968 541 502 511 617 825 263 342 305 274 671 206 416 862 732 165 528 407 676 139 958 676 671 942 371 453 279 846 862 103 555 703 730 798 023 755 999 290 263 414 138 746 996 425 262 647 505 106 222 430 745 688 071 901 801 071 909 721 466 836 906 811 151 133 473 603 131 174 810 929 399 280 998 101 699 398 944 715 801 811 235 142 753 236 456 432 868 426 363 041 983 113 354 252 997 303 564 408 348 123 661 878 478 353 722 682 766 588 036 480 451 677 385 451 192 294 010 288 486 562 150 551 258 990 678 187 626 397 933 471 267 212 659 382 047 684 908 251 671 777 313 746 267 962 574 481 960 017 676 147 336 443 608 528 865 821 788 061 578 040 438 881 156 396 976 534 679 536 477 744 559 804 314 840 614 495 141 020 847 691 737 745 193 471 783 611 637 455 592 871 506 037 036 173 282 712 025 702 605 093 453 646 018 500 436 656 036 503 814 680 490 899 726 366 531 275 975 724 397 022 092 725 970 923 899 174 562 238 279 814 456 008 771 885 761 907 917 633 109 135 250 592 173 833 771 549 657 868 899 882 724 833 177 350 653 880 665 122 207 329 113 965 244 413 668 948 439 622 163 744 809 859 006 963 982 753 480 759 651 997 582 823 759 605 435 167 770 997 150 230 598 943 486 938 482 234 140 460 796 206 757 230 465 587 420 581 985 312 889 685 791 023 660 711 466 304 041 608 315 840 180 083 623 903 760 913 411 030 936 698 892 365 463 484 655 371 978 555 215 241 419 051 756 637 532 976 736 697 930 030 949 995 728 239 530 882 866 713 856 024 688 223 531 470 672 787 115 758 429 874 008 695 136 417 331 917 435 528 118 587 185 775 028 585 687 114 094 178 329 752 966 233 231 383 772 407 625 995 111 380 343 784 339 467 510 448 938 064 950 157 595 661 802 643 159 880 254 674 421 388 754 566 879 844 560 548 121 596 469 573 480 869 786 916 240 396 682 202 067 625 013 440 093 219 782 321 400 568 004 201 960 905 928 079 577 408 670 605 238 675 195 724 104 384 560 742 962 264 328 294 373 028 338 181 834 383 818 752.

    I scientific notation skrivs detta tal 2,82 * 101505 eller 2,82E1505. Med tanke på att vi ännu inte närmat oss någon bortre gräns, så kan man lugnt konstatera att den som äger en vanlig PC förvaltar en svindlande räkningskapacitet.

  • Gosbust 0.9.2

    Gosbust 0.9.2

    Version 0.9.2 av Gosbust finns nu att ladda hem. Tillåt mig presentera några exempel på vad aktuell version kan göra.

    Det stora tillskottet handlar om stödet för arrayer. Kommandot ARRY tar ett namn (A-Z, A#-Z#, A$-Z$ eller A?-Z?) samt önskat antal element. 5 ger fem element med index 0-4.

    ARRY A 5

    Detta kolliderar inte med en eventuell enskild buffer som heter A. R$ och R$[3] lagras i olika minnesstrukturer. För att nå individuella element används hakparenteser. Detta skapar ett slumptal i arrayen A’s första element:

    RAND A[0]

    Om jag läser av element 0 borde jag se ett heltal, vilket som helst men troligen inte 0.

    <<A[0]

    Element 1 borde däremot fortfarande ha värdet 0, eftersom ingen tilldelning gjorts än.

    <<A[1]

    Om jag skulle önska ha ett slumptal i samtliga element i arrayen A, kan jag ersätta indexet med tre punkter:

    RAND A[...]

    Nu kan jag läsa av vilket element som helst (0-4) och få ett värde. Om jag skulle önska se samtliga element så kan jag skriva följande:

    <<A[...]

    Modulus kan inte appliceras på samtliga element i en array, så skulle jag önska att alla slumptal ska ligga mellan 0 och 9 kan jag skriva följande:

    0>>I Mod: A[I]%10>>A[I] I++ I<10>>R? GOTO R? Mod

    Om du använvänder konsollen så skulle bubbelsortering av 10 tal mellan 0 och 99 skulle se ut så här:

    10 ARRY A 10 RAND A[...]
    20 0>>I Mod: A[I]%100>>A[I] I++ I<10>>R? GOTO R? Mod
    30 <<"Ej sorterat:" <<A[...]
    40 0>>I 0>>J
    50 Repeat: A[I]>A[J]>>S? GOSB S? Swap
    60 I++ I>9>>I? GOSB I? NextOuter GOTO 1 Repeat
    70 ShowResult: <<"Sorterat:" <<A[...]
    80 QUIT 0
    90 (Swap A[I]<>A[J] )
    100 (NextOuter 0>>I J++ J>9>>J? GOTO J? ShowResult )

    Notera att QUIT 0 lika gärna kan ersättas med RTRN. Om callstacken är tom har dessa två kommandon samma betydelse. Men om man skulle vilja avsluta med en felkod så kan QUIT n användas.

    (Om du läser in en fil istället för att använda konsollen behövs inte radnumren.)

    Mycket nöje!

  • Konsollen i Gosbust 0.9.1

    Konsollen i Gosbust 0.9.1

    En liten förhandstitt på vad som är på gång i Gosbust.

  • Lite roliga skivor

    Lite roliga skivor

    Medan jag städade och sorterade skivor så hittade jag några roliga som jag ville visa upp för världen. Mycket nöje!

  • Två nya podcastavsnitt

    Två nya podcastavsnitt

    I avsnitt 130 av Radio Houdi pratar vi bl.a. om nationaldagsfirande, gudomlig healing, rasism och Queen. Lyssna här!

    Och i avsnitt 57 av Commoflage gräver vi i vanlig ordning ner oss i den underbara C64-musiken. Lyssna här!

  • Nytt kodexempel – Gissa ett tal

    Nytt kodexempel – Gissa ett tal

    Gosbust finns i ny version (0.9) och med ett nytt exempelprogram. Följande fungerar med version 0.9 eller senare av Gosbust som finns att ladda ner här.

    0>>X
    /* Trolla fram ett slumptal mellan 1 och 100. */
    RAND C C%100>>C C++
    
    Restart:
    /* Fråga användaren efter ett tal mellan 1 och 100. */
    <<"Skriv ett tal mellan 1 och 100."
    X++ ->A$ A$>>A>>I? !I? GOTO I? Restart
    
    /* Sätt flaggor som beskriver om gissningen
    var rätt, för liten eller för stor. */
    A>C>>S? A<C>>L? A=C>>C?
    
    GOSB S? TooHigh
    GOSB L? TooLow
    GOTO C? Correct
    GOTO 1 Restart
    
    (TooHigh <<"För stort!" )
    (TooLow <<"För litet!" )
    Correct: <<"Rätt!" <<X

    guess

    Om du vill fuska, komplettera med <<C på raden före Restart: så ser du det rätta svaret på skärmen.

  • Anropa alla klienter från ASP.NET

    Anropa alla klienter från ASP.NET

    SignalR är ett ramverk som låter webbklienter prata direkt med webbservern via sockets, och som låter webbservern prata direkt med klienterna på samma sätt. Tack vare SignalR kan du göra funktionsanrop direkt till de användare som för tillfället råkar surfa på din hemsida. SignalR bygger ovanpå ramverket OWIN för ASP.NET.

    I detta exempel arbetar jag i Visual Studio 2013 med ett projekt av typen ASP.NET MVC, så jag utgår alltså ifrån Microsofts exempelprogram som levereras som mall i Visual Studio 2013.

    signalr1

    För att komma igång ska vi:

    • Installera paketet SignalR i projektet.
    • Läsa in JavaScript-filen för SignalR.
    • Koppla SignalR-hubbarna till OWIN.
    • Skapa en hubb.

    Därefter är det fritt fram att börja kommunicera!

    För att installera SignalR, skriv följade i Package Manager Console:

    Install-Package Microsoft.AspNet.SignalR

    Eftersom SignalR ligger i version 2.2 när jag skriver detta, så kompletteras mitt program med filerna jquery.signalR-2.2.0.js och jquery.signalR-2.2.0.min.js i mappen Scripts, men tänk på att versionsnumret kan variera om du vill testa detta.

    Därefter ska det minimerade scriptet inkluderas i de vyer som SignalR ska fungera på. Jag hade tänkt mig att kunna använda den överallt, så jag lägger in scriptet i Views/Shared/_Layout.cshtml. Vi ska även läsa in det autogenererade scriptet signalr/hubs.

    <script type="text/javascript"
       src="~/scripts/jquery.signalR-2.2.0.min.js"></script>

    Jag föredrar att flytta upp de script som redan ligger på sidan till dokumentets head-sektion, så att den ser ut så här:

    <head>
       <meta charset="utf-8" />
       <meta name="viewport" content="width=device-width, initial-scale=1.0">
       <title>@ViewBag.Title - My ASP.NET Application</title>
       @Styles.Render("~/Content/css")
       @Scripts.Render("~/bundles/modernizr")
       @Scripts.Render("~/bundles/jquery")
       @Scripts.Render("~/bundles/bootstrap")
       <script type="text/javascript"
         src="~/scripts/jquery.signalR-2.2.0.min.js"></script>
       
       @RenderSection("scripts", required: false)
    </head>

    I nästa steg ska vi koppla SignalR-hubbarna till OWIN. Hubbarna, alltså kommunikationsnaven, har ett client-attribut som används för att prata med klienten och ett server-attribut som används för att prata med servern, och identifieras av adressen “/signalr” om vi inte sagt något annat. För att göra kopplingen behöver vi ha en OWIN Startup class. Högerklicka på ditt projektnamn i Solution Explorer, välj att lägga till en OWIN Startup class. Kalla den för OwinStartup. Det ger oss följande kod:

    using System;
    using System.Threading.Tasks;
    using Microsoft.Owin;
    using Owin;
    
    [assembly: OwinStartup(typeof(WebApplication3.OwinStartup))]
    
    namespace WebApplication3
    {
       public class Startup
       {
          public void Configuration(IAppBuilder app)
          {
             // For more information on...
          }
       }
    }

    Ersätt kommentaren i funktionen Configuration med följande anrop:

    app.MapSignalR();

    Nu har vi gjort de förberedelser som behövs för att kommunicera. Till att börja med tänkte jag att klienten ska få skicka ett meddelande till servern när användaren klickar på en knapp.

    Den sista förberedelsen innan vi kan börja är att skapa en hubb. En hubb är en klass som ärver från Microsoft.AspNet.SignalR.Hub. Kontrollera att du har mappen App_Code, annars kan den skapas från menyn som visas när man högerklickar på projektnamnet. Sedan, högerklicka på mappen App_Code och lägg till en klass av typen SignalR Hub Class (v2). Ge den namnet MinTestHub. Det går också att lägga till en vanlig klass och läsa in namnrymden Microsoft.AspNet.SignalR, och sedan ärva från Hub. Här i kan vi skriva de funktioner som vi vill exponera för klienterna, i mitt fall SkickaMeddelande.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using Microsoft.AspNet.SignalR;
    
    namespace WebApplication3.App_Code
    {
       public class MinTestHub : Hub
       {
          public void SkickaMeddelande(string meddelande)
          {
             this.Clients.All.servernPratar(
                "Ett meddelande: " + meddelande);
          }
       }
    }

    Visual Studios texteditor är inte speciellt hjälpsam eftersom koden är dynamisk och okänd för Visual Studio, men detta betyder att när en klient får för sig att anropa servern med SignalR, så svarar servern genom att anropa alla klienter.

    Tänk på att hubben exponeras som ett attribut till ett JavaScript-objekt, connection, på klientsidan med första bokstaven som gemen, vilket betyder att denna hubb kommer att heta minTestHub på klienten – inte MinTestHub.

    För att summera klassen MinTestHub: Funktionen SkickaMeddelande exponeras för klienterna och låter dem prata med servern. Servern svarar genom att anropa JavaScript-funktionen servernPratar hos alla klienterna.

    Det som återstår nu är klientkoden. Jag skriver den i applikationens startsida, Views/Home/Index.cshtml.

    På en lämplig plats vill jag ha en knapp kallad SendFromClient och därefter öppnar jag ett script-block.

    <input type="button" id="SendFromClient" value="Send from client" />
    <script type="text/javascript">
    
    </script>

    All kod skriver jag i jQuery:s uppstartevent. Det innebär att hela koden är inkapslad av $(function () { och });. I script-blocket gör jag två saker. Först ansluter jag mig till hubben och tillhandahåller definitionen av servernPratar, alltså skriver vad som ska hända när ett anrop från servern kommer in.

    var testhub = $.connection.minTestHub;
    testhub.client.servernPratar = function (meddelande) {
       alert(meddelande);
    };

    Därefter, när hubben är startad, så talar jag om vad som ska hända när knappen blir klickad på. Då vill jag anropa funktionen skickaMeddelande på servern.

    $.connection.hub.start().done(function () {
       $("#SendFromClient").click(function () {
          testhub.server.skickaMeddelande("Hej från klienten!");
       });
    });

    Hela klientkoden som jag infogar ser alltså ut så här:

    <input type="button" id="SendFromClient" value="Send from client" />
    
    <script type="text/javascript">
       $(function () {
    
          var testhub = $.connection.minTestHub;
          testhub.client.servernPratar = function (meddelande) {
             alert(meddelande);
          };
    
          $.connection.hub.start().done(function () {
             $("#SendFromClient").click(function () {
                testhub.server.skickaMeddelande("Hej från klienten!");
             });
          });
       });
    </script>

    Om du testar att öppna flera webbläsare och surfar till din applikation, kommer du att se att när du klickar på knappen på en av dem, så visar sig meddelanderutan i alla. (För bäst effekt, använd olika webbläsare med tanke på att en meddelanderuta kan blockera andra meddelanderutor.)

    Hubben i din server blir hårt belastad kan meddelanden skickas i okontrollerad ordning. Det beror på att anropen till klienten är asynkrona, men detta kan avhjälpas med async och await.

  • 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
    {
    }
  • Schemalagda meddelanden i Akka .NET

    Schemalagda meddelanden i Akka .NET

    Denna gång tänker jag använda en Windows Forms Application för att visa schemaläggaren i Akka .NET. Som vanligt, för att komma åt Akka .NET i ditt projekt, växla till fönstret Package Manager Console och skriv Install-Package Akka. Om du inte ser Package Manager Console, klicka på rullgardinsmenyn Tools, välj Library Package Manager och Package Manager Console.

    Programmet består av följande meddelandeklass:

    public class TimedMessage
    {
       public string Message { get; private set; }
       public TimedMessage(string message)
       {
          this.Message = message;
       }
    }

    …och följande aktör:

    public class Listen : ReceiveActor
    {
       public Listen()
       {
          this.Receive<TimedMessage>(x =>
          {
             System.Diagnostics.Debug.WriteLine(x.Message);
          });
       }
    }

    Huvudfönstret har läst in namnrymden Akka.Actor för att några extension methods ska vara tillgängliga…

    using Akka.Actor;

    …och innehåller en knapp som heter button1. I formulärets Load-event skapas aktörssystemet och aktören och under knappen så skickar jag ett meddelande (TimedMessage) till aktören.

    public partial class Form1 : Form
    {
       private ActorSystem actorSystem;
       private IActorRef listen;
    
       public Form1()
       {
          InitializeComponent();
       }
    
       private void button1_Click(object sender, EventArgs e)
       {
          this.listen.Tell(new TimedMessage("Helloooo!"));
       }
    
       private void Form1_Load(object sender, EventArgs e)
       {
          this.actorSystem = ActorSystem.Create("ActorSystem");
          this.listen = this.actorSystem.ActorOf(
             Props.Create(typeof(Listen)));
       }
    }

    Om du testar detta lilla exempel så märker du att meddelandet går iväg till aktören i samma ögonblick som du klickar på knappen. Men istället för att anropa aktörens Tell-funktion i Click-eventet, så kan vi använda aktörssystemets Scheduler-property. Den har ett antal funktioner för schemaläggning och upprepning. Följande förändring gör att meddelandet går iväg först tre sekunder efter att man klickar på knappen.

    private void button1_Click(object sender, EventArgs e)
    {
       this.actorSystem.Scheduler.ScheduleTellOnce(
          TimeSpan.FromSeconds(3), this.listen,
          new TimedMessage("Helloooo!"), ActorRefs.Nobody);
    }

    Funktionen ScheduleTellOnce schemalägger ett meddelande istället för att skicka iväg det direkt. Det är det första argumentet som bestämmer när meddelandet ska gå iväg. Uttrycket TimeSpan.FromSeconds(3) säger att meddelandet ska gå iväg efter tre sekunder, oavsett vad klockan är. Schemaläggaren förväntar sig att man anger relativa tider, så om jag vill att meddelandet ska gå iväg ett bestämt klockslag, t.ex. den 19/5 kl. 13:30, kan jag istället skriva new DateTime(2015, 5, 19, 13, 30, 0).Subtract(DateTime.Now).

    Det andra argumentet är aktören som ta emot meddelandet. I mitt exempel finns endast en aktör, så den skickar jag in. Det tredje argumentet är meddelandet som ska skickas. Slutligen, det fjärde argumentet är avsändaren. Om det är en aktör som skickar meddelandet, skriver man normalt this.Self, men det går bra att skicka in en annan aktör här.

    I mitt fall när det inte finns någon aktör som avsändare (det är ju formuläret som skickar) så kan det vara frestande att skicka in null, men i dagsläget fungerar inte det. Klassen ActorRefs innehåller konstanter som kan användas som substitut för en avsändare, och Nobody betyder just ingen. (Detta kan komma att ändras.)

    Men tänk dig att du vill bli informerad när något inte händer. Från första klicket vill du veta om användaren fortsätter att klicka var tredje sekund, eller om användaren har blivit passiv. Det kan vi lösa på följande vis:

    1. När första klicket inträffar, säger vi att vi vill ha ett meddelande om tre sekunder.

    2. När knappen blir klickad på nästa gång, inom tre sekunder, så avbryter vi beställningen, och gör om den på nytt.

    3. Skulle användaren inte klicka inom tre sekunder så går meddelandet iväg eftersom det inte hunnit avbrytas. Således vet vi att användaren har blivit passiv.

    Detta kräver minimala förändringar. Först vill vi ha ett kvitto på beställningen. För detta använder jag en medlemsvariabel av typen Akka.Actor.ICancelable.

    private ICancelable idle_detector = null;

    Funktionen ScheduleTellOnce ger ingen retur, men ScheduleTellOnceCancelable ger ett svar i ICancelable-format som jag kan lagra i min nya medlemsvariabel. När användaren klickar, kan jag kolla om det finns en liggande beställning, och i så fall kan jag avbryta den – eftersom användaren klickat så är han ju inte inaktiv.

    private void button1_Click(object sender, EventArgs e)
    {
       if (!(this.idle_detector == null))
          this.idle_detector.Cancel();
       this.idle_detector =
          this.actorSystem.Scheduler.ScheduleTellOnceCancelable(
          TimeSpan.FromSeconds(3), this.listen,
          new TimedMessage("Idle!!!"), ActorRefs.Nobody);
    }

    För att testa programmet, starta och klicka lite då och då på knappen. För varje gång tre sekunder får passera utan något klick så går meddelandet iväg till aktören.

  • Excel-SM avgjort

    Excel-SM avgjort

    Då var första Excel-SM avgjort hos Microsoft i Akalla.

    11329978_1456121171351889_6754263162152353179_n

    Vinnaren Patrik Von Knorring (nedan) körde faktiskt Excel på svenska, lite som extra utmaning.

    10411080_1456128681351138_2790449441832854668_n

  • Akka .NET Remote duplex

    Akka .NET Remote duplex

    Jag har tidigare visat hur man skickar enkla meddelanden med Akka .NET Remote. Exemplet bestod av tre projekt, en server, ett datalager och en klient. Här tänkte jag visa hur man använder Akka .NET Remote för att både skicka och ta emot meddelanden. I detta exempel återanvänder jag servern BioServer och datalagret BioData från det förra exemplet.

    vs

    Tanken är alltså att programmet är ett enkelt bokningssystem för en liten biograf med tre sittplatser. Denna gång tänker jag utöka datalagret med lite nya meddelanden och servern med kapaciteten att svara på inkommande anrop. Dessutom bygger jag en ny grafisk klient.

    Datalagret
    I den förra versionen hade vi bara ett enda definierat meddelande i datalagret, klassen Bokningsmeddelande. Detta meddelande finns kvar, men nu kompletterar vi med Bokningssvar som ska skickas tillbaka till klienten som skickar in ett Bokningsmeddelande.

    public class Bokningsmeddelande
    {
       public int StolNr { get; private set; }
       public int BokadAv { get; private set; }
       public Bokningsmeddelande(int stolNr, int bokadAv)
       {
          this.StolNr = stolNr;
          this.BokadAv = bokadAv;
       }
    
       public override string ToString()
       {
          return string.Format(
             "Bokning från {0} på stol {1}.",
             this.BokadAv, this.StolNr);
       }
    }
    
    public class Bokninssvar
    {
       public bool BokningAccepterad { get; private set; }
       public int StolNr { get; private set; }
       public Bokninssvar(bool bokningAccepterad, int stolNr)
       {
          this.BokningAccepterad = bokningAccepterad;
          this.StolNr = stolNr;
       }
    }

    Servern
    Låt oss börja med att implementera stödet för Bokningsmeddelande och Bokningssvar.

    Klassen Stollista, alltså den bokningsbara resursen, har jag valt att utöka med en funktion som kan uppge vem som bokat en plats.

    public int BokadAv(int stolNr)
    {
       var stol = this.Find(m => m.StolNr == stolNr);
       if (stol == null)
          return 0;
       else
          return stol.BokadAv;
    }

    Det innebär att hela klassen nu ser ut så här:

    class Stollista : List
    {
       public override string ToString()
       {
          var ret = new System.Text.StringBuilder();
          foreach (var s in this)
             ret.AppendLine(s.ToString());
          return ret.ToString();
       }
    
       public bool Boka(int stolNr, int bokadAv)
       {
          var stol = this.Find(m => m.StolNr == stolNr);
          if (stol == null)
             return false;
          stol.BokadAv = bokadAv;
          return true;
       }
    
       public int BokadAv(int stolNr)
       {
          var stol = this.Find(m => m.StolNr == stolNr);
          if (stol == null)
             return 0;
          else
             return stol.BokadAv;
       }
    }

    Dessutom har jag modifierat aktören Bokningsaktor så att den kontrollerar huruvida den önskade stolen är bokningsbar, och meddelar klienten om bokningen accepteras. Notera hur aktören använder propertyn Sender för att kommunicera med avsändaren.

    class Bokningsaktor : Akka.Actor.ReceiveActor
    {
    
       public Bokningsaktor()
       {
          this.Receive(b => {
             Console.WriteLine("Ny bokning!");
             if (Program.stolar.BokadAv(b.StolNr) == 0 ||
                Program.stolar.BokadAv(b.StolNr) == b.BokadAv)
             {
                Program.stolar.Boka(b.StolNr, b.BokadAv);
                this.Sender.Tell(new BioData.Bokninssvar(true, b.StolNr));
             }
             else
                this.Sender.Tell(new BioData.Bokninssvar(false, b.StolNr));
             Console.WriteLine(Program.stolar.ToString());
          });
       }
    }

    Den nya klienten
    I detta fall vill jag ha en grafisk klient för att kunna representera svaren från servern på ett någorlunda snyggt sätt. Jag har skapat en applikation av typen Windows Forms Application, satt referenser till Akka .NET och Akka .NET Remote med hjälp av NuGet samt satt en referens till datalagret BioData. Dessutom vill jag läsa in namnrymden Akka.Actor för att komma åt en extension method – en överlagring av funktionen ActorOf.

    using Akka.Actor;

    Vidare väljer jag att hålla en referens till mitt aktörssystem i huvudformuläret.

    private Akka.Actor.ActorSystem system;

    På formuläret har jag placerat ut ett textfält där användaren förväntas infoga sitt användar-ID (ett heltal) samt tre knappar som representerar de tre bokningsbara stolarna. Man förväntas ange sitt ID i textfältet innan man klickar på någon knapp.

    form

    I formulärets Load-event initierar jag aktörssystemet på samma sätt som tidigare, men sparar nu referensen i medlemsvariabeln som jag deklarerade ovan.

    private void Form1_Load(object sender, EventArgs e)
    {
       var config = Akka.Configuration.ConfigurationFactory.ParseString(@"
    akka {
        actor {
            provider = ""Akka.Remote.RemoteActorRefProvider, Akka.Remote""
        }
        remote {
            helios.tcp {
                port = 0
                hostname = localhost
            }
        }
    }
    ");
       this.system = ActorSystem.Create("Bokningsklient", config);         
    }

    Eventet FormClosed får sköta uppstädningen.

    private void Form1_FormClosed(object sender, FormClosedEventArgs e)
    {
       this.system.Shutdown();
       this.system.Dispose();
    }

    Sedan ska Click-eventet under varje knapp inleda bokningsförfarandet.

    private void btnStol1_Click(object sender, EventArgs e)
    {
       this.Boka(1);
    }
    
    private void btnStol2_Click(object sender, EventArgs e)
    {
       this.Boka(2);
    }
    
    private void btnStol3_Click(object sender, EventArgs e)
    {
       this.Boka(3);
    }

    För att aktören ska kunna komma åt knapparna direkt, har jag även en enkel funktion som tar ett stolsnummer och ger en referens till en knapp.

    public Button Stolreferens(int StolNr)
    {
       Button[] ret = { this.btnStol1, this.btnStol2, this.btnStol3 };
       return ret[StolNr - 1];
    }

    Sista funktionen i formuläret är den som utför själva bokningen. Notera att vi skickar med en referens till en lokal aktör som ska kunna ta emot svaret från servern.

    private void Boka(int StolNr)
    {
       var avsandare = this.system.ActorOf();
       var bokare = this.system.ActorSelection(
          "akka.tcp://AllaBokare@localhost:8080/user/Bokare");
       bokare.Tell(new BioData.Bokningsmeddelande(StolNr,
          int.Parse(txtAnvID.Text)), avsandare);
    }

    Den lokala aktören ser ut så här. Notera att den innehåller lite fulkod för att erhålla en referens till formuläret i fråga. Koden förmodar att formuläret är ensamt i programmet.

    public class LocalActor : Akka.Actor.ReceiveActor
    {
       public LocalActor()
       {
          this.Receive(s =>
          {
             var stol = (Application.OpenForms[0]
                as Form1).Stolreferens(s.StolNr);
             stol.BackColor = s.BokningAccepterad ? Color.Green : Color.Red;
          });
       }
    }

    Varje klient som klickar på en knapp för att utföra en bokning, kommer att få en bekräftelse genom att knappen blir grön. Om stolen inte är bokningsbar blir istället knappen röd.

    form2

  • Introduktion till Akka .NET Remote

    Introduktion till Akka .NET Remote

    Akka .NET Remote låter aktörerna i Akka .NET skicka meddelanden till andra processer genom Windows Sockets. Det innebär att ett system kan distribueras över flera processer oavsett om de ligger på samma fysiska maskin eller på en annan maskin i nätverket.

    Arkitekturen i Akka .NET Remote är serverfri – varje peer kan kontakta och kommunicera med vilken annan peer som helst, givet att namn och port är känt. Det innebär att om du bygger en server, kan den utan problem förmedla direktkontakt mellan sina klienter.

    Detta exempel visar ett mycket enkelt bokningssystem av en biosalong med tre stolar. Systemet består av tre stycken projekt, en server, ett datalager och en klient. Servern och klienten är Console Applications (C#) och datalagret är ett klassbibliotek.

    vs

    Servern
    Låt oss börja med att titta på servern. I mitt exempel heter den BioServer. All kod som visas i servern förutsätter följande:

    1. Akka .NET är installerat. Detta kan göras med följande Nuget-kommando:

    Install-Package Akka

    2. Akka .NET Remote är installerat.

    Install-Package Akka.Remote

    3. Följande using-sats:

    using Akka.Actor;

    Så här ser huvudprogrammet ut:

    class Program
    {
       public static Stollista stolar;
    
       static void Main(string[] args)
       {
          var config =
             Akka.Configuration.ConfigurationFactory.ParseString(@"
    akka {
        actor {
            provider = ""Akka.Remote.RemoteActorRefProvider, Akka.Remote""
        }
    
        remote {
            helios.tcp {
                port = 8080
                hostname = localhost
            }
        }
    }
    ");
    
          stolar = new Stollista()
             { new Stol(1), new Stol(2), new Stol(3) };
          Console.WriteLine(stolar.ToString());
    
          using (var system =
             Akka.Actor.ActorSystem.Create("AllaBokare", config))
          {
             var bokare = system.ActorOf(new
                Akka.Actor.Props(typeof(Bokningsaktor)),"Bokare");
             Console.ReadLine();
          }
       }
    }

    Notera att konfigurationen anger vilken port servern ska lyssna på (8080). Den kodraden har jag klippt ut från dokumentationen för Akka .NET Remote och klistrat in här. Portnumret måste vara förutbestämt för att den första klienten ska kunna hitta servern.

    Därefter har jag min biosalong bestående av tre stolar. Dessa beskrivs av följande klasser:

    class Stollista : List<Stol>
    {
       public override string ToString()
       {
          var ret = new System.Text.StringBuilder();
          foreach (var s in this)
             ret.AppendLine(s.ToString());
          return ret.ToString();
       }
    
       public bool Boka(int stolNr, int bokadAv)
       {
          var stol = this.Find(m => m.StolNr == stolNr);
          if (stol == null)
             return false;
          stol.BokadAv = bokadAv;
          return true;
       }
    }

    Därefter använder jag Akka.Actor.ActorSystem.Create för att skapa mitt aktörssystem (kallad AllaBokare) på samma sätt som om jag hade tänkt att använda Akka .NET i en och samma process, och i using-blocket för aktörssystemet så skapar jag min aktör (kallad Bokare). Det som återstår i servern är klassen som beskriver aktören. Den ser ut så här:

    class Bokningsaktor : Akka.Actor.ReceiveActor
    {
    
       public Bokningsaktor()
       {
          this.Receive(b => {
             Console.WriteLine("Ny bokning!");
             Program.stolar.Boka(b.StolNr, b.BokadAv);
             Console.WriteLine(Program.stolar.ToString());
          });
       }
    }

    Datalagret
    Datalagret har jag valt att lägga i ett annat projekt i samma lösning som servern. Jag har kallat datalagret för BioData. Här finns en klass som beskriver meddelandet som klienten kan skicka till servern.

    public class Bokningsmeddelande
    {
       public int StolNr { get; private set; }
       public int BokadAv { get; private set; }
       public Bokningsmeddelande(int stolNr, int bokadAv)
       {
          this.StolNr = stolNr;
          this.BokadAv = bokadAv;
       }
    
       public override string ToString()
       {
          return string.Format("Bokning från {0} på stol {1}.",
             this.BokadAv, this.StolNr);
       }
    }

    Både servern och klienten ska ha en referens till datalagret.

    Klienten
    Klienten är mycket enkel. Notera först skillnaden i konfigurationen mellan klienten och servern. Servern har port 8080, vilket klienten måste veta om för att kunna hitta servern. Men när klienten gjort sig tillkänna så vet servern vilken port klienten använder, därför behöver vi inte bestämma någon port åt klienten. Att ange port 0 är att begära dynamisk port.

    Därefter skapar vi ett aktörssystem med det godtyckliga namnet Bokningsklient. Från systemet i klienten väljer vi att ansluta oss till servern genom att anropa ActorSelection. Som argument använder jag sökvägen till aktören på servern (som hette Bokare i systemet AllaBokare). Slutligen samlar jag in information från slutanvändaren som skickas till servern med metoden Tell. Så här ser källkoden för klienten ut:

    class Program
    {
       static void Main(string[] args)
       {
          var config = Akka.Configuration.ConfigurationFactory.ParseString(@"
    akka {
        actor {
            provider = ""Akka.Remote.RemoteActorRefProvider, Akka.Remote""
        }
        remote {
            helios.tcp {
                port = 0
                hostname = localhost
            }
        }
    }
    ");
    
          using (var system =
             Akka.Actor.ActorSystem.Create("Bokningsklient", config))
          {
             var bokare = system.ActorSelection(
                "akka.tcp://AllaBokare@localhost:8080/user/Bokare");
             Console.Write("Vad är ditt användar-ID? Skriv ett heltal. ");
             var userId = int.Parse(Console.ReadLine());
             while(true)
             {
                Console.Write("Vilken stol vill du boka? (1-3) ");
                var stol = Console.ReadLine();
                if (stol == "")
                   break;
                bokare.Tell(new
                   BioData.Bokningsmeddelande(int.Parse(stol), userId));
             }
             system.Shutdown();
          }
       }
    }

    Bilden visar servern bakom en klient.

    akka_remote

    Eftersom portnumret tilldelas till klienten dynamiskt så kan flera klienter startas och boka platser. Och eftersom arkitekturen i Akka .NET Remote är peer-to-peer så kan dessa skicka meddelanden till varandra.

  • Introduktion till Gosbust

    Introduktion till Gosbust

    Efter mycket om och men så kan nu mitt programmeringsspråk Gosbust laddas hem. Ladda hem filen Gosbust.zip och packa upp innehållet, Gosbust.exe, på lämplig plats. Om du bara vill testa lite, duger skrivbordet bra. Om du vill köra ett eget program så kan du använda t.ex. Anteckningar för att skriva programmet, spara det och skicka det som argument till Gosbust.exe. Men om du bara vill titta på språket och labba lite, kan du starta Gosbust genom att dubbelklicka på Gosbust.exe. Du möts då av detta:

    gosbust

    Jag vill igen betona att detta programmeringsspråk är utvecklat lite som ett terapiprojekt, och är absolut inget jag förespråkar användning av, även om jag förmodligen kommer implementera något skarpt i detta bara-för-att… 😉

    Konsolen förstår några enkla kommandon och dessa presenteras för användaren när han startar Gosbust. Om man skriver något som konsolen inte förstår så skickas det vidare till kompilatorn som försöker exekvera. Om du vill testa något av de medföljande tre exempelprogrammen, skriv namnet på programmet. Gosbust visar källkoden för programmet och frågar om du vill köra. Varje gång du skriver RESET eller kör ett exempelprogram så töms minnet och du förlorar alla dina variabler.

    Gosbust version 0.8 känner till fyra olika datatyper. Heltal (32-bit), flyttal, bitar och strängar. För att skriva ut ett värde på skärmen, t.ex. ett heltal, skriv << direkt åtföljt av värdet som du vill skriva ut. Detta skriver ut talet 5:

    <<5

    Heltal känns igen av Gosbust på att de endast innehåller tecknen 0-9. Flyttal innehåller en punkt, bitar är en nolla eller en etta (0/1) och strängar är allt annat. Om du vill att Gosbust ska skriva Hej!, skriv:

    <<Hej!

    Däremot, om du vill att Gosbust ska skriva ut Hej världen! kan du inte skriva så här:

    <<Hej världen!

    Det bror på att Gosbust tror att du har skickat två instruktioner, och den andra (världen!) känns inte igen av kompilatorn. Om Hej världen! kapslas in av citattecken, så fungerar det som förväntat.

    <<"Hej världen!"

    Gosbust är typsäkert. När man lagrar data i en variabel, måste variabeln vara av den typ som datat man försöker lagra. Variablernas namn är i denna version begränsat till en bokstav och en eventuell typmarkör. Detta lagrar heltalet 10 i variabeln A:

    10>>A

    Du kan läsa av värdet av A genom att skriva <<A. Variabler som lagrar bitar heter A? till Z?, flyttal lagras i A# till Z# och strängar lagras i A$ till Z$. Det är inte tillåtet att läsa av en variabel som inte är initierad. Detta ger ett fel:

    <<A$

    Men om du först skriver t.ex. “Hej!”>>A$ kommer <<A$ att fungera. Om du vill radera minnet, skriv reset.

    gosbust2

    Provkör gärna testprogrammen genom att skriva fibonacci, fizzbuzz eller primes. Mycket nöje!

  • SKYpe? Apple?

    SKYpe? Apple?

    Knappt har vi vant oss att säga OneDrive istället för SkyDrive om Microsofts molnlagringstjänst innan DN meddelar att Microsofts telefontjänst Skype inte får heta Skype, eftersom det är för likt mediebolaget Sky.

    “Det är inte bara namnen med ordet “Sky” som liknar varandra utan även Skypes logotyp, med en molnformad kant runt namnet Skype, som skapar associationer till ordet “Sky”, menar domstolen.”

    Ja, vad är det egentligen som är vad här?

    Även Apple och Apple har bråkat om att namnen har varit lika, men eftersom Apple och Apple var i olika branscher – Apple säljer musik och Apple säljer datorer – så gjordes en överenskommelse om att så länge Apple håller sig borta från musikbranschen så kan man leva med förväxlingsrisken. När Apple sedan gav sig in i musikbranschen så valde Apple 2006 att stämma Apple. Apple vann. Apple fick använda sin logga för att lansera sin musiktjänst.

  • 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.

  • Vad ska språket heta?

    Vad ska språket heta?

    Uppdatering 2015-05-02: Språket kommer att heta Gosbust – ett genialt namn med referenser både till andra språk, populärkultur och till nyckelord i detta språk. Upphovsmannen till namnet är anonym, men har fler fyndigheter på sin meritlista, t.ex. ordet som bättre än något annat beskriver kristendomens gud Jahve – UMO (Undetectable Mythological Object).

    Uppdatering 2015-05-03: Under ett tidigt stadium av språkets utveckling så skrev man A>>1 för att lagra 1 i variabeln A. Nu när Gosbust går att ladda hem är detta ändrat. Numera skriver man istället 1>>A för att lagra 1 i variabeln A. Uttrycket A>>1 ger ett kompileringsfel idag. Detta gäller för alla datatyper, och även för de underliggande instruktionerna. PUTI A 1 ska nu skrivas PUTI 1 A, där 1 är värdet som ska lagras och A är variabeln den ska lagras i.

    Först och främst vill jag betona att detta språk är lite av ett terapiarbete för mig – det kommer absolut inte lösa världsproblemen eller frälsa några programmerare. Parsern är inte hierarkisk och upplägget är inspirerat av Assembler på Commodore 128. Detta innebär att språket består av instruktioner, där varje instruktion alltid tar samma antal argument. Det innebär att man inte behöver ha olika separatorer mellan argumenten och mellan programsatserna. Om ett kommando tar två argument kommer kompilatorn att läsa två argument och sedan anta att det nästa som påträffas är nästa kommando. Som separator används whitespace (blanksteg, tabb eller enterslag), och det går bra att infoga flera whitespaces efter tycke, men endast på de platser där det ska vara whitespaces. Det går inte att undvika whitespaces där whitespaces förväntas.

    Följande kod placerar värdet 1 i heltalsvariabeln A, följt av en kommentar:

    PUTI A 1 /* Sparar 1 i A */

    Detta däremot kommer inte att fungera eftersom uttrycket 1/* inte är en giltig heltalskonstant:

    PUTI A 1/* Sparar 1 i A */

    Notera att variablernas datatyp härleds från deras namn. Variabler utan postfix, t.ex. A, B eller C antas vara 32-bitars heltal. Variabler vars namn slutar med $, t.ex. A$, B$ eller C$ antas vara strängar. Frågetecken betyder bit (t.ex. A?) och korsbrygga betyder flyttal (t.ex. A#).

    Det finns ett enkelt syntaxlager ovanpå instruktionerna som syftar till att öka läsbarheten. PUTI A 1 kan kortas ner till 1>>A. Detta program visar de 20 första talen i Fibonacci-sekvensen (utom 0):

    PUTI 1 A
    GETI A
    PUTI 1 B
    GETI B
    PUTI 0 X
    PROC Run
       ADDI A B C
       GETI C
       INCI X
       SWPI A B
       SWPI B C
       LETI X 18 J?
       GOTO J? Run
    GETS "Done!"

    Första raden placerar 1 i variabeln A. GETI A presenterar värdet av A. PROC Run markerar starten av en subrutin som kort och gott heter Run – subrutiner kan heta vad som helst, men om namnet består av något annat än bokstäverna A-Z måste namnet kapslas in av citattecken. Subrutiner körs även om de inte blir anropade, så efter PUTI X 0 (rad 5) kommer ADDI A B C (rad 7) att köras (fallthrough). ADDI A B C sparar summan av A och B i C, vilket kan kortas ner till A+B>>C. INCI X ökar X med 1, vilket kan kortas ner till X++. SWAPI A B låter värdet av A hamna i B och värdet av B hamna i A, vilket kan kortas ner till A<>B. LETI X 18 J? testar huruvida X är mindre än 18, och lagrar i så fall 1 i J?, annars 0. Detta kan kortas ner till X<18>>J?. Motsvarigheten heter GRTI. GOTO J? Run anropar subrutinen Run (i detta fall rekursivt) om värdet av J? är 1.

    Så här ser den korta versionen av mitt lilla Fibonacci-program ut:

    1>>A <<A 1>>B <<B 0>>X
    Run: A+B>>C <<C X++ A<>B B<>C X<18>>J?
    GOTO J? Run
    <<"Done!"

    Apropå anrop så finns två nyckelord för att anropa subrutiner: GOTO och GOSB. Skillnaden mellan dessa är att GOTO egentligen bara ändrar nuvarande exekveringspunkt, medan GOSB (“go sub”) dessutom lagrar vilken plats man lämnar i en enkel callstack. Subrutiner som anropas med GOSB kan lämna tillbaka kontrollen till den anropande koden genom instruktionen RTRN (“return”) som plockar ett värde från callstacken. Om RTRN används när callstacken är tom avslutas programmet.

    Trots att språket varken är färdigt eller släppt så finns redan konceptet som en Ruby DSL. Tack Jonas Elfström!

    Jag återkommer med fler kodsnuttar, och tar tacksamt emot tips på namn! (Dawkins är upptaget eftersom eftersom den berömde biologen Richard Dawkins faktiskt också har designat ett programmeringsspråk.)

    Uppdatering: Tidigare idag publicerade Roger Alsing en Fizz Buzz på Facebook. Finessen med hans Fizz Buzz var att den saknade tester, utan utnyttjade objektorienteringen i C# för att få till serien. Mitt språk är tyvärr inte objektorienterat, men så här ser en fungerande Fizz Buzz ut:

    1>>I
    Run:
       1>>C? I%3>>F F=0>>F? I%5>>B B=0>>B? F?&B?>>X?
       GOSB X? FizzBuzz
       GOSB B? Buzz
       GOSB F? Fizz
       GOSB C? NoFizzBuzz
       I++ I<101>>J?
       GOTO J? Run
    /* Quit! */
    RTRN
    
    /* Sub routines... */
    FizzBuzz: 0>>F? 0>>B? 0>>C? <<"Fizz Buzz" RTRN
    Buzz: 0>>F? 0>>C? <<"Buzz" RTRN
    Fizz: 0>>C? <<"Fizz" RTRN
    NoFizzBuzz: <<I RTRN
  • Sci-Fi World

    Sci-Fi World

    Det är ännu inte för sent att besöka Sci-Fi World i Stockholm, som har öppet även imorgon söndag. Jag spenderade lördagen där, och nördade loss rejält bland Magic-kort och D&D-tärningar. Väldigt mycket kretsade kring Stjärnornas Krig. Dragplåstren var Robert Patrick, Ray Park, Samantha Fox, Joe Cantillo och Sam Jones.

    Sam J. Jones spelade Flash Gordon 1980 (som Queen gjorde soundtracket till) och sig själv i filmen Ted.

    WP_20150425_008

    Detta är bilen Kit från tv-serien Knight Rider med David Hasselhoff:

    WP_20150425_027

    Har tar min dotter kort på mig och Samantha Fox (som sjöng in en låt till Terror på Elm Street 5):

    WP_20150425_011

    Detta är Robert Patrick som spelade T-1000 i filmen Terminator 2 och John Doggett i Arkiv X:

    WP_20150425_019

  • 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
  • Betydelsen av ordet “nörd” är justerat

    Betydelsen av ordet “nörd” är justerat

    Mattias Boström twittrade (följ!) detta igår:

    “Nörd” har fått ändrad definition i nya SAOL-upplagan (upptill på bilden).

    Han bifogar två bilder föreställande den tidigare och den reviderade definitionen från Svenska Akademiens ordlista, där den tidigare lyder “enkelspårig och löjeväckande person, tönt” och den nya lyder “person som har ett stort specialintresse och som därför kan verka något enkelspårig“.

    Eventuellt kan min syn på ordet vara färgad av hur vi använde ordet på 80-talet eftersom jag knappt använt ordet sedan dess, men då var “nörd” svengelska för “nerd” som fortfarande är en nedsättande beskrivning av någon som är lite töntig. Men de som beskriver sig själva som nördar idag, väljer alltså inte ett negativt ord för att påvisa självdistans, utan ett ord som betraktas som relativt neutralt.

    Den som vill ta sig an kulturskatten av college- och high school-filmer från 80-talet kommer fortfarande komma i kontakt med “nördar” i ordets gamla bemärkelse, men dessa kanske vi ska benämna som “nerdar” idag för att visa kopplingen till det engelska ordet “nerd”.

    Vad sägs om höjdarfilmen “Nördarna kommer!” (Revenge of the nerds) från 1985:

    Eller klassikern “Drömtjejen” (Weird Science), också från 1985:

    Mycket nöje!