Lektionsbloggen: LINQ och ny C#-syntax

Lektionens datum: 5/8 2008
Lektionens rubrik: Linq (1/2)
Lektionsbloggens fokus: LINQ to Objects och diverse ny C#-syntax

Dagens lektion introducerade LINQ och demade också ett antal nya syntax-möjligheter i C#.

Automatiska properties
Här följer två varianter på exakt samma klass. I exemplet till höger används C# 2008:s nya sätt att skriva en property som automatiskt ger en privat datamedlem som kapslas in av den publika propertien;

Traditionellt sätt:
class Contact
{
    private string _name;
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    private string _city;
    public string City
    {
        get { return _city; }
        set { _city = value; }
    }
}
Ny C#-syntax:
class Contact
{
    public string Name { get; set; }
    public string City { get; set; }        
}

Som du ser kan man spara in en hel del kod på det här sättet. Observera dock att det endast är möjligt att använda denna kortvariant om du inte har behov av att hantera propertien på något sätt i get- eller set-blocket.

Object Initialization Syntax
En annan syntax-nyhet i C# 2008 är Object Initialization. Tidigare var man tvungen att initera ett objekts properties med värden antingen genom en constructor eller så här:

Contact a = new Contact();
a.Name = "Ola Salo";
a.City = "Malmö";

Med den nya syntaxen kan du skriva exakt samma sak så här:

Contact b = new Contact { Name = "Alexander Bard", City = "Stockholm" };

Återigen, du sparar in en del tangenttryckningar och dessutom minskar behovet av att förse klassen med en constructor.

LINQ-introduktion
LINQ gör det möjligt att på ett enhetligt sätt hantera data från olika datakällor. En datakälla kan exempelvis vara en SQL-databas, ett XML-dokument eller rent av en lista med objekt. Se följande kod, där vi återanvänder objekten a och b från ovan;

List<Contact> contactList = new List<Contact>();
contactList.Add(a);
contactList.Add(b);
contactList.Add(new Contact { Name = "Lasse Kronér", City = "Göteborg" });
contactList.Add(new Contact { Name = "Nina Persson", City = "Jönköping" });
contactList.Add(new Contact { Name = "Tina Nordström", City = "Helsingborg" });
contactList.Add(new Contact { Name = "Pekka Heino", City = "Stockholm" });

var sthlmCitizens = from contact in contactList where contact.City=="Stockholm" select contact;

foreach (Contact c in sthlmCitizens)
{
    Console.WriteLine(c.Name);
}

Själva linkuttrycket är fetstilt ovan och följer följande struktur:
from identifier in list where condition select identifier

Notera att sthlmCitizens-referensen är deklarerad med nyckelordet var. var är ett nyckelord som gör det möjligt att deklarera en variabel och sedan låta första tilldelningen till variabeln avgöra vilken datatyp variabeln är. I detta fall returernar Linq-uttrycket ett objekt av typen IEnumerabe<Contact>, men det är inte alltid man kan veta, så var är en smidig genväg. Det du kan vara säker på är att ett Linq-uttryck alltid returnerar ett objekt som går att loopa med foreach.

När Linq-uttrycket körs loopar den igenom listan (contactList) för att se vilka objekt i listan (kan även vara en collection, array etc.) som uppfyller villkoret (contact.city=="Stockholm"). Identifier är den variabel som för varje loop innehåller det aktuella objektet, precis som c i foreach-loopen. Linq-uttrycket avslutas med en select-sats där man talar om vad som ska väljas ut, i det här fallet hela contact-objektet vilket innebär att jag i foreach-loopen kan typa c som en Contact och komma åt både c.Name och c.City. Man kan begränsa detta genom att förändra select-satsen i Linq-uttrycket enligt följande;

var sthlmCitizensNames = from contact in contactList where contact.City == "Stockholm" select new { contact.Name };

Det som händer här är att en helt ny datatyp skapas "on-the-fly" via new. Denna datatyp innehåller endast en property, nämligen Name. Eftersom vi inte kan veta namnet på denna datatyp innebär det att vi nu måste använda var i den foreach-loop som skriver ut träffarna;

foreach (var c in sthlmCitizensNames)
{
    Console.WriteLine(c.Name); //c.City finns inte här
}

Man kan också låta Linq-uttrycket sortera objekten, t.ex. så här;

var sthlmCitizensOrdered = from contact in contactList where contact.City == "Stockholm" orderby contact.Name descending select contact;

Apropå mina tidigare inlägg om sortering så är ju det här ganska intressant med tanke på att klassen Contact inte implementerar IComparable och vi har inte gjort någon separat IComparer-klass heller. Nyckelordet descending kan utelämnas eller ersättas med ascending, man kan också andrahandssortera genom att kommaseparera de properties man vill sortera på.

Extension Methods
En annan nyhet i C# 2008 är möjligheten att utöka redan existerande klasser med egna metoder. I följande exempel kommer jag att utöka klassen char med en metod som returnar true om tecknet är en vokal och annars false. För att detta ska vara möjligt måste metoden placeras i en static klass, metoden i sig måste vara public och static och metodens första parameter måste vara av den typ som jag vill utöka samt föregås av nyckelordet this. Dessa förutsättningar kommer i fetstilt här;

public static class CharExtensions
{
    public static bool IsVowel(this char letter)
    {
        letter = Convert.ToChar(letter.ToString().ToLower());

        char[] vowels = { 'a', 'o', 'u', 'å', 'e', 'i', 'y', 'ä', 'ö' };        
        return vowels.Contains<char>(letter);
    }
}

För att metoden ska hantera både versaler och gemener korrekt börjar jag med att i metodens första rad konvertera bokstaven till en sträng och göra den till liten bokstav via String-klassens ToLower-metod. Därefter konverteras tecknet tillbaka till en char via Convert.ToChar.

Sen deklarerar jag en array av char som innehåller alla vokaler (som gemener) och slutligen anropar jag char-arrayens Contains-metod som kommer att returnera true när letter är en vokal, annars false.

Nu kan denna metod anropas direkt på en instans av en char;
image

I IntellieSense-fönstret ser du att det är en extension-method på den blå lilla pilen i ikonen, samt att tooltipfönstret markeras med "(extension)".

Imorgon ska lektionen handla om Linq to SQL.

Ladda hem "the Lektionsbloggen Solution"

No comments :

Post a Comment