C#s framtida finesser (Generics Klasser)
Förord
Som alla vet är C# ett modernt innoativt programmeringsspråk. För att hålla sig fast vid detta så har Microsoft introducerat flera nya potentionella finesser för att öka utvecklingens produktivitet. Sedan 2001 då C# introducerades har man använt C# för att bygga en hel del applikationer så som .Net Ramverk, MSN Web properties och Tablet PC SDK m.m. Vilket bevisar sig självt att det är ett språk som passar bra till högkvalitéts mjukvaror.Innehåll
»»
»
Nyheter i nästa C# version
Många av finesserna i C# är baserade på olika mål. Så som Förenat typsystem och det förenklade sättet value- och referenstyperna används i språket. Kompnentbaserad design, genom finesser som exempelvis Attribut, händelse (Events), delegat(Delegates). Andra mål är även sättet hur C# hjälper utvecklarna med överflödes kontroller med mera. För att öka utvecklarnas produktivitet har man förenklat olika statements, så som foreach, using etc... I nästa version av C# har man planerat att bygga in nya eleganta syntaxer för att öka produktiviteten, prestandan ännu mera. Bland dessa finesser finns generics,iterators,anonyma metoder och partial types. Jag tänkte här förklara kort hur Generics fungerar.
Generics
Projekten blir mer och mer avancerade vilket ställer mer krav på återanvändbarhet och specialtillverkade mjukvaror.För att uppnå en sådan hög nivå av kod återanvändning har man lagt till en finess som kallas generics. Vilket skilljer sig lite från det många känner till från språk som C++, det som kallas för Templates.
Redan idag kan man bygga enkla generic klasser i C#. Dock är man tvungen att använda sig av objekt typen. Tack vare att objekten i C# ärver objekt typen kan man eneklt använda sig av boxing samt unboxing.
Programmerare kan spara både referens- och värdetyper i en variabel av typen objekt. Nackdelen är dock konverteringen mellan de olika typrna.
Låt oss ta en titt på ett exempel ang detta. Tänk dig en enkel Stack class med två metoder ´Push´ och ´Pop´. För att vår Stack skall kunna hantera vilken typ som helst använder vi oss av objekt typen. Ex:
public class Stack
{
//Skapar en object array som är 100 poster stor
private object[] items = new object[100];
//Vår Push tar object som inputparameter
public void Push(object data)
{
...
}
//Vår Pop returnerar det object som kom in sist.
public object Pop()
{
...
}
}
Som många känner till så är en stack en LIFO (Last In Först Out).
Låt oss säga att vi nu har en klass som heter Order, och vi vill spara undan order klasser i vår stack skulle syntaxen se ut så här:
Stack s = new Stack();
s.Push(new Order());
Sedan vill vi plocka ut sista sparade ordern, detta gör vi genom vår Pop metod. Kom ihåg att vi hade typen objekt i vår Stack, för att kunna plocka ut vår Order så vi kan använda dess attribut samt metoder behöver vi göra en casting:
Order c = (Order) s.Pop();
Om vi skulle använda oss av en värde typ så som integer I vår Push metod skulle denna automatiskt under runtime konverteras till en referens typ. Genom boxing. Vill vi sedan plocka ut vår värde typ skulle en unboxing ske:
Stack s = new Stack();
s.Push(3); //3:an boxas om till en referenstyp.
int i = (int) s.Pop(); //3:an unboxas från referenstyp till en värde typ.
Boxing samt unboxing kan vara en krävande process för prestandan m.m.
Nu till Generics.
Gerrics i C# ger oss datastrukturer som blandannat ökar prestandan. De använder sig av något som kallas för parameter typer “ parameterized types´. Syntaxen för dessa typer skrives med samma notation som C++. Nämligen med (< och >) som en tag i Html. När man använder en klass med dessa parametrar måste man ersätta dem med den typ man vill avvända i klassen.
Vi skall ta en titt på hur detta ser ut. Vi tar får Stack klass igen. Tidigare angav vi att vår Stack använde sig av typen object, vilket innebar att vi var tvugna att nyttja konverteringar så som Casting,boxing,unboxing.I exemplet här nedan anger vi en typ parameter kallad ItemType, deklarerad med
Public class Stack
Istället för att tvinga fram konventeringar kommer vår stack klass att använda sig av den typ vi anger vid instansiering. Parametern ItemType aggerar ungefär som en proxy tills typen är specifierad vid instansiering och används som typ till vår interna items array lika så kommer Push och Pop metoderna att då den typ vi instansierat med. Vi bygger om vår Stack klass till en Generic klass:
public class Stack
{
private ItemType[] items;
public void Push(ItemType data)
{
...
}
public ItemType Pop()
{
...
}
}
När vi sedan använder vår Stack klass, som exemplet nedan, så har vi sagt åt den att använda int typen. I detta fall säger vi åt Stack klassen att använda den primitiva typen integer genom att använda < > notationen vid instansiering.
Stack stack = new Stack;
stack.Push(3);
int x = stack.Pop(); //Ingen boxng krävs
När vi gör detta kommer alla
Stack stack = new Stack;
stack.Push(new Order());
Order c = stack.Pop(); //Ingen Casting krävs
Dock låser vi vår Stack instans till att använda just Order typen eller vad vi nu väljer för typ. Skulle vi ange en annan typ till vår Push metod skulle vi få kompilerings fel:
Stack stack = new Stack;
stack.Push(new Order());
stack.Push(3) // Kompilerings fel
Order c = stack.Pop();
Detta skulle dock vara möjligt I vår Stack klass med typen object. Generics kontrolleras även under kompilering. Tack vare Generics kan vi minska utvecklingstid samt kod. Vi behöver exempelvis inte bygga en Stack impelemntation för varje typ utan återanvänder vår Generic klass. Vi kan även i en Generic klass ha flera parametrar. Exempelvis en Dictionary klass:
public class Dictionary
{
public void Add(KeyType key, ValType val)
{
...
}
public ValType this[KeyType key]
{
...
}
}
Instansieringen ser ut på följade sätt:
Dictionary dict = new Dictionary;
dict.Add(3, new Order());
Order c = dict.Get[3];
Man kan även använda sig av restiktioner (Constraints) till Generic klassena. Varför behöver vi använda oss av restriktioner?
Låt säga att i vår Add metod till Dictionary klassen vill använda oss av CompareTo. Typ så här:
public class Dictionary
{
public void Add(KeyType key, ValType val)
{
...
switch(key.CompareTo(x))
{
}
...
}
}
Detta kommer tyvärr ge fel vid kompilering då kompilatorn tolkat KeyType efter object som har metoder så som ToString m.m. men inte CompateTo. Vad vi kan göra är att vi gör en casting på vår Key i switchen. Med ICompareble interfacet. Detta förutsätter att man har implementerat Icomparable i den typ man använder sig av som KeyType:
public class Dictionary
{
public void Add(KeyType key, ValType val)
{
...
switch(((IComparable) key).CompareTo(x))
{
}
...
}
}
För att slippa denna casting kan vi redan efter class syntaxen använda oss av syntaxen where. Här gäller samma regler som vid arv. Man kan ange flera iterfaces men endast en klass. Låt oss ta en titt på detta. Vi har vill fortfarande använda vår CompareTo medod. Och den skall användas av den typ som ersätter KeyType. Vi kan då enkelt ange en restriktion (constraint) i vår Generic klass så här:
public class Dictionary where KeyType : IComparable
{
public void Add(KeyType key, ValType val)
{
...
switch(key.CompareTo(x))
{
}
...
}
}
Vi säger åt kompilatorn att KeyType måste ha Icomparable interfacet för att fungera med vår Generic klass. Låt oss säga att vår Order inte har Icomparable interfacet implementerat då skulle följande rad ge kompileringsfel:
Dictionary
Samt så slipper vi använda Casting när vi vill använda special metoder som inte ingår i object typen. Som jag även tidiagare förklarade kan man ange flera restriktioner:
public class Dictionary where
KeyType : IComparable,
KeyType : IEnumerable,
ValType : Order
{
public void Add(KeyType key, ValType val)
{
...
switch(key.CompareTo(x))
{
}
...
}
}
Här säger vi åt KeyType måste ha Icomparable samt IEnumerable implementarade och ValType klassen Order.
Sammanfattning
Generic klasser är inget unikt för bara C#. Andra programmeringspråk har sedan tidagare haft stöd för liknande saker. Enda som skilljer är lite syntax och hur de hanteras av kompilatorn eller under runtime. C++ har sina templates, Java kommer också snart få stöd för Generic klasser. Dock kommer man inte bygga om JVM (Java Virtuall Machine) så värde typer kommer inte att fungera under Javas implemenation vilket går att göra i C#. Det finns mer saker att skriva om Generic klasser, ex hur de hanteras under runtime etc... Men det lämnar jag åt framtida arkitklar. I nästa artikel kommer jag kort skriva om hur Generic klasser hanteras under runtime. Efter det kommer jag att ha 3 ytterligare artiklar om nyheter. Så som Itterationer, Anonyma klasser samt Partial types. Håll ut till dess.
OBS! Vill bara informera att dessa finesser är fortfarande under utveckling och kan vara ändrade då de blir en final.
Mvh Johan Normén
Källa:
Johan Normén
Hej. Ni som sätter betyg får gärna komma med konstruktiv kritik så jag vet vad man kan trycka på för att göra det hela bättre. Tack! Mvh Johan
Johan Wendelstam
Bra förklaringar, korta och enkla utan att krångla till det i onödan.
Mathias Olsson
Jag skulle också vilja påpeka att särskrivning faktiskt försvårar läsandet. Man måste liksom läsa meningen och sedan tyda den, istället för att förså på en gång när man läser...lixom. Artikeln var precis vad jag behövde. Har stött på uttrycket "generic" massor av gånger, men aldrig vetat vad det har varit. Jag tappade lite på slutet, men det beror nog mer på några glas vin, än författarens pedagogiska kunskaper ;) Jag gillar också korta, enkla förklaringar. Tack. /Mathias
Johan Normén
Så sant jag är ingen skribent... Ville dela med mig av lite roliga saker. Suger när det gäller skriva... där av undviker jag det så ofta det går... Att bara läsa engelesk litteratur underlättar inte heller då allt särskrivs och det sitter mer i ryggmärgen att skriva ENG än svenska så det blir lätt "Onda fällan" :-( Mvh Johan