Aliohjelmien kutsuminen
Katso myös Aliohjelmien kirjoittaminen.
1. Aluksi
Aliohjelmakutsuihin EI tule tyyppejä (ellei samalla luoda new
:llä uusia olioita, mutta siitä myöhemmin). Olisi 'väärin' kirjoittaa seuraavasti.
y = double Math.Sin(double x); // TÄMÄ ON VÄÄRIN!!! Kutsuun ei tule tyyppejä!
Alla oleva on oikein (olettaen että x
on hyvin määritelty).
y = Math.Sin(x);
Oikeastaan "vaikeinta" aliohjelmakutsuissa on päättää onko kyseessä staattisen aliohjelman kutsu (luku 1.) vai olion metodin kutsu (luku 2). Tämä päätös voidaan tehdä katsomalla aliohjelman esittelyrivistä lukeeko siinä static
vai ei.
2. Miten aliohjelmia kutsutaan
2.1 Tuleeko alkuun mitä?
Lyhyesti:
- jos static, kutsun eteen tulee luokan nimi
- jos ei-static, kutsun eteen tulee jonkun luodun olion nimi
2.2 Miltä aliohjelmakutsu näyttää
Jo kurssin ensimmäisistä esimerkeistä alkaen on kutsuttu aliohjelmia, funktiota, muodostajia ja metodeja:
System.Console.WriteLine("Moikka!"); // aliohjelmakutsu (static)
Camera.ZoomToLevel(); // metodikutsu
// Seuraavana muodostajan kutsu:
PhysicsObject p1 = new PhysicsObject(2 * 100.0, 2 * 100.0, Shape.Circle);
Add(p1); // oman metodin kutsu joka voisi olla myös:
this.Add(p1) // joka voisi olla jopa selkeämpi
PiirraLumiukko(this, 0, 100); // oman luokan aliohjelman kutsu (static)
ala = KolmionAla(kanta, korkeus); // oman luokan funktion kutsu (static)
Eli aliohjelmakutsuissa (tai metodi/funktio) ei ole sinällään mitään ihmeellistä. Pitää vain tietää aliohjelman nimi ja parametrilista. Jos kyseessä on funktio, tulos ehkä sijoitetaan johonkin apumuuttujaan tms.
2.3 Mistä löytää miten aliohjelmia kutsutaan
Tätä varten pitää osata lukea dokumentaatiota. Esimerkiksi WriteLine-aliohjelman kohdalta löytyy mm:
Namespace: System
Console.WriteLine Method (String)
public static void WriteLine(string value)
Tätä luetaan seuraavasti:
- aliohjelma on kirjoitettu
System
-nimiavaruudenConsole
-luokan staattiseksi (static
) aliohjelmaksi ja se ei palauta mitään (void
). - aliohjelma ottaa parametrikseen yhden merkkijonon. Kutsussa pitää siis olla jokin merkkijonoarvoinen lauseke.
- Eli laillinen kutsu olisi esimerkiksi:
System.Console.WriteLine("Hello World!");
Toisaalta jos koodin alussa on using System;
niin nimiavaruutta ei tarvitse enää erikseen sanoa (mutta saa sanoa jos haluaa):
using System;
...
Console.WriteLine("Hello World!");
2.4 Arvon palauttava aliohjelma, eli funktio
Tutkitaan esimerkiksi Char
-luokan funktiota ToLower
(dokumentaatio), joka antaa kirjaimen pienenä kirjaimena:
Namespace: System
Char.ToLower Method (Char)
public static char ToLower(char c)
- Jälleen funktio on
System
-nimiavaruudessa, joten kutsuun tulee ensinSystem.
(huomaa piste.) tai ohjelman alussa on oltavausing System;
- Funktio on staattinen Char-luokassa, eli kutsussa on oltava myös
Char.
- Funktio palauttaa
char
-tyypisen arvon, eli funktiota käytettäessä sen tulos usein sijoitetaan char-tyyppiseen apumuuttujaan. - Funktiolle täytyy viedä parametrina jokin char-tyyppinen lauseke (esim char tyyppisen muuttujan arvo). Kutsuun ei missään nimessä kirjoiteta parametrien tyyppejä.
- Näin ollen kutsu voisi olla muotoa:
char eka = 'A';
char pieni = System.Char.ToLower(eka);
tai
using System;
...
char pieni;
pieni = Char.ToLower('A');
Toki funktion palauttamaa arvoa voidaan käyttää suoraankin, esim:
using System;
...
Console.WriteLine(Char.ToLower('M'));
Vastaavasti trigonemetrista sin
-funktiota käytettäisiin dokumentaation
Namespace: System
Math.Sin Method
public static double Sin(double a)
mukaan seuraavasti:
- Funktio on
System
-nimiavaruudessa, joten kutsuun tulee jälleenSystem.
tai ohjelman alussa on oltavausing System;
. - Funktio on staattinen
Math
-luokassa, eli kutsussa on oltava myösMath.
- Funktio palauttaa
double
-tyypisen arvon, eli funktiota käytettäessä sen tulos (usein, ei välttämättä) sijoitetaandouble
-tyyppiseen apumuuttujaan. - Funktiolle täytyy viedä parametrina jokin double-tyyppinen lauseke (esim double tyyppisen muuttujan arvo). Kutsuun ei missään nimessä kirjoiteta parametrien tyyppejä.
- Näin ollen kutsu voisi olla muotoa:
using System;
...
double kulma = Math.PI / 4; // 45 astetta
double y;
y = Math.Sin(0.5*kulma);
2.5 Eihän PiirraLumiukko-aliohjelman kutsussa ollut mitään pistettä!
Mutta eihän esimerkiksi LumiukkoAli.cs:ssä olleessa lumiukon piirtämisessä ollut muuta kuin:
PiirraLumiukko(this, 0, Level.Bottom + 200.0);
Miksi?
Tuossa ei enempää tarvittu, koska PiirraLumiukko
oli esitelty samassa luokassa kuin mistä sitä kutsuttiin. Toki kutsu olisi voinut olla myös
LumiukkoAli.PiirraLumiukko(this, 0, Level.Bottom + 200.0);
3. Metodin kutsuminen
Edellä käsiteltiin staattisten aliohjelmien kutsumista. Staattisia aliohjelmia voidaan kutsua ilman että yhtään vastaavaa oliota on luotu. Suuri osa C#:in aliohjelmista on kuitenkin olioiden metodeja. Tällöin metodin kutsumista varten pitää ensin olla luotu tai muulla tavoin saatu (silloinkin joku on jossakin vaiheessa luonut) olio (tai oikeastaan viite olioon).
3.1 StringBuilder ja String
Oletetaan että meillä pitäisi poistaa sanasta "Krokotiili" alkuosa ja lisätä sana "talo" tuohon perään. Tämä voitaisiin tehdä pelkästään luomalla uusia olioita String
-luokan avulla, mutta muutoksia voidaan tehdä merkkijonossa suoraan jos jono luodaan StringBuilder
-luokasta.
Valmis "ohjelma" tulee olemaan:
Aluksi meidän pitäisi luoda siis uusi olio jonka sisältönä on jono "Krokotiili". Siksi on etsittävä StringBuilder
-luokasta muodostaja (dokumentaatio), eli jolla tuo voitaisiin tehdä:
Namespace: System.Text
StringBuilder.StringBuilder(String) Constructor
public StringBuilder (string value)
new
luo uuden olion vaatiman muistitilan ja muodostajan tehtävä on alustaa oliolle varattu tila halutulla tavalla. Muodostajan nimi on C#:issa aina sama kuin luokan nimi. Muodostajia voi olla useita (sama nimi, eri parametrilista).
Muodostajan esittelystä ei suoraan näe että sitä on useimmiten (aina?) käytettävä nimenomaannew
-avainsanan kanssa. Tämä on siis tiedettävä siitä, kun nähdään kyseessä olevan muodostaja
using System.Text;
...
StringBuilder sb = new StringBuilder("Krokotiili");
- Näin meille syntyi uusi
StringBuilder
luokan edustaja, jonka sisältönä on jono "Krokotiili" ja nyt apumuuttujasb
viittaa siihen, koskanew
palauttaa viitteen luomaansa olioon, jonka sisällön muodostaja laittoi pyydetyksi.
Seuraavaksi syntyneestä oliosta (eli muutettavasta merkkijonosta) pitäisi löytää 1. t-kirjain, mutta StringBuilder
-luokasta ei tällaista metodia (valitettavasti???) löydy. Voimme tosin tehdä jonosta uuden String
-tyyppisen kopion ja kysyä tältä t-kirjaimen paikkaa (dokumentaatio):
Namespace: System.Text
StringBuilder.ToString Method ()
public override string ToString()
- Koska
ToString
on metodi (ei siis staattinen aliohjelma), PITÄÄ sen kutsumiseksi olla olemassa (tässä tapauksessa)StringBuilder
-tyyppinen olio. Sellainen meillä onneksi nyt on, elisb
viittaa tuollaiseen olioon. - Metodille ei tule yhtään parametria (koska olio saa itsestään kaiken tarvitsemansa informaation kopioin luomiseen).
- Metodi palauttaa
string
-tyyppisen viitteen.override
tarkoittaa, että tässä luokassa tehdään uusi versio perityn luokan metodista. Kutsumisen kannalta tällä tiedolla ei meille ole (tässä vaiheessa vielä) lisäarvo. Kutsu on siis muotoa:
string s = sb.ToString();
Tästä jonosta voimme etsiä t-kirjaimen paikkaa (String.IndexOf-dokumentaatio):
Namespace: System
String.IndexOf Method (Char)
public int IndexOf(char value)
- Jälleen on kyse metodista, eli kutsumiseksi pitää olla String-tyypin olio (String ja string ovat samoja jos on
Using System;
. Meillä nyts
on viite sellaiseen olioon. - Metodi tarvitsee yhden char-tyyppisen lausekkeen parametrikseen.
- Metodi palauttaa kokonaisluvun. Näin ollen kutsu voisi olla esim:
int i;
i = s.IndexOf('t');
Koska sb
ja s
ovat toistensa kopioita (olioita, joissa on sama sisältö, mutta asuvat aivan eri puolilla muistia), on i
nyt samalla myös t-kirjaimen paikka sb
-jonossa.
StringBuilder
-tyyppisestä jonosta merkkejä voidaan oikeasti poistaa (StringBuilder.Remove-dokumentaatio):
Namespace: System.Text
StringBuilder.Remove Method
public StringBuilder Remove(int startIndex,int length)
- Jälleen kyseessä on metodi, eli sen kutsumiseksi tarvitaan nyt
StringBuilder
-luokan oliota (koska onStringBuilder.Remove Method
). Meilläsb
on viite sellaiseen olioon. - Metodi tarvitsee parametrikseen poistamisen alkupaikan ja poistettavien merkkien määrän. Jos haluamme poistaa jonon alkuosan t-merkkiin saakka, niin t:n paikka on itse asiassa tällä kertaa myös sitä edeltävien merkkien lukumäärä. Ja koska alusta, niin poiston alkupaikka on tällä kertaa 0.
- Metodi palauttaa viitteen
StringBuilder
tyyppiseen olioon, joka itse asiassa on viite samaan olioon, josta olemme poistamassa. Usein tehdään tällaisia metodeja jotta ketjutetut (esimerkki myöhemmin) kutsut olisivat sujuvampia. Tällä kertaa emme tarvitse tuota viitetietoa vaan voimme unohtaa sen (koska poisto tehdään oliosta itsestään ja siihen meillä on jo viite)
sb.Remove(0, i); // poistaa 0:sta alkaen i merkkiä, eli jonoksi jää tiili
Lopuksi haluaisimme lisätä jonon loppuun jonon "talo": StringBuilder.Append-dokumentaatio
Namespace: System.Text
StringBuilder.Append Method (String)
public StringBuilder Append(string value)
- Kuten edellä kyseessä on metodi ja
sb
osoittaa kivasti tarvittavaan olioon. - Metodi tarvitsee parametrikseen lisättävän jonon. Mehän aiomme lisätä jonon "talo".
- Kuten edellellä kutsu muuttuu itse jonoa, mutta palauttaa myös viitteen (samaan) muutettuun jonoon. Tälläkään kertaa emme tarvitse muutettua jonoa. Joten kutsuksi riittää
sb.Append("talo"); // tiilitalo
Nyt voisimme vaikkapa tulostaa jonon sb
sisällön.
Mikäli olisimme varmoja t:n löytymisestä (emme nytkään tarkistaneet että löytyykö t-kirjainta, eli että oliko i>=0
), koko ketju voitaisiin kirjoittaa paluuviitteiden ansiosta yhdeksi ainoaksi lauseeksi:
using System;
using System.Text;
...
StringBuilder sb2 = new StringBuilder("Krokotiili");
Console.WriteLine(sb2.Remove(0,sb2.ToString().IndexOf('t')).Append("talo"));
3.2 String
Vastaava "ohjelma" tehtynä pelkästään String
-luokan avulla olisi:
Edeltä muistamme että IndexOf
:in käyttöön tarvitsisimme olion, joka olisi luotu. Tässähän new
lausetta ei esiinny??? Mutta tosiasiassa "Krokotiili" on oikeastaan vain lyhenne new String(new char[]{'K',...'i'})
. Eli siinä mielessä tarvittava luonti on kyllä tehty.
Substring
palauttaa viitteen uuteen merkkijono-olioon (koska itse String
luokan olioita ei voi muuttaa (immutable)). Samoin s3 + "talo"
luo uuden olion ja palauttaa siihen viiteen. Näin ollen koko "ohjelman" aikana syntyy 3 uutta oliota (tai oikeastaan 4, koska syntyy myös jono "talo").
Olioita syntyy riippumatta siihen, minkä nimiseen muuttujaan viite sijoitetaan. Ohjelma olisi täsmälleen sama vaikka se olisi:
string s2 = "Krokotiili";
int tp = s2.IndexOf('t');
s2 = s2.Substring(tp);
s2 = s2 + "talo";
System.Console.WriteLine(s2);
Ainoa ero olisi, että "Krokotiili"-merkkijonoon ei päästäisi enää käsiksi ohjelman lopussa (kun siihen ei enää kukaan viittaa).
StringBuilder
-oliolla tehtynä ei syntynyt näin montaa oliota.
Tämäkin voitaisiin kirjoittaa yhtenä ketjutettuna lauseena:
string s5 = "Krokotiili";
System.Console.WriteLine(s5.Substring(s5.IndexOf('t'))+"talo");
tai jopa:
Console.WriteLine("Krokotiili".Substring("Krokotiili".IndexOf('t')) + "talo");
Apumuuttujia käyttäen kuitenkin ehkä paljastuu paremmin miten monta oliota matkalla syntyy.
3.3 Omien metodien kutsuminen
Kurssilla ei hirveästi kirjoiteta omia luokkia. Kuitenkin esimerkissä
public class Lumiukko : PhysicsGame
{
public override void Begin()
{
PhysicsObject p1 = new PhysicsObject(2 * 100.0, 2 * 100.0, Shape.Circle);
p1.Y = Level.Bottom + 200.0;;
this.Add(p1);
...
oleva Lumiukko
-luokka perii kaikki PhysicsGame
-luokassa olevat ominaisuudet. Siis myös metodit. Näin ollen Lumiukko
-luokassa on myös metodi Add
: Game-luokan dokumentaatio.
Game luokkareferenssi:
void Add(IGameObject o)
Lisää olion peliin, kerrokseen 0.
joka lisää minkä tahansa IGameObject
rajapinnan (interface) toteuttavan peliolion peliin.
Add
-metodin kutsumiseksi siis pitäisi olla olemassa Game
-luokan olio. Kun kuitenkin olemme kirjoittamassa Lumiukko
-luokan Begin
metodia, niin sen olion, jonka metodia tässä kirjoitamme, on joku joutunut jo luomaan että Begin
-metodiin päästään. Jypelissä yleensä Main
-funktiossa luodaan peliluokka. Jokaisessa metodissa on käytettävissä avainsana this
, joka viittaa siihen olioon, jonka metodia on kutsuttu. Begin
-metodia kutsutaan Jypelissä itse peliluokasta sitten, kun sen luomisen perusjutut ovat valmiita.
Näin ollen kutsu on muotoa:
this.Add(p1);
Mutta C#:issa (kuten Javassa ja C++:ssakin) kutsut ovat oletuksena this
-olioon jos muuta ei mainita. Eli em. on täsmälleen sama kuin kutsu:
Add(p1); // kutsuu this-olion Add-metodia.
These are the current permissions for this document; please modify if needed. You can always modify these permissions from the manage page.