© 2011,
Flemming Koch Jensen
Alle rettigheder forbeholdt |
Introduktion |
C# (udtales: "C-sharp") er udviklet af Anders Hejlsberg som også stod bag Turbo Pascal og dens efterfølgende store succes: Delphi. Sproget er udviklet til Microsoft; hvor det indgår i deres .NET platform.
1. .NET Framework'et
Først og fremmest er .NET Framework'et en fælles platform for de fire sprog som Microsoft vælger at satse på: C#, Visual C++1, Visual Basic og JScript. Denne platform består i al væsentlighed af to dele:
CLR - Common Language Runtime
CLR står for afviklingen af programmerne. Dette skal ikke forstås som, at man anvender bytecode som i Java, og at CLR er en interpreter der fortolker programmerne. CLR er snarere et Runtime Environment, der stiller programafvikling til rådighed, og giver mulighed for at programmer kan bruge hinanden, selv om de er skrevet i forskellige sprog.
BCL - Base Class Library
BCL er program-bibliotekerne - dem man fra Java kender som f.eks. java.io. Disse er fælles for alle fire sprog. Det betyder at kapitlet om Collection klasser, i virkeligheden ikke kun drejer sig om C#. De container klasser man kan lave med System.Collections kan også bruges i de andre sprog.
Der er andre centrale elementer, der spiller en rolle i forbindelse med .NET
1.1 Assemblies
DLL eller EXE En assembly kan være en DLL- eller en EXE-file. Ud over det sædvanlige indhold i en sådan file, indeholder den også metadata, der beskriver indholdet. Man kan sammenligne en assembly med en package i Java. Ligesom man i Java kan loade en klasse dynamisk, kan man i C# loade hele assemblies, undersøge hvilke klasser de indeholder, og vælge at bruge nogle af dem. CLS En assembly's dele kan være skrevet i forskellige sprog. Man kan endog lade en klasse skrevet i et sprog nedarve fra en skrevet i et andet sprog. For at klasser skrevet i forskellige sprog skal kunne bruge hinanden, skal de overholde CLS (Common Language Specification). CLS kan betegnes som en laveste fællesnævner for de fire sprog. Ting der public må ikke være af en sådan beskaffenhed, at det ikke giver mening i et af de andre sprog. F.eks. må man ikke have public ting der er unsigned, da unsigned ikke findes i alle fire sprog.
1.2 Intermediate Language
IL Intermediate Language (IL) er et fælles mellemsprog som alle compilere (for de fire sprog) bruger. I forbindelse med compilerkonstruktion har det altid være en idé, at have et fælles intermediate sprog, da man så kunne anvende den samme kodegenerator, og dermed den samme optimizer. Dette gøre det enklere at lave compilerne, samtidig med at man kan koncentrere arbejdet om kodeoptimering. Ikke bytecode Man skal ikke forstå IL som en slags bytecode - det er det ikke! Hvis man skulle sammenligne det med noget, er det nærmere en slags assembler - et sprog der består af primitive/basale konstruktioner. Følgende er et mindre brudstykke af et program skrevet i IL.
IL_001e: ldstr "Message:\n {0}" IL_0023: ldloc.0 IL_0024: callvirt instance string [mscorlib]System.Exception::get_Message() IL_0029: call void [mscorlib]System.Console::WriteLine(string, object) IL_002e: ldstr "Stack Trace:\n{0}" IL_0033: ldloc.0 IL_0034: callvirt instance string [mscorlib]System.Exception::get_StackTrace() IL_0039: call void [mscorlib]System.Console::WriteLine(string, object) IL_003e: ldstr "Help Link:\n {0}" IL_0043: ldloc.0 IL_0044: callvirt instance string [mscorlib]System.Exception::get_HelpLink() IL_0049: call void [mscorlib]System.Console::WriteLine(string, object) IL_004e: leave.s IL_0050Vi vil ikke komme nærmere ind på kodens betydning.
Afhængig af .NET Framework Eftersom C# umiddelbart kan compileres til EXE-filer, skulle man tro at man kunne udføre et sådant program på enhver PC med Windows, men det er ikke tilfældet! .NET Framework skal være installeret, ellers terminerer programmet med det samme — uden videre indikation.
2. Hello World
Traditionen tro er det første eksempel altid det berømte "Hello World":
using System; public class HelloWorld { public static void Main() { Console.WriteLine( "Hello World" ); } }Namespaces Den første linie siger at vi vil bruge namespace System. I dette namespace finder vi klassen Console med den statiske metode WriteLine, som vi bruger til at udskrive "Hello World". Et namespace svarer lidt til en package i Java - mere om det senere. Koden ligner meget den tilsvarende i Java. Man bemærker at Main skrives med stort, returnerer void og ikke tager nogen parametre. Casing Konvensionen med navngivning og casing er stort set den samme som i Java - dog starter man altid metode-navne med stort. Det er en fordel at holde sig til denne konvension, da ens kildetekst vil indeholde metodekald af både ens egne metoder og metoder fra C#'s biblioteker. Hvis man derfor skrev sine egne metodenavne med lille startbogstav, ville kildeteksten blive mere forvirrende at læse. Det skal forøvrigt bemærkes at C#, ligesom C++/Java er case-sensitive. Main kan laves i forskellige udgaver. F.eks. kan man lade den returnere en int, der fortolkes som en termineringskode til operativsystemet:
using System; public class HelloWorld { public static int Main() { return 1; // 1 fortæller at noget gik galt! } }Kommentarer Da vi her har en kommentar i koden, skal det kort bemærkes at kommentarer skrives på samme måde som i C++ og Java. Det er også muligt at skrive specielle kommentarer der danner grundlag for dokumenterende XML-filer, i stil med Java's javadoc - mere om det senere. Man kan også lade den tage kommando argumenter som parameter. F.eks.:
using System; public class HelloWorld { public static void Main( string[] argv ) { for ( int i=0; i < argv.Length; i++ ) Console.WriteLine( argv[ i ] ); } }Hvis man f.eks. kalder programmet med:
hello_world.exe dette er en testvil det give følgende udskrift:
dette er en testMan bemærker at programmet navn ikke er med som det første element i array'et, ligesom i Java, men i modsætning til C++. Bemærk at man finder længden af et array på samme måde som i Java, ved at skrive .Length efter array'et navn (selvfølgelig med lille i Java). I C# er der en endnu smartere måde at formulere denne for-sætning på:
using System; public class HelloWorld { public static void Main( string[] argv ) { foreach ( string arg in argv ) Console.WriteLine( arg ); } }Vi vil senere se på hvilke krav der stilles for at man kan bruge denne specielle variant af for-sætningen, men indtil videre noterer vi os, at den kan bruges til gennemløb af arrays. Java har senere også fået en tilsvarende foreach-løkke, med næsten samme syntaks.
2.1 Console.WriteLine
"Hello World"-eksemplet er naturligvis ikke noget specielt godt eksempel, men det giver os alligevel anledning til med det samme at se lidt på mulighederne for simpel udskrift til skærmen. "klistre-plus" Ovenfor er anvendet Console.WriteLine, der meget ligner Java's System.out.println. Ligesom i Java kan man anvende "klistre-plus" når man opbygger en tekststreng, men i forbindelse med udskrift har man i C# også en anden mulighed. Denne mulighed ligner meget C's måde at gøre tingene på (med stdio.h). Lad os se et eksempel:
using System; public class Test { public static void Main() { int x=5; int y=3; Console.WriteLine( "({0}, {1})", x, y ); } }
(5, 3)Den statiske metode WriteLine er erklæret i klassen Console som findes i det namespace, der hedder System, som vi anfører i første linie. Formatering Metoden tager et variabelt antal parametre; hvoraf den første er en tekststreng, der beskriver formatet. I formatet indgår der en række symboler {0} og {1}, som beskriver hvor i tekststrengen de efterfølgende parametre skal indsættes. Vi skal ikke her beskrive mulighederne med formatering (der er mange), men blot konstatere at vi med {x}; hvor x = 0, 1, 2, ... , kan indplacere de værdier vi ønsker at udskrive. Den første parameter efter formatet har nummer 0, osv. Man er ikke bundet af rækkefølgen af parametrene, og kan lave gentagelser:
Console.WriteLine( "{1} - {1} - {0}", x, y );
3 - 3 - 5
Ønsker man ikke noget efterfølgende linieskift har C# også Console.Write, svarende til Java's System.out.print.
3. Metoder
Metoder fungerer i alt væsentlighed som man kender dem fra Java, men der er nogle ekstra muligheder. En af mulighederne: delegater - eller populært sagt: funktions-pointere, skal vi studere senere - andre vil vi se nærmere på her:
3.1 Reference-parametre
ref Reference-parametre laves ved at sætte det reserverede ord ref foran både den formelle og den aktuelle parameter - dvs. både i erklæringen af parameteren og i kaldet. F.eks.:
using System; public class Tal { int x; public Tal( int x ) { this.x = x; } public void getX( ref int x ) { x = this.x; } } public class Test { public static void Main() { Tal t = new Tal( 5 ); int value=0; t.getX( ref value ); Console.WriteLine( value ); } }Bemærk at vi her initialiserer value til 0. Havde vi ikke gjort det, ville vi have får en compilerings-fejl - idet en aktuel ref-parameter skal have en værdi. out Da reference-variable kan bruges til at lave multipel returnering (returnering af flere værdier), kan det være bekvemt at kunne sende en ikke-initialiseret variabel med som reference-parameter, som metoden så skal sætte til en (retur-)værdi. Dette kan gøres ved i stedet at bruge det reserverede ord out:
using System; public class Tal { int x; public Tal( int x ) { this.x = x; } public void getX( out int x ) { x = this.x; } } public class Test { public static void Main() { Tal t = new Tal( 5 ); int value; t.getX( out value ); Console.WriteLine( value ); } }Når man bruger out, bliver det til gengæld metodens pligt at sætte variablen til en værdi - ellers får man en compiler-fejl.
3.2 Variabelt antal parametre
Dette kendes fra C++, og savnes i Java. I C# laves det med det reserverede ord: params. Lad os se et eksempel:
using System; public class Printer { public void Print( string s ) { Console.WriteLine( s ); } public void Print( params string[] args ) { for ( int i=0; i < args.Length; i++ ) Console.Write( args[ i ] + " " ); Console.WriteLine(); } } public class Test { public static void Main() { Printer p = new Printer(); p.Print( "Test" ); string[] args = { "Dette", "er", "en", "test" }; p.Print( args ); p.Print( "Dette", "er", "en", "test" ); } }
Test Dette er en test Dette er en testNår C# skal finde den metoder der kaldes, søger den først uden at tage params i bestragtning - hvis dette ikke giver noget resultat "slår den params brillerne til", og ser efter en gang mere. Det betyder at metoder med params både kan tage et variabelt antal parametere, men de kan også direkte tage et array af f.eks. string-referencer Den parameter der angives med params skal være den sidste i parameter-listen. Den behøver ikke være et array af string, som i eksemplet, men kan være et array af f.eks. int hvis man vil begrænse parametrene til integers.
4. Compiler
Hvordan kan man lave et program i C#, få det oversat og kørt? I skrivende stund (april 2003) er den seneste version af .NET Frameworket version 1.1. Den består af to dele: selve frameworket og SDK'en. Disse dele er populært sagt "maskinen" og compileren. Man skal under alle omstændigheder installere frameworket for at kunne eksekvere sine programmer, men om man installerer compileren selv eller bruger MS Visual Studio, må man selv vælge. I det følgende antager jeg, at man ikke bruger Visual Studio. Det første man gør, er at hente Microsoft .NET Framework Version 1.1 Redistributable (23MB) og Microsoft .NET Framework 1.1 Software Development Kit (ca. 120MB) fra Microsoft's site. Når man har hentet det hele kører man de to eksekverbare filer i nævnte rækkefølge, og følger anvisninger.
Der findes et Kawa-lignende program - dvs. en letvægts IDE, som bliver udviklet under GNU GPL (gratis!). Det hedder SharpDevelop og er endnu kun i beta, men jeg synes den fungerer nogenlunde. Man kan finde den på: http://www.icsharpcode.net/OpenSource/SD/default.asp .cs Når man skal compilere for første gang kan man f.eks. prøve med et af "Hello World"-eksemplerne ovenfor. Man placerer kildeteksten i en file; hvis navn man selv kan vælge. Lad os sige vi vælger at kalde filen hello_world.cs. Ligesom man bruger suffix .cpp i C++ og .java i Java, bruger man .cs i C# ('s' for sharp).
4.1 Command-prompt
Hvis man ikke bruger en IDE, er det muligt at compilere programmet fra en command-prompt
csc hello_world.csMan skal huske suffix, ellers kan compileren ikke finde filen. Under installationen af .NET Framework SDK er PATH ikke blevet sat så man kan køre compileren (csc.exe) uanset hvor man er. Om dette er en forglemmelse (tidligere versioner satte selv PATH) skal være usagt, men man skal altid køre: "Program Files\Microsoft.NET\SDK\v1.1\Bin\SDKVars.bat"når man starter en ny DOS-prompt, for at det virker. Jeg har sat "-er udenom, da det er nødvendigt for at DOS ikke tror, man vil køre et program, der hedder: Program. Hvis oversættelsen ellers går godt, vil resultatet blive en exe-file ved navn hello_world.exe - denne kan umiddelbart køres. Den eksekverbare file vil altid få navn efter den file der indeholder Main-metoden. Har man opdelt sit program i flere filer kan de compileres med f.eks.:
csc hello_world.cs mere.cs endnu_mere.cs
5. Dokumentation
MSDN Library Dokumentationen til C# og .NET Framework'et er ikke helt så let tilgængelig som man kender det fra Java's JDK. Microsoft har en dokumentation der retter sig mod udviklere, som de har placeret i deres MSDN Library (MSDN: MicroSoft Developers Network). Derfor skal man enten abonnere på MSDN eller nøjes med den online. Den online version vil altid være den mest opdaterede; hvilket er en fordel, da .NET Frameworket stadig er meget nyt. Til gengæld er det uhensigtsmæssigt for dem, der ikke har hurtig/billig adgang til internettet. Link: MSDN Library
fodnoter:Det kræver dog at man bruger noget der hedder Managed Extensions (jeg er ikke helt klar over hvad det er!).