Archive for the ‘.NET 4.0’ Category

Nyheder i C# 4 – syvende del: Varians

Thursday, August 19th, 2010

Det er blevet tid til sidste indlæg i min serie om nyheder i C# 4. Denne gang skal det handle om varians eller mere specifikt ko- og kontravarians.

Inden C# 4 kom på gaden var dette noget, der blev omtalt en hel del, men i realiteten er de introducerede forbedringer af den type, hvor vi egentlig ikke behøver at kende detaljerne. Vi kan blot glæde os over, at tidligere problemfyldte konstruktioner nu fungerer som forventet.

Lad os først få de relevante definitioner på plads for varians:

Kovarians vil sige, at vi kan konvertere en ”mindre” type til en ”større” type. Eksempelvis kan vi konvertere fra Int32 til Int64.

Kontravarians vil sige, at vi kan konvertere fra en ”større” type til en ”mindre” type. C# kræver eksplicit konvertering i dette tilfælde, da vi risikerer at miste data. Konverterer vi således fra Int64 til Int32, kræver C# at vi laver et cast af værdien til Int32.

Ideen bag de introducerede features er at tillade disse konverteringer på en sikker måde.

Array er kovariant

C# har understøttet kovarians for arrays siden version 1 men desværre ikke uden problemer.

Betragt nedenstående klassehierarki.

public class Creature {
}

public class Monster : Creature {
   public override string ToString() { return "Argh!!"; }
}

public class Vampire : Monster {
   public override string ToString() { return "I Vant Yor Blod"; }
}

public class Zombie : Monster {
   public override string ToString() { return "Braaaaiins!"; }
}

Monster er en specialisering af Creature, og Zombie og Vampire er yderligere specialiseringer af Monster. Som følge af konventionerne for arv, er enhver Zombie således et Monster og et Creature. Ligeledes er enhver Vampire et Monster og et Creature. Har vi en instans af Zombie, kan vi således tillade os at betragte den som enten Zombie, Monster eller Creature.

Opretter vi et array af Vampire, burde det således være i orden at betragte det som et array af Monster, eftersom Vampire er en specialisering af Monster. Nedenstående kode er derfor tilladt.

Vampire[] vampires = new Vampire[1];
Monster[] monsters = vampires;

Så langt, så godt. Men Zombie er jo også et Monster, så hvad sker der, hvis vi smider en Zombie i vores array?

monsters[0] = new Zombie();

Compileren tillader dette, da Zombie jo er en specialisering af Monster. Afviklingsmiljøet opdager dog, at vi nu har en Zombie i vores Vampire[] og smider en ArrayTypeMismatchException. Ups!

Problemet og løsningen

Hvorfor er ovenstående et problem? Når vi kan indsætte en instans af Zombie i et array af Vampire, kan vi komme til at referere denne via en Vampire-reference, og det er som bekendt ikke tilladt i C# og derfor smider afviklingsmiljøet en exception. Heldigvis er denne form for varians kun understøttet for array. Forsøger vi således ovenstående med List, kommer vi ikke langt.

List<Vampire> vampires = new List<Vampire>();
List<Monster> monsters = vampires; // compilefejl

Ovenstående oversætter således ikke. Men hvorfor egentlig ikke? Hvorfor kan vi ikke anskue en samling af Vampire som en samling af Monster, når vi kan betragte en enkelt instans af Vampire som en instans af Monster?

Problemet er at array tillader, at vi ændrer indholdet, så hvis vi ønsker at betragte en samling af Vampire som en samling af Monster, er vi nødt til at gøre det på en måde, så vi ikke kan ændre elementerne som ovenfor.

IEnumerable<T> tillader os at gennemløbe en samling af objekter, uden at vi kan ændre denne. Så hvis vi ændrede eksemplet fra at bruge array til at bruge IEnumerable<T>, kunne det således ud:

List<Vampire> vampires = new List<Vampire>();
IEnumerable<Monster> monsters = vampires;

Desværre virker ovenstående ikke i tidligere version af C#, for skønt vi ved, at vi ikke kan ændre indholdet via en IEnumerable<T>-reference, er compileren desværre uvidende på dette punkt. Vi mangler altså en måde at synliggøre dette over for compileren.

I .NET 4 er IEnumerable<T> og flere andre typer således blevet opdateret med denne information via en ny syntaks til understøttelse af ko- og kontravarians. Den opdaterede definition af IEnumerable<T> er som følger:

IEnumerable<out T>

out indikerer, at vores instans af T kun bliver brugt som output. Vi kan altså få lov at løbe en samling af T igennem og betragte hvert element som instanser af Ts baseklasse, fordi elementerne kun optræder som output.

På samme vis er f.eks. Action<T> blevet opdateret som følger:

Action<in T>

Her angiver in, at instanser af T kun bliver brugt som input til metoden, og dermed kan vi tillade følgende:

Action<Monster> write_monster = m => { Console.WriteLine(m); };
Action<Vampire> write_vampire = write_monster;

Eftersom Vampire er en specialisering af Monster, kan vi betragte en metode, der tager en instans af Vampire som input, som en gyldig variant af en metode, der tager en instans af Monster som input, eftersom alle instanser af Vampire også er en instans af Monster, og dermed har vi fået en kontravariant version af Action<T>.

Afrundning og næste punkt på programmet

Ko- og kontravarians er kun understøttet for interfaces og delegate-typer, og der skal naturligvis findes en gyldig referencekonvertering mellem de involverede typer, for at det kan lade sig gøre. Ydermere er ko- og kontravarians ikke understøttet for ref og out.

Der er flere detaljer omkring varians, men jeg runder min gennemgang af her. Det er ikke nødvendigt, at forstå samtlige finurligheder for at glæde sig over, at visse oplagte konstruktioner nu er understøttet.

Dermed er vi nået til enden af min gennemgang af nyheder i C# 4.

Jeg er ved at forberede en TechTalk om PFX (tag jer ikke af overskriften, der er et eller andet galt med Microsofts event-system), så de næste mange indlæg kommer til at omhandle parallel programmering og de nye muligheder i .NET 4 og VS2010.

Nyheder i C# 4 – sjette del: Bedre integration med COM

Thursday, July 22nd, 2010

Mens vi venter på afgørelsen af kodegolfkonkurrencen (du kan stadig nå at indsende bidrag, så go go go!), fortsætter jeg min serie om nyheder i C# 4.

Som vi så på i forrige indslag i serien, har det tidligere været lidt af et helvede at kalde Microsoft Office COM objekter via C#. Dynamiske typer samt valgfri og navngivne parametre hjælper i den sammenhæng, men heldigvis er Microsoft gået endnu længere for at gøre kode, der arbejder mod disse grænseflader lettere at arbejde med. Udover de nævnte features tilbyder C# 4 følgende til bedre COM-integration:

  • No-ref
  • Index properties
  • No PIA

Lad os se på et eksempel. Skal vi f.eks. starte Word og åbne et nyt dokument, så koden før C# 4 ud nogenlunde som følger:

private void OpenWord() {
   // instance needed due to ref
   object missing = Type.Missing;
   object index = 1;
   var word = new Word.Application();

   word.Documents.Add(ref missing, ref missing, ref missing, ref missing);

   // pseudo index property
   Document = word.Documents.get_Item(ref index);

   word.Visible = true;
}

Læg mærke til at COM-grænsefladen kræver, at alle parametre overføres via ref, og derfor er vi nødt til at oprette lokale referencer – også selvom vi egentlig ikke er interesseret i at overføre en værdi i flere af tilfældene. Det betyder også, at når vi skal overføre værdien 1 til get_Item(), er vi nødt til at oprette en reference til en boxed int med værdien 1, så vi kan overføre denne som en reference.

Heldigvis er der hjælp at hente i C# 4. No-ref fjerner behovet for at skrive alle de omfattende ref-overførelser af parametre. Selve kaldet er naturligvis ikke ændret, da grænsefladen nu engang ser ud, som den gør, men compileren sørge for at generere den nødvendige kode på baggrund af en velkendt syntaks. Ovenstående metode ser således ud som følger i C# 4.

private void OpenWord() {
   var word = new Word.Application();

   word.Documents.Add();
   Document = word.Documents[1];

   word.Visible = true;
}

Læg mærke til, at alle de kedelige ref-kald er væk. De overflødige parametre er ligeledes fjernet, og compileren tillader nu, at vi kan nu kalde get_Item(), som om det var en almindelig index property. Alt i alt får vi en syntaks for COM-interaktion, der ligner det vi er vant til fra interaktion med almindelige .NET typer.

Det bringer os til sidste nye feature i denne sammenhæng: No-PIA, men inden vi ser på den, er det nok på sin plads at forklare, hvad PIA er. PIA står for Primary Interop Assemblies, og er en betegnelse for specielle assemblies, der erklærer .NET-typedefinitioner for eksisterende COM-typer. Det er med andre ord den grænseflade, vores .NET applikation bruger til at kommunikere med COM-objekter i f.eks. Microsoft Office.

For at kunne tilgå de ønskede COM-objekter, er vi altså nødt til at have det eller de relevante PIAs til rådighed. Hvert PIA indeholder typedefinitioner for alle de relevante COM-typer for den givne applikation, så de kan blive ganske omfattende. Det vil sige, at selv hvis vores applikation kun har brug for en lille del af f.eks. Word eller Excel, er vi nødt til at inkludere de komplette PIAs for disse. Det kan blive et problem i visse sammenhænge.

Med No-PIA sørger compileren for at inkludere de nødvendige typedefinitioner i vores assembly, hvilket har to konsekvenser. For det første behøver vi ikke længere at bekymre os om distribution af PIAs og for det andet, slæber vi ikke rundt på definitioner for samtlige typer. Det gør distribution af vores applikation mere smidig.

I næste og sidste indlæg i denne serie skal vi se på varians.

Nyheder i C# 4 – fjerde del: ExpandoObject og DynamicObject

Monday, June 28th, 2010

Efter en længere pause som følge af en svær lungebetændelse, fortsætter jeg her med fjerde indlæg i min serie om nyhederne i C# 4.

I de foregående indlæg har vi set på motivationen for dynamiske typer, eksempler på brug af dynamiske typer og den underliggende infrastruktur for dynamiske typer. I dette indlæg er vi kommet til to nye, dynamiske typer i .NET 4, ExpandoObject og DynamicObject.

ExpandoObject

ExpandoObject er en type, der tillader, at vi kan modificere egenskaberne for konkrete instanser. Opretter vi en dynamisk instans af ExpandoObject, kan vi således tilføje properties, metoder og så videre til denne. Her er et eksempel, hvor vi tilføjer en property ved navn Text:

dynamic expando = new ExpandoObject();
expando.Text = "hello world";

Herefter kan vi tilgå denne property på instansen på almindelig vis, og ganske som forventet udskriver nedenstående teksten hello world.

Console.WriteLine(expando.Text);

Denne egenskab gør, at vi kan skrive kode, der i nogle tilfælde bliver betydelig mindre omfattende, end vi er vant til. Lad os se på et eksempel.

Når vi arbejder med XML, er konstruktioner som nedenstående hyppige:

var movie = new XElement("Movie",
   new XElement("Title", "Blade Runner"),
   new XElement("Release", "1982"),
   new XElement("Cast",
      new XElement("Actor", "Harrison Ford"),
      new XElement("SupportingActor", "Rugter Hauer"),
      new XElement("Actress", "Sean Young"),
      new XElement("SupportingActress", "Daryl Hannah")
   )
);

Console.WriteLine((string)movie.Element("Cast").Element("Actor"));

Ved hjælp af Element(), kan vi udvælge specifikke dele af vores XElement-struktur. Dette er blot en af flere varianter over samme tema, men de fleste af dem fungerer ved, at vi bruger tekst til at navigere rundt i strukturen. I de tilfælde er brugen af dynamiske typer oplagt.

Ved hjælp af ExpandoObject, kan vi erklære en tilsvarende struktur således:

dynamic movie = new ExpandoObject();
movie.Title = "Blade Runner";
movie.Release = "1982";
movie.Cast = new ExpandoObject();
movie.Cast.Actor = "Harrison Ford";
movie.Cast.SupportingActor = "Rutger Hauer";
movie.Cast.Actres = "Sean Young";
movie.Cast.SupportingActres = "Daryl Hannah";

Fordi vores instans af ExpandoObject tilpasser sig det konkrete behov, kan vi blot tilføje egenskaber til objektet efter behov. Brugen ligner til forveksling den kode, vi ville skrive, hvis vi havde en eksplicit type til håndtering af vores film, men vi har ikke skrevet kode til erklæring af denne type.

Tilgang til dele af objektet ligner ligeledes den kode, vi ville skrive, hvis vi havde en eksplicit type:

Console.WriteLine(movie.Cast.Actor);

Bemærk at som jeg også var inde på i forrige indlæg, er det vigtigt, at instansen erklæres med typen dynamic, da det er denne angivelse, der tillader ExpandoObject at redefinere sig selv efter behov. Havde vi erklæret movie af typen ExpandoObject, havde vi fået en instans med denne statiske type, og dermed afskåret os selv mulighederne for at definere opførslen ved hjælp af det dynamiske metaobjekt.

Lad os udbygge eksemplet lidt. Hvordan ville det se ud, hvis vi havde brug for en samling af film-objekter baseret på ExpandoObject? Her er et bud:

var movies = new List<dynamic>();

movies.Add(new ExpandoObject());
movies[0].Title = "Blade Runner";
movies[0].Release = 1982;

movies.Add(new ExpandoObject());
movies[1].Title = "Alien";
movies[1].Release = 1979;

dynamic movie = new ExpandoObject();
movie.Title =  "Delicatessen";
movie.Release = 1991;
movies.Add(movie);

Ikke mange overraskelser her og ganske som forventet, kan vi bruge LINQ på vores samling af film:

var movies80 = from m in movies
               where m.Release >= 1980 &amp;amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;amp; m.Release < 1990
               select m.Title;

foreach(var m in movies80) {
    Console.WriteLine(m);
}

Som det fremgår, tilbyder ExpandoObject en simplere syntaks for eksemplerne i dette tilfælde. Performance er i begge tilfælde begrænset af de dynamiske opslag, så her adskiller brugen af ExpandoObject sig hverken i positiv eller negativ retning. Det er selvfølgelig værd at understrege, at syntaks ikke er alt, og hvis vi har en rig datamodel i koden, er vi bedre tjent med statiske typer, men ExpandoObject tilbyder et interessant alternativ i specifikke situationer.

DynamicObject

DynamicObject tilbyder langt hen ad vejen den funktionalitet, vi så implementeret via IDynamicMetaObjectProvider i forrige indlæg. Det interessante ved DynamicObject er, at den fritager os for en del af arbejdet med implementering af dette interface ved at udstille et sæt virtuelle metoder, vi kan overstyre efter behov for at få lignende funktionalitet.

Har vi behov for en type, hvor vi kan omdefinere hele eller en del af den gængse opførsel, kan vi således med fordel gøre dette ved at lave en specialisering af DynamicObject.

Den åbenlyse begrænsning ved DynamicObject er, at at vi kun kan bruge den, hvis vi ikke har brug for yderligere specialiseringer. Skal vores type ligeledes arve fra en anden type, er vi nødt til at gå den slagne vej og implementere IDynamicMetaObjectProvider selv. I de resterende tilfælde kan vi med fordel gøre brug af DynamicObjects implementering.

Lad os se på et eksempel.

public sealed class DynamicType : DynamicObject {
   public override bool TryInvokeMember(
      InvokeMemberBinder binder,
      object[] args, out object result) {

      result = null;

      Console.WriteLine("Calling the method {0}", binder.Name);
      return true;
   }
}

Ovenstående type overstyrer TryInvokeMember(), hvilket vil sige, at hver gang, vi kalder en metode på en instans af denne type, vil vi få kaldt ovenstående. Vi kan derfor erklære en dynamisk instans og kalde et antal ikke-eksisterende metoder således:

dynamic d = new DynamicType();

d.Boing();
d.Boom();
d.Tschak();

Resultatet er udskriften Calling the method Boing, Calling the method Boom og Calling the method Tschak. Det er selvfølgelig ikke særlig brugbart i sig selv, men det illustrerer, hvordan vi kan redefinere, hvad det vil sige, at kalde metoder på en instans af denne type.

For et øjeblik siden påstod jeg, at TryInvokeMember() bliver kaldt hver gang, vi kalder en metode på en instans af vores type. Det er ikke helt korrekt. Havde det været korrekt, ville vi kunne implementere eksemplet fra forrige indlæg via DynamicObject, men det kan vi ikke. Kalder vi en metode, der findes på typen, bliver denne kaldt. Så hvis vi eksempelvis gør følgende:

Console.WriteLine(d.GetType());

Får vi udskriften NewsInCS4.DynamicType og ikke Calling the method GetType, som man ellers kunne tro.

Det skyldes, at DynamicObject først undersøger, om en given metode er tilgængelig via objektets statiske type. Så hvis vi kalder en eksisterende metode, kommer TryInvokeMember() ikke ind i billedet, og derfor kan vi ikke bruge DynamicObject til at implementere en type, hvor der er byttet om på to metoder.

DynamicObject tilbyder således stort set de samme muligheder, som hvis vi selv implementerer IDynamicMetaObjectProvider, men da den fritager os for en del af implementeringsarbejdet, vil det i mange tilfælde være lettere at lave en specialisering af DynamicObject end at skulle implementere IDynamicMetaObjectProvider.

Nyheder i C# 4 – tredje del: The Object is King

Thursday, June 17th, 2010

Dette er tredje indlæg i en serie om nyhederne i C#. Foregående indlæg er her og her.

I forrige indlæg antydede jeg, at dynamic tilbyder større fleksibilitet end Reflection, og det skal vi se på i dette indlæg.

Sammen med C#s nye dynamic nøgleord er der ligeledes kommet nogle udvidelser til klassebiblioteket og afviklingsmiljøet for at understøtte de dynamiske egenskaber. Formålet er at understøtte et koncept, Microsoft har kaldt ”The Object is King”. Ideen er at introducere en abstraktion for, hvad det vil sige, at gøre noget på et objekt. Centralt i denne sammenhæng er det nye interface IDynamicMetaObjectProvider.

Hvis en type implementerer dette interface, betyder det, at typen selv håndterer, hvad det vil sige, at udføre forskellige operationer på instanser af typen. Mere specifikt betyder det, at typen kan returnere en instans af DynamicMetaObject, og det er denne instans, der står for nævnte funktionalitet. Ved at overstyre metoder i DynamicMetaObject, kan vi således definere objektets eksakte grundopførsel.

Lad os se på et eksempel, men lad os først få et par aspekter omkring statiske typer på plads. Betragt nedenstående metode:

public void SomeMethod(SomeType instance) {
   instance.Method();
}

Hvad sker der her? Som udgangspunkt får vi kaldt Method på en instans af typen SomeType. Hvis SomeType er en referencetype, Method er virtual, og det aktuelle objekt, instance peger på, er af en specialisering af SomeType, kan vi få kaldt en variant af Method. I begge tilfælde kan vi dog på oversættelsestidspunktet generere den specifikke kode, fordi C#s typebinding dikterer, hvad der skal ske her. (1)

Lad os nu forestille os, at vi flytter den handling der afgør, at vi kalder Method fra SomeType fra oversættelsestidspunktet til afviklingstidspunktet. Hvis metoden var erklæret som void SomeMethod(dynamic instance) ville det være præcis det, der ville ske, men lad os gå et skridt videre.

Lad os ydermere forestille os, at selve afgørelsen omkring, hvad det vil sige, at kalde en metode er blevet uddelegeret. I så fald er det ikke længere C#s statiske typesystem, der afgør, at effekten af instance.Method() er at kalde koden på SomeType associeret med Method(). I stedet kan vi implementere præcis den opførsel, vi har brug for. Det er, hvad IDynamicMetaObjectProvider tillader os.

Lad os se på et eksempel. Betragt nedenstående klasse:

public sealed class WeirdType : IDynamicMetaObjectProvider {
   public void Foo() {
      Console.WriteLine("Foo");
   }

   public void Bar() {
      Console.WriteLine("Bar");
   }

   public DynamicMetaObject GetMetaObject(Expression parameter) {
      return new WeirdTypeMetaObject(parameter, this);
   }
}

WeirdType har to simple metoder, Foo og Bar, der henholdsvis udskriver teksterne ”Foo” og ”Bar”. Kalder vi dem som følger, får vi helt som forventet udskriften: Foo efterfulgt af Bar.

var wt = new WeirdType();
wt.Foo();
wt.Bar();

(Husk var blot betyder, at compileren sætter typen af wt til den type, den kan udlede af tildelingen – altså WeirdType. Der er ikke noget magi, og det er ikke farligt.)

Derudover implementerer WeirdType IDynamicMetaObjectProvider og har derfor metoden GetMetaObject, som returnerer en instans af WeirdTypeMetaObject. Det betyder, at hvis vi kalder metoder på en dynamisk erklæret instans, vil det være opførelsen i det returnerede metaobjekt, der definerer den tilhørende funktionalitet. Det er lidt af en mundfuld, så lad mig prøve at konkretisere.

Som et illustrativt eksempel kunne vi få metaobjektet til at bytte om på metoderne, så når vi kalder Foo(), afvikles koden for Bar() og omvendt. For at gøre dette, skal vi implementere den nødvendige logik i WeirdTypeMetaObject.

Ændrer vi således ovenstående eksempel fra var til dynamic, vil compileren vide, at den er nødt til at overlade håndteringen af wt til afviklingsmiljøet, og eftersom typen implementerer IDynamicMetaObjectProvider, ved afviklingsmiljøet, at den aktuelle opførsel for instansen er defineret af vores metaobjekt.

For at gøre en lang historie kort: Kører vi nedenstående, får vi udskriften Bar efterfulgt af Foo, fordi vi har redefineret, hvad det vil sige, at kalde metoder på objektet.

dynamic wt = new WeirdType();
wt.Foo();
wt.Bar();

Eksemplet er selvsagt ikke det mest brugbare, men det illustrerer to vigtige pointer:

For det første viser det, at der kan gælde helt andre regler for, hvad det vil sige, at udføre operationer på en instans, når referencen til denne er erklæret som dynamic.

For det andet illustrerer det, at vi kan implementere regler, der ændrer de grundlæggende spilleregler fundamentalt. Det er en stærk feature, der kan åbne nogle døre i specielle situationer, men det er selvfølgelig også noget, der bør bruges med omtanke.

Definitionen af WeirdTypeMetaObject ser ud som følger:

public class WeirdTypeMetaObject : DynamicMetaObject {
   public WeirdTypeMetaObject(Expression expression, WeirdType value)
      : base(expression, BindingRestrictions.Empty, value) {}

   public override DynamicMetaObject BindInvokeMember(
      InvokeMemberBinder binder, DynamicMetaObject[] args) {

      Action action;

      if (binder.Name == "Bar") {
         action = new Action(((WeirdType) Value).Foo);
      } else if (binder.Name == "Foo") {
         action = new Action(((WeirdType) Value).Bar);
      } else {
         return base.BindInvokeMember(binder, args);
      }

      return new DynamicMetaObject(
         Expression.Block(
            Expression.Call(Expression.Constant(Value), action.Method),
            Expression.Default(typeof(object))
         ),
         BindingRestrictions.GetInstanceRestriction(Expression, Value),
         Value
      );
   }
}

Jeg vil ikke komme ind på detaljerne i ovenstående i dette indlæg, men jeg skal nok vende tilbage til det senere, da der er mindst en overraskelse, der kan drille lidt.

Ved at implementere IDynamicMetaObjectProvider får vi komplet kontrol over en types basale semantik. Desværre er det en lidt omstændelig affære at implementere dette interface, og hvis vi ikke har brug for at kunne kontrollere alt, tilbyder .NET to typer, der laver meget af arbejdet for os. I næste indlæg skal vi således se nærmere på DynamicObject og ExpandoObject.


  1. Faktisk ”snyder” Microsoft lidt her, for hvis SomeType er en referencetype genererer compileren et kald via callvirt, uanset om der er tale om en virtuel metode eller ej. Fordi vi kalder en metode via et referenceargument, kan referencen være null og derfor udnytter compileren, at callvirt laver et implicit null-tjek- Af den grund ser vi callvirt her i stedet for call.

Nyheder i C# 4 – andel del

Friday, June 11th, 2010

Dette er andet indlæg i en lille serie om nyhederne i C# 4. I forrige indlæg så vi på udviklingen af C#, og specifikt på motivationen for at udvide sproget med adgang til dynamiske typer. I dette indlæg skal vi se på et konkret eksempel på brug af dynamiske typer.

Så lad os kaste os ud i det. Betragt nedenstående stump kode:

Adder adder = GetAdder();
int result = adder.Add(40, 2);
Console.WriteLine(result);

Det er, som vi kender det. Ingen dynamiske typer. Det er plain vanilla C#, men lad os alligevel bruge et øjeblik på at slå fast, hvad der sker her.

Forudsat at vi har en type kaldet Adder med en metode kaldet Add, der tager to ints og returnerer en int samt en lokal metode ved navn GetAdder, der returnerer en instans af Adder, oversætter dette og fungerer som forventet.

Vi kan udføre det samme på dynamisk vis ved hjælp af Reflection. I så fald ser kode ud som følger:

object adder = GetAdder();
Type adderType = adder.GetType();
object returnObject = adderType.InvokeMember("Add",
   BindingFlags.InvokeMethod, null, adder, new object[] { 40, 2 });
int result = Convert.ToInt32(returnObject);
Console.WriteLine(result);

Ikke alene fylder dette en del mere, det er også sværere at læse, men den grundlæggende funktionalitet er som ovenfor.

Vi har stadig en GetAdder(), men da vi ikke vil lægge os fast på en bestemt type til returværdien, er vi nødt til at betragte den som en instans af object.

Vi vil stadig gerne kalde Add(), men for at kunne gøre det via Reflection, er vi nødt til at have fat i den aktuelle type på returværdien fra GetAdder() først. Via typeobjektet kan vi kalde metoden, men vi er nødt til at beskrive signaturen, hvordan typebinding skal foretages, hvilken instans metoden skal kaldes for samt de relevante argumenter til metoden. Derfor bliver kaldet til InvokeMethod() ret omfattende.

Returværdien er ligeledes repræsenteret via en instans af object, så for at få pillet værdien ud af denne, er vi nødt til at kalde ToInt32(), og herefter er vi i mål.

Ulemperne ved ovenstående er indlysende. Det er tungt at skrive og tungt at læse. Der er adskillige muligheder for fejl på afviklingstidspunktet. Måske har typen ikke en Add(), måske er argumenterne ikke korrekte, måske får vi noget tilbage, der ikke kan konverteres til int.

Dertil kommer, at hver gang vi kalder ovenstående, skal alle disse elementer verificeres, og det tager naturligvis lidt tid.

Hvad er fordelen så? Fordelen er, at ovenstående virker, uanset hvad GetAdder() end returnerer, så længe der er tale om en instans, der har en Add() med den korrekte signatur. Koden er således ikke afhængig af specifikke typer, men derimod specifikke egenskaber ved vilkårlige typer.

Med dynamiske typer får vi større fleksibilitet samt bedre ydelse med en syntaks, der ligger meget tæt op ad den vi kender fra statiske typer. Med dynamiske typer ser koden således ud:

dynamic adder = GetAdder();
dynamic result = adder.Add(40, 2);
Console.WriteLine(result);

Nu ligner koden til forveksling, det vi kender med den ene forskel, at vi har udskiftet de statiske typer med det nye nøgleord dynamic. Ligesom Reflection-eksemplet fungerer dette, uanset hvad GetAdder() end returnerer, så længe den returnerede instans kan honorere kaldet til Add(). Det er fleksibelt og letlæseligt.

Vi har naturligvis stadig samme faldgruber som ved Reflection. Hvis metoden ikke findes, får vi en fejl på kørselstidspunktet. Hvis argumenterne til metoden ikke passer, får vi ligeledes en afviklingsfejl. I teorien kunne vi også få en fejl, hvis WriteLine ikke kunne kaldes med den konkrete instans af result, men eftersom WriteLine() ender med at kalde ToString(), som instansen får fra object, er der ingen ko på isen her.

Den tilgængelige syntaks er ikke den eneste fordel. I vores Reflection-eksempel betaler vi prisen for dynamikken ved hvert kald, så hvis vi kalder ovenstående i en løkke, kommer programmet til at lave det samme arbejde mange gange. Vi kan selvfølgelig gemme resultatet af forarbejdet og genbruge det i løkken, men det kræver mere kode.

Med DLRen får vi dette forærende af selve afviklingsmiljøet i form af call site caching, så når den specifikke Add() metode er blevet identificeret for vores aktuelle instans, gemmes denne oplysning og genbruges automatisk, hvis vi f.eks. kalder Add() flere gange i en løkke.

Okay, er humlen så, at vi blot har fået pænere og hurtigere Reflection? Ikke helt for dynamic kan mere end Reflection, men det ser vi på i næste indlæg.

Nyheder i C# 4 – første del

Friday, June 4th, 2010

Dette er første indlæg i en lille serie omkring nyhederne i C# 4.

Med frigivelsen af Visual Studio 2010, har vi også fået adgang til den nyeste version af C#, men der er meget mere end det på tapetet. Ikke alene har Visual Studio gennemgået en større om- og udbygning. .NET afviklingsmiljøet, klassebiblioteket og C# er alle ligeledes blevet opdateret med nye features.

Jeg vil i første omgang begrænse mig til nyhederne i selve C# sproget, men i senere indlæg vil jeg også komme ind på nogle af nyhederne i klassebiblioteket.

Nyhederne i C# 4 er:

Men først …

Lidt historie

C# har gennemgået en omfattende udvikling gennem årene. Den første version kom i 2002 og blev af mange betragtet som en Redmond-version af Java. Sprogene lignede da også hinanden til forveksling, men de begyndte allerede at divergere i 2005, da version 2 tilføjede generics til ikke bare sproget men også til selve afviklingsmiljøet. Dermed adskilte C# sig fra både Java og C++ på dette punkt.

C#s udvikling

I 2007 introducerede Anders Hejlsberg og Co. Language INtegrated Query, LINQ, og dermed fik C# et sæt ret unikke features, der var med til at lægge afstand til Java.

Seneste skud på stammen er muligheden for at arbejde med dynamiske typer. Så til alle dem, der råbte vagt i gevær omkring brugen af var: Det er nu, I skal være bange.

Sammenlignet med de to forrige versioner af C#, er denne nyhed muligvis ikke så revolutionerende. Har man brug for at arbejde med dynamiske typer, vil man værdsætte de nye tiltag, men hvis alle ens udfordringer kan løses med statiske typer, vil de nye muligheder muligvis kun bidrage som ammunition i den verserende skyttegravskrig mellem tilhængere af henholdsvis dynamiske og statiske sprog.

I modsætning til generics og LINQ vil dynamiske typer ikke ændre den gængse brug af sproget, men det er et værktøj, der er særdeles anvendelig i visse situationer.

Dynamiske typer

Hvad er dynamiske typer, og hvad er der sket med .NET for at imødekomme brugen af disse?

Svaret på det første spørgsmål er, at dynamiske typer er typer, hvor undersøgelser og beslutninger omkring typernes egenskaber foretages på afviklingstidspunktet i stedet for på oversættelsestidspunktet.

Så de tjek, compileren normalt laver som en del af oversættelsen, laves i stedet på afviklingstidspunktet. Det vil selvfølgelig også sige, at de fejl, der under normale omstændigheder ville give fejl under oversættelsen, giver nu fejl under kørslen i stedet.

Det er der ikke noget nyt i. Konceptet findes allerede i mange andre sprog som f.eks. Javascript, Perl, Python, Ruby og mange andre. Det nye består i, at C# nu tillader, at typer kan opføre sig på denne måde, og vi kan således have kode, der benytter såvel statiske som dynamiske typer.

Svaret på det andet spørgsmål er lidt mere omfattende. CLRen har nemlig fået et sæt af udvidelser, der tilsammen betegnes Dynamic Language Runtime eller blot DLR.

Dynamic Language Runtime

Overordnet set tilbyder DLRen i skrivende stund tre komponenter:

Dynamic dispatch er den mekanisme, der gør den dynamiske håndtering mulig. I C# er det repræsenteret ved dels et nyt reserveret ord, dynamic samt et interface, IDynamicMetaObjectProvider, der angiver, hvordan det dynamiske skal opføre sig. Hvis en type implementerer dette interface, er det et signal til afviklingsmiljøet om, at der gælder specielle regler for, hvad det vil sige at udføre operationer på instanser af denne.

Expression trees er som sådan ikke nye, men de er blevet udvidet til at kunne håndtere de nødvendige sprogkonstruktioner.

Call site caching er en optimering, der gør, at omkostningerne ved dynamiske opslag på afviklingstidspunktet reduceres. Første gang en metode kaldes på et dynamisk objekt, er afviklingsmiljøet nødt til at finde ud af, hvad det vil sige, at kalde denne metode. Denne operation tager tid, så derfor gemmer afviklingsmiljøet resultatet, så det kan genbruges for lignende operationer.

Med de faciliteter DLRen stiller til rådighed, er det let at implementere nye dynamiske sprog i .NET-verden. De to mest populære for tiden er nok IronPython og IronRuby.

Det essentielle er dog ikke, at vi har fået et par nye, hippe sprog. Den store nyhed er, at afviklingsmiljøet nu tilbyder en platform til implementering af disse eller dele deraf. Da de dynamiske faciliteter er en del af infrastrukturen, får vi en masse af den grundlæggende forærende, og det er ret interessant. På samme måde som CLRen giver os en fælles platform på tværs af sprog, giver DLRen os nu et fundament til at bygge dynamiske systemer.

Vi kan afvikle kode skrevet i dynamiske sprog som IronPython eller IronRuby, vi kan interagere med dynamiske miljøer som COM, DOM og så videre, og vi kan lade dele af vores system implementere en dynamisk adfærd, som vi definerer. Alt dette vil jeg se nærmere på i de efterfølgende indlæg.

ANUG Visual Studio 2010 Launch Event

Friday, May 21st, 2010

Jeg tog en fridag fra min ferie i går og tilbragte dagen i selskab med over 200 andre .NET-entusiaster til ANUGs Visual Studio 2010 Launch Event i Århus. Det var en rigtig god dag med masser af interessant indhold på programmet og god mulighed for networking.

Jeg holdt en lettere reduceret udgave af min Nyheder i C# 4 Tech Talk. Det gik stærkt, da jeg kun havde 45 minutter, men jeg har fået mange positive tilbagemeldinger, så det gik åbenbart ganske fint.

Dagens bedste indlæg var efter min mening Rasmus Kromann-Larsens introduktion til Resharper 5. Demoerne var velvalgte, lærerige og ikke mindst underholdende. Rasmus udviste godt overblik og præsentationen forløb rigtig godt.

Jeg synes også, at Rasmus Wulff Jensens indlæg om nyheder i Visual Studio 2010 fortjener at blive fremhævet. Han havde ligeledes godt styr på materialet, kom fint rundt i nyhederne og havde gode eksempler til at illustrere det hele med.

Dagens største skuffelse var desværre det indlæg, jeg havde set mest frem til. Carsten Juel Andersens indlæg om Task Parallel Library var desværre noget rodet, og der var for mange unøjagtigheder til min smag. Eksempler var fint præsenteret, men desværre ikke alle lige velvalgte. Pointen med en thread safe collection er jo ikke, at man kan gennemløbe den, samtidig med at man modificerer den. Og så er thread safe altså noget man ligesom graviditet enten er eller ikke er. Man kan altså ikke være hverken 30% gravid eller delvis thread safe.

Det ændrer dog ikke ved, at arrangementet som helhed var glimrende og udførelsen i top. Jeg havde tilmed fornøjelsen at spise aftensmad med et par af arrangørerne og de andre taler, hvilket var en god afslutning på en udbytterig dag.

Stor tak til ANUG og de mange fremmødte.

Der er yderligere indtryk fra dagen på ANUGs hjemmeside.