Archive for the ‘LINQ’ Category

Opgrader gamle collections til LINQ – andet forsøg

Monday, March 30th, 2009

I begejstring over at have fundet OfType<T>-metoden til IEnumerable, gjorde jeg mig desværre ikke den ulejlighed at undersøge sagen til bunds, så derfor opdagede jeg ikke, at der er to metoder til det formål. 

IEnumerable er nemlig blevet udvidet med både OfType<T> og Cast<T>. De implementerer sammen funktionalitet, nemlig at oversætte IEnumerable til IEnumberable<T>, men måden, de gør det på, er forskellig.

OfType<T> tager de elementer fra samlingen, der kan oversættes til T og gennemløber disse. Så hvis vi har en blandet collection som f.eks. 

var mixed = new ArrayList() { 42, "hello", 1337, "world" };
foreach (var element in mixed.OfType<int>()) {
   Console.WriteLine(element);
}

vil OfType<T> kun behandle de to heltal, 42 og 1337 og springe de to tekster over. 

Cast<T> tager derimod hver element og laver et cast til T, så hvis vi forsøger at bruge Cast<int> på ovenstående, får vi en InvalidCastException, da "hello" og "world" ikke kan castes til int

I mit Regex-eksempel ved vi, at de enkelte elementer er af typen Match, så her vil det være mest oplagt, at bruge Cast<T> frem for OfType<T>, som jeg først havde valgt. Eksemplet ser derfor ud som følger:

static void Main(string[] args) {
   var input = "3249785x329x48572938x79287459328576923" +
      "489263592374x598237459x236459812x73459872918798" +
      "7192871293874x1923857319487x510237x203012374127" +
      "34234912873501723x501293741x243452345x234523403";

   var pattern = @"\d";

   foreach (var match in Regex.Matches(input, pattern)) {
      Console.WriteLine(match);
   }

   var query = Regex.Matches(input, pattern).Cast<Match>()
      .GroupBy(match => match.Value)
      .OrderBy(grouping => grouping.Count());

   foreach (var entries in query) {
      Console.WriteLine("{0} = {1}", entries.Key, entries.Count());
   }
}

Opgrader gamle collections til LINQ

Wednesday, March 25th, 2009

(Bemærk: Dette indlæg har flere detaljer om nedenstående)

Klassebiblioteket til .NET kommer med et hav af brugbare klasser. En del af disse har været med siden den spæde begyndelse og nyder således ikke glæde af generics, der som bekendt først blev indført i .NET 2.0. 

Eksempelvis returnerer Regex.Matches ikke en IEnumerable<Match>, men derimod en MatchCollection, der er en specifik indkapsling af en ArrayList indeholdende instanser af Match. Der er andre eksempler på metoder, der returnerer sådanne specifikke Collections. MatchCollection og lignende implementer såvel ICollection som IEnumerable, så vi kan let løbe dem igennem, men da de ikke implementerer IEnumerable<T>, kan vi desværre ikke umiddelbart bruge LINQ.

Til alt held er IEnumerable blevet udvidet med en extension method, OfType<T>, der tager en IEnumerable og laver den om til en Enumerable<T>. Har man læst grundigere på LINQ, end jeg tilsyneladende har, er dette næppe en nyhed, men jeg har altså først fundet den nu. Jeg skal ikke kunne sige, hvordan jeg har overset denne fremragende feature, men nu hvor jeg er blevet opmærksom på den, vil jeg i hvert fald gøre mit til, at andre ikke overser den. 

Hvad kan vi så bruge OfType<T> til? Hvis vi fortsætter med Regex-eksemplet, har vi nu mulighed for at kombinere Regex’s stærke søgefunktionalitet med LINQs ditto forespørgselsfunktionalitet. Lad os se på et lille eksempel. 

static void Main(string[] args) {
   var input = "32497x85329485x72938792x87459328576923" +
      "4892635923745982374xx5923645981273x459872918798" +
      "719287129387x419238573194x87510237203x012374127" +
      "3423491x287350172x3501293741x243452345234523403";

   var pattern = @"\d";

   var query = Regex.Matches(input, pattern).OfType<Match>()
      .GroupBy(match => match.Value)
      .OrderByDescending(grouping => grouping.Count());

   foreach (var entries in query) {
      Console.WriteLine("{0} : {1}", entries.Key, entries.Count());
   }
}

 
Ovenstående leder efter cifre i input bestående af cifre og støj (x) ved hjælp af et simpel regular expression. Resultatet er en MatchCollection. Ved hjælp af OfType<T> konverterer vi denne til IEnumerable<T> og bruger derefter LINQ til at gruppere og tælle forekomsterne af de enkelte cifre for til sidst at udskrive det samlede resultat sorteret efter hyppighed som illustreret nedenfor.

2 : 27
3 : 26
7 : 21
9 : 20
4 : 19
8 : 16
5 : 16
1 : 13
0 : 6
6 : 3

Det, synes jeg, er cool. Det kan selvfølgelig laves på andre måder, men det her er kort og præcis (og i dette tilfælde er det faktisk kortere at kalde extension methods end at bruge LINQs forespørgselssprog).

Eksemplet er selvfølgelig meget simpelt, men forestil dig, at vi ikke leder efter cifre men derimod IP-adresser i en logfil til en webserver. Så er det blot at ændre vores Regex for at ovenstående giver en en komplet rapport over besøgende sorteret efter hyppighed. Kombinationen af Regex og LINQ er ret potent.

Materiale fra en dag med Jon Skeet

Wednesday, November 5th, 2008

Jeg har lagt en zip-fil med slides og kodeeksempler fra Jons præsentation til download. God fornøjelse.

Videoen er ikke på trapperne endnu, men jeg skal nok sige til.

Ja tak til kompakt kode

Wednesday, October 29th, 2008

“One-liners er bare så 1995″, skrev Klaus Hebsgaard i en kommentar. For mit vedkommende stod 90erne i høj grad i C++s tegn, og det var der altså ikke meget one-liner over. Det løber mig stadig koldt ned af ryggen, når jeg tænker på templates i C++, men lad det nu ligge. Det er lang tid siden,  jeg har haft fingrene i den slags, og jeg savner det faktisk ikke. Nu går min tid hovedsagelig med C#, så lad os tale lidt om, hvordan one-liners har det i C#.

Hvis one-liners er et fænomen, der hører midt 90erne til, har den seneste inkarnation af C# vel taget et par skridt tilbage mod svundne tider. Mange af de nye tiltag i C# 3 går i hvert fald på at gøre sproget mindre teksttungt. Det bifalder jeg. 90er-stil eller ej.

Det gode er jo, at sproget stadig tillader, at vi stadig kan overspecificere koden i en grad, der gør den næsten ulæselig, så hvis vi vil følge Klaus’ eksempel, kan vi skrive:

IEnumerable<string> seventiesmovies = Enumerable.Select<Movie, string>(
   Enumerable.OrderBy<Movie, string>(
   Enumerable.Where<Movie>(movies, delegate(Movie movie) {
      if(movie.ReleaseYear >= 1970 && movie.ReleaseYear <= 1979) {
         return true;
      } else {
         return false;
      }}) ,
   delegate(Movie movie) { return movie.Title; }),
   delegate(Movie movie) { return movie.Title; }
);

Men hvis vi hellere vil hylde 1995 og de nye toner i C#, kan vi også bare skrive:

var seventiesmovies = from movie in movies
   where movie.ReleaseYear >= 1970 && movie.ReleaseYear <= 1979
   orderby movie.Title
   select movie.Title;

Jeg skal indrømme, at jeg ikke ved, om Klaus rent faktisk foretrækker det første eksempel, og det er ikke min mening at hænge Klaus ud, så jeg håber, at du, kære læser og Klaus i særdeleshed, vil fange at dette er skrevet med et glimt i øjet.

Jeg synes blot, at jeg ville slå endnu et slag for kompakt kode. Undskyld Klaus.

C# 3.0 – LINQ

Wednesday, October 24th, 2007

Efter at have set på de mange mindre udvidelser til C# 3.0, er vi endelig kommet til den store nyhed: Language-INtegrated Query eller bare LINQ. Skåret til benet er LINQ blot en SQL-lignende syntaks til forespørgsler på typer, der understøtter IEnumerable<T>. Det lyder måske ikke af meget, men når man tænker på hvor mange typer, der understøtter IEnumerable<T> samt tager i betragtning, at egne typer naturligvis også kan implementere dette interface, begynder det at blive interessant. I praksis betyder det, at vi f.eks. kan anvende LINQ mod collections, XML og naturligvis relationelle data.

En stor del af LINQs SQL-lignende funktionalitet kommer af et sæt udvidelsesmetoder til IEnumerable<T>. C# 3.0 har dog reserverede nøgleord for disse, så syntaksen bliver mere koncis, men under kølerhjelmen er der altså blot tale om metodekald. Med brug af de specielle LINQ nøgleord, bliver syntaksen som følger:

from [type] id in expr
   [ from [type] id in expr ]
   [ let id = expr ... ]
   [ where bool-expr ]
   [ join [type] id in expr on expr equals expr ]
   [ orderby order-expr [ ascending | descending ], order-expr, ... ]
      [ select expr | group expr by key ]
         [ into id query ] ...

men det er måske lettere blot at se på et eksempel. Jeg vil springe det åbenlyse databaseeksempel over, da det allerede er beskrevet så mange steder og i stedet se på LINQ til XML. Her viser LINQs elegance sig nemlig med stor overbevisning.

Lad os sige, at vi har en liste af kunder ud fra hvilken, vi ønsker at lave et XML-dokument med alle vores kontakter i Danmark. Med en traditionel tilgang til XML kunne det se ud som vist nedenfor:

XmlDocument document = new XmlDocument();
XmlElement contacts = document.CreateElement("contacts");
// dump all DK contacts to document
foreach (Contact customer in customers) {
   if (customer.Country == "DK") {
      XmlElement contact = document.CreateElement("contact");
      XmlElement name = document.CreateElement("name");
      name.InnerText = customer.Name;
      contact.AppendChild(name);
      XmlElement phone = document.CreateElement("phone");
      phone.InnerText = customer.Phone;
      contact.AppendChild(phone);
      contacts.AppendChild(contact);
   }
}
document.AppendChild(contacts);

Koden er ganske ligetil. Vi opretter et dokument, indsætter en sektion for vores kontakter, og løber derefter vores interne datastruktur igennem for at finde alle danske kontakter. For hver af disse opretter vi et contact-element og indsætter passende elementer for navn og telefonnummer. Det er der næppe noget odiøst i. Metoden er drevet af dokumentets struktur, og der er ikke nogen direkte forbindelse mellem udvælgelsen af poster og selve dokumentet. Den forbindelse skaber vi i koden. Koden er også relativ omfattende.

Med LINQ kunne den tilsvarende løsning se således ud:

XElement contacts =
   new XElement("contacts",
      from customer in customers
      where customer.Country == "DK"
      select new XElement("contact",
         new XElement("name", customer.Name),
         new XElement("phone", customer.Phone)
      )
   );

Ikke alene er ovenstående langt kortere, men læg også mærke til hvordan strukturen af koden stemmer fuldstændig overens med vores datastruktur. Bemærk ligeledes hvordan udvælgelsen og skabelsen af contact-elementerne er en samlet operation.

Den resulterende XML-stump er naturligvis identisk i begge tilfælde, og ifølge Microsoft bruger LINQ 30-50% mindre hukommelse end XML DOM. Jeg kunne ærlig talt ikke se det smarte ved LINQ, de første par gange jeg så det omtalt, men for mit vedkommende er LINQ fremover måden at arbejde med XML på.

I og med at C# 3.0 compiler kode til at køre på CLR 2.0, kan vi bruge Reflector til at se på, hvad ovenstående kode egentlig resulterer i og ikke overraskende, kan vi konstatere, at det hele er implementeret via kald til de føromtalte udvidelsesmetoder samt diverse anonyme delegates.

XElement contacts = new XElement("contacts",
   customers.Where<Contact>(delegate (Contact customer) {
      return (customer.Country == "DK");
   }).Select<Contact, XElement>(delegate (Contact customer) {
      return new XElement("contact", new object[] {
         new XElement("name", customer.Name),
         new XElement("phone", customer.Phone) });
      }));
Console.WriteLine(contacts);

(Bemærk, at Reflector kan sættes til at analysere IL til forskellige versioner af .NET. Hvis ovenstående vises i Reflector med de korrekte C# nøgleord, er det fordi den står til at Disassemble til .NET 3.5 (ja, det kalder han det altså). Indstillingen kan ændres under View > Options > Disassembler > Optimization.)

Devscovery, dag 2-3 og VM i jetlag

Sunday, August 19th, 2007

Det var ellers min plan at opdatere kodehoved med indtryk fra de enkelte dage på devscovery, men en improviseret middagsaftale efter 2. dag og en hjemtur efterfulgt af en seriøs omgang jetlag efter dag 3 slog den ide i stykker. Så med en smule forsinkelse kommer her overblik over dag 2 og 3. Der kommer detaljer senere.

Anden dag var en blandet omgang med en del forskellige emner. Det bedste af dem var Jeffrey Richters gennemgang af nyhederne i C# 3.0. På underholdende vis gennemgik han alle de lettere overflødige features den nye version byder på for til sidst at binde det hele sammen i en præsentation af LINQ (der benytter alle disse “overflødige” features). Jeg har ikke været den store fan af LINQ indtil videre, men jeg må bøje mig for LINQs håndtering af XML. Det er bare så meget bedre, end det muligheder vi har været vant til. Derudover bød dagen på et par sikkerhedsindlæg omkring ASP.NET og Silverlight. For en gammel sikkerhedsnørd som mig, var der ikke meget nyt, men dog alligevel et par interessante elementer.

3. dag var ubetinget den bedste. Jeg havde ellers overvejet at droppe Jeffrey Richters heldagsindlæg omkring tråde og skalerbarhed til fordel for John Robbins indlæg om debugging, fordi jeg mente, at jeg havde styr på de forskellige klasser og APIer. Det er jeg meget glad for, at jeg ikke gjorde for i modsætning til hvad jeg ellers har hørt og læst om threading i .NET, kom Richter med en masse gode råd til, hvordan man bruger disse mekanismer på den bedste måde. Det var interessant og jeg kommer helt sikkert til at skrive et par indlæg om det.