Operaattorin ylikuormitus ja käyttäjän muunnokset



Samankaltaiset tiedostot
Delegaatit ja tapahtumakäsittelijät

Javan perusteita. Janne Käki

ITKP102 Ohjelmointi 1 (6 op)

ITKP102 Ohjelmointi 1 (6 op)

Attribuutit. Copyright IT Press Tämän e-kirjan kopiointi, tulostaminen ja jakeleminen eteenpäin luvatta on kielletty.

815338A Ohjelmointikielten periaatteet Harjoitus 5 Vastaukset

Harjoitus Olkoon olemassa luokat Lintu ja Pelikaani seuraavasti:

Tietotyypit ja operaattorit

ITKP102 Ohjelmointi 1 (6 op), arvosteluraportti

Ohjelmassa henkilön etunimi ja sukunimi luetaan kahteen muuttujaan seuraavasti:

Ohjelmoinnin peruskurssi Y1

VIII. Osa. Liitteet. Liitteet Suoritusjärjestys Varatut sanat Binääri- ja heksamuoto

ITKP102 Ohjelmointi 1 (6 op)

Operaattoreiden ylikuormitus. Operaattoreiden kuormitus. Operaattoreiden kuormitus. Operaattoreista. Kuormituksesta

Ohjelmoinnin perusteet Y Python

Olion elinikä. Olion luominen. Olion tuhoutuminen. Olion tuhoutuminen. Kissa rontti = null; rontti = new Kissa();

JUnit ja EasyMock (TilaustenKäsittely)

1. Miten tehdään peliin toinen maila?

812341A Olio-ohjelmointi Peruskäsitteet jatkoa

Olio-ohjelmointi Javalla

ITKP102 Ohjelmointi 1 (6 op)

15. Ohjelmoinnin tekniikkaa 15.1

Ohjelmoinnin perusteet Y Python

Ohjelmointitaito (ict1td002, 12 op) Kevät Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen

Ohjelmoinnin perusteet Y Python

1 Tehtävän kuvaus ja analysointi

Taulukot. Jukka Harju, Jukka Juslin

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op Taulukot & Periytyminen

Ohjelmoinnin perusteet, kurssikoe

Java-kielen perusteet

Sisällys. 1. Omat operaatiot. Yleistä operaatioista. Yleistä operaatioista

T Olio-ohjelmointi Osa 5: Periytyminen ja polymorfismi Jukka Jauhiainen OAMK Tekniikan yksikkö 2010

JReleaser Yksikkötestaus ja JUnit. Mikko Mäkelä

Ohjelmoinnin jatkokurssi, kurssikoe

Oliosuunnitteluesimerkki: Yrityksen palkanlaskentajärjestelmä

Java-kielen perusteet

Ohjelmoinnin perusteet Y Python

17. Javan omat luokat 17.1

14. oppitunti. Operaattorin ylikuormitus. Osa. Operaattorin ylikuormittaminen

Koodin kirjoittaminen

12. Monimuotoisuus 12.1

1. Omat operaatiot 1.1

JAVA-PERUSTEET. JAVA-OHJELMOINTI 3op A JAVAN PERUSTEET LYHYT KERTAUS JAVAN OMINAISUUKSISTA JAVAN OMINAISUUKSIA. Java vs. C++?

Rajapinta (interface)

Sisällys. Metodien kuormittaminen. Luokkametodit ja -attribuutit. Rakentajat. Metodien ja muun luokan sisällön järjestäminen. 6.2

15. Ohjelmoinnin tekniikkaa 15.1

13 Operaattoreiden ylimäärittelyjä

Pong-peli, vaihe Aliohjelman tekeminen. Muilla kielillä: English Suomi. Tämä on Pong-pelin tutoriaalin osa 3/7. Tämän vaiheen aikana

C#-luokkien perusteet

7. Näytölle tulostaminen 7.1

7. Oliot ja viitteet 7.1

20. Javan omat luokat 20.1

Sisällys. 20. Javan omat luokat. Java API. Pakkaukset. java\lang

Ohjelmointi 1 C#, kevät 2013, 2. tentti

Java kahdessa tunnissa. Jyry Suvilehto

13. Loogiset operaatiot 13.1

etunimi, sukunimi ja opiskelijanumero ja näillä

Ohjelmointitaito (ict1td002, 12 op) Kevät Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen

Ohjelmoinnin perusteet Y Python

Informaatioteknologian laitos Olio-ohjelmoinnin perusteet / Salo

on ohjelmoijan itse tekemä tietotyyppi, joka kuvaa käsitettä

Metodit. Metodien määrittely. Metodin parametrit ja paluuarvo. Metodien suorittaminen eli kutsuminen. Metodien kuormittaminen

Sisällys. 6. Metodit. Oliot viestivät metodeja kutsuen. Oliot viestivät metodeja kutsuen

Sisällys. Yleistä attribuuteista. Näkyvyys luokan sisällä. Tiedonkätkentä. Aksessorit. 4.2

Metodien tekeminen Javalla

Ohjelmoinnin perusteet Y Python

Sisällys. Yleistä attribuuteista. Näkyvyys luokan sisällä ja ulkopuolelta. Attribuuttien arvojen käsittely aksessoreilla. 4.2


9. Periytyminen Javassa 9.1

C++11 Syntaksi. Jari-Pekka Voutilainen Jari-Pekka Voutilainen: C++11 Syntaksi

Sisällys. 7. Oliot ja viitteet. Olion luominen. Olio Java-kielessä

815338A Ohjelmointikielten periaatteet Harjoitus 2 vastaukset

Ohjelmoinnin peruskurssi Y1

Ohjelmoinnin perusteet Y Python

17. Javan omat luokat 17.1

A) on käytännöllinen ohjelmointitekniikka. = laajennetaan aikaisemmin tehtyjä luokkia (uudelleenkäytettävyys)

16. Javan omat luokat 16.1

14. Poikkeukset 14.1

Olio-ohjelmointi: Luokkien toteuttaminen. Jukka Juslin

4. Luokan testaus ja käyttö olion kautta 4.1

13. Loogiset operaatiot 13.1

Ohjelmoinnin peruskurssi Y1

Sisällys. 12. Näppäimistöltä lukeminen. Yleistä. Yleistä

Ohjelmoinnin perusteet Y Python

Ohjelmointi 2 / 2010 Välikoe / 26.3

Ohjelmoinnin peruskurssi Y1

12. Monimuotoisuus 12.1

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op. Standardi- ja tietorakenneluokkia

IDL - proseduurit. ATK tähtitieteessä. IDL - proseduurit

ATK tähtitieteessä. Osa 3 - IDL proseduurit ja rakenteet. 18. syyskuuta 2014

Listarakenne (ArrayList-luokka)

Harjoitustyö: virtuaalikone

Sisällys. 14. Poikkeukset. Johdanto. Johdanto

Ohjelmoinnin perusteet Y Python

19. Olio-ohjelmointia Javalla 19.1

Tietojen syöttäminen ohjelmalle. Tietojen syöttäminen ohjelmalle Scanner-luokan avulla

812347A Olio-ohjelmointi, 2015 syksy 2. vsk. X Poikkeusten käsittelystä

Muuttujien roolit Kiintoarvo cin >> r;

Sisällys. 3. Muuttujat ja operaatiot. Muuttujat ja operaatiot. Muuttujat. Operaatiot. Imperatiivinen laskenta. Muuttujat. Esimerkkejä: Operaattorit.

3. Muuttujat ja operaatiot 3.1

Transkriptio:

13 Operaattorin ylikuormitus ja käyttäjän muunnokset Luvussa 7, "Ominaisuudet, taulukot ja indeksoijat," opit, miten luokan yhteydessä käytetään ohjelmallisesti []-operaattoria, jolloin objektia voidaan käsitellä kuin se olisi taulukko. Tässä luvussa tutkimme siihen läheisesti liittyviä C#:n ominaisuuksia, joiden avulla voit tehdä rakenteita ja luokkarajapintoja, joita on helpompi ja luonnollisempi käyttää: operaattorin ylikuormitusta ja käyttäjän omia muunnoksia. Aloitan johdannolla operaattorin ylikuormitukseen ja sen tarjoamiin etuihin ja sen jälkeen katsomme syntaksia, jolla operaattorin oletuskäyttäytyminen määritellään uudelleen sekä todellisemman esimerkin, jossa ylikuormitan +-operaattorin yhdistämään useita Invoice-objekteja. Sitten näytän luettelon niistä yksioperandisista ja binaarisista operaattoreista, jotka voidaan ylikuormittaa sekä kerron muutamasta asiaan liittyvästä rajoituksesta. Operaattorin ylikuormituksen käsittely lopetetaan muutamiin suunnitteluohjeisiin, jotka tulee huomioida, kun luokan suunnittelun yhteydessä päätetään mahdollisista operaattorien ylikuormituksista. Toisena asiana opit uuden käsitteen, käyttäjän muunnos (user-defined conversion). Aloitan taas johdannolla kertomalla ominaisuuden perusteet ja sitten sukellan luokkaan, joka näyttää, miten voit muunnoksen avulla tehdä tyyppimuunnoksen struct- tai class-tyypistä toiseen struct- tai class-tyyppiin tai C#:n perustyyppiin. Operaattorin ylikuormitus Operaattorin ylikuormitus mahdollistaa olemassa olevan C#-operaattorin uudelleenmäärittämisen käytettäväksi käyttäjän omien tyyppien kanssa. Operaattorin ylikuormitusta sanotaan joskus pelkäksi sokerikuorrutukseksi, koska se itse asiassa on vain erilainen tapa kutsua metodia. On myös sanottu, että se ei pohjimmiltaan tuo kieleen mitään uutta. Vaikka se onkin teknisessä mielessä totta, operaattorin ylikuormitus auttaa olioperusteisen ohjelmoinnin tärkeimmässä perusteessa: abstraktiossa. 267

Osa III Koodin kirjoittaminen Oletetaan, että sinun pitää yhdistää kokoelma määrätyn asiakkaan laskuja. Operaattorin ylikuormitusta käyttämällä voit kirjoittaa seuraavanlaisen koodin, jossa +=operaattori on ylikuormitettu. Invoice summaryinvoice = new Invoice(); foreach (Invoice invoice in customer.getinvoices()) summaryinvoice += invoice; Tällaisen koodin etu on sen luonnollisuudessa ja siinä tosiseikassa, että käyttäjä on abstraktoitu siitä, miten laskujen yhdistäminen yksityiskohtaisesti tapahtuu. Yksinkertaisesti sanoen, operaattorin ylikuormitus auttaa kirjoittamaan ohjelmia, jotka ovat edullisempia tehdä ja ylläpitää. Syntaksi ja esimerkki Kuten sanoin, operaattorin ylikuormitus tarkoittaa metodin kutsumista. Määritelläksesi operaattorin luokassa uudelleen, sinun tulee vain käyttää seuraavaa mallia, missä op on ylikuormitettava operaattori: public static retval operatorop (object1 [, object2]) Pidä mielessä seuraavat tosiasiat, kun käytät operaattorin ylikuormitusta: Kaikki ylikuormitettavat operaattorit tulee määritellä määreellä public tai static. Teknisesti retval (paluuarvo) voi olla mitä tahansa tyyppiä. On kuitenkin yleinen käytäntö, että paluuarvo on sama tyyppi, missä metodi on määritelty sillä poikkeuksella, että true ja false-operaattorin tulee aina palauttaa Boolean-tyyppi. Välitettävien parametrien (object1, object2) määrä riippuu ylikuormitettavan operaattorin tyypistä. Jos ylikuormitetaan yksioperandista operaattoria, sillä on yksi parametri. Jos operaattori on binaarinen (eli sillä on kaksi operandia), välittään kaksi parametria. Yksioperandisen operaattorin tapauksessa metodin parametrin tulee olla saman tyyppinen kuin luokka tai tietue, jossa metodi määritellään. Toisin sanoen, jos uudelleenmäärittelet!-operaattorin luokassa nimeltä Foo, pitää metodin ainoan parametrin tyyppi olla Foo. Jos ylikuormitettava operaattori on binaarioperaattori, sen ensimmäisen parametrin tulee olla samaa tyyppiä, jossa metodi määritellään. Toinen parametri voi olla mitä tahansa tyyppiä. 268

Operaattorin ylikuormitus ja käyttäjän muunnokset Luku 13 Edellisen kappaleen pseudokoodissa käytin +=-operaattoria Invoice-luokassa. Syystä, jonka kohta ymmärrät, et voi tosiasiallisesti ylikuormittaa näitä yhdistelmäoperaattoreita. Voit ylikuormittaa vain "kantaoperaattorin", tässä tapauksessa siis operaattorin +. Tässä rakenne, jolla määritellään Invoice-luokan operator+-metodi: public static Invoice operator+ (Invoice invoice1, Invoice invoice2) // Luo uusi Invoice-objekti. // Lisää haluttu sisältö // invoice1-objektista uuteen Invoice-objektiin. // Lisää haluttu sisältö // invoice2-objektista uuteen Invoice-objektiin. // Palauta uusi Invoice-objekti. Katsotaan nyt vähän sisällökkäämpää esimerkkiä. Esimerkissä on kaksi pääluokkaa: Invoice ja InvoiceDetailLine. Invoice-luokassa on ArrayList-tyyppinen jäsenmuuttuja, joka esittää kaikkien laskun rivien kokoelman. Jotta kahden laskun (invoice) rivien yhdistäminen olisi mahdollista, ylikuormitin +-operaattorin. (Katso yksityiskohtia alla olevasta operator+metodista). Invoice.operator+-metodi luo uuden Invoice-objektin ja käy läpi kummankin laskun laskurivien taulukon lisäten kunkin rivin uuteen Invoice-objektiin. Tämä uusi Invoice-objekti palautetaan sitten kutsujalle. Todellisessa laskutusmoduulissa tämä oli luonnollisesti paljon monimutkaisempi toimenpide, mutta esimerkki kuitenkin näyttää suhteellisen todentuntuisesti, miten operaattorin ylikuormitusta voi käyttää. using System; using System.Collections; class InvoiceDetailLine double linetotal; public double LineTotal get return this.linetotal; public InvoiceDetailLine(double LineTotal) this.linetotal = LineTotal; (jatkuu) 269

Osa III Koodin kirjoittaminen class Invoice public ArrayList DetailLines; public Invoice() DetailLines = new ArrayList(); public void PrintInvoice() Console.WriteLine( \nline Nbr\tTotal ); int i = 1; double total = 0; foreach(invoicedetailline detailline in DetailLines) Console.WriteLine( 0\t\t1", i++, detailline.linetotal); total += detailline.linetotal; Console.WriteLine( =====\t\t=== ); Console.WriteLine( Total\t\t1", i++, total); public static Invoice operator+ (Invoice invoice1, Invoice invoice2) Invoice returninvoice = new Invoice(); foreach (InvoiceDetailLine detailline in invoice1.detaillines) returninvoice.detaillines.add(detailline); foreach (InvoiceDetailLine detailline in invoice2.detaillines) returninvoice.detaillines.add(detailline); return returninvoice; class InvoiceAddApp public static void Main() 270

Operaattorin ylikuormitus ja käyttäjän muunnokset Luku 13 Invoice i1 = new Invoice(); for (int i = 0; i < 2; i++) i1.detaillines.add(new InvoiceDetailLine(i + 1)); Invoice i2 = new Invoice(); for (int i = 0; i < 2; i++) i2.detaillines.add(new InvoiceDetailLine(i + 1)); Invoice summaryinvoice = i1 + i2; summaryinvoice.printinvoice(); Ylikuormitettavat operaattorit Vain seuraavat yksioperandiset ja binaariset operaattorit voidaan ylikuormittaa. Yksioperandiset: +, -,!, ~, ++, --, true, false Binaarioperandit: +, -, *, /, %, &,, ^, <<, >>, ==,!=, >, <, >=, <= Huomaa Pilkkua käytetään tässä erottamaan eri ylikuormitettavat operandit toisistaan. Pilkku-operaattoria, jota käytetään for-käskyssä ja metodikutsuissa, ei voi ylikuormittaa. Rajoitukset operaattorin ylikuormituksessa Ei ole mahdollista ylikuormittaa sijoitusoperaattoria =. Kun ylikuormitat binaarioperaattoria, sen yhdistelmäsijoitusoperaattori ylikuormitetaan kuitenkin epäsuorasti. Jos esimerkiksi ylikuormitat +-operaattorin, +=-operaattori kuormitetaan epäsuorasti, kun käyttäjän määrittämää operator+-metodia kutsutaan. []-operaattoreita ei voi ylikuormittaa. Kuten kuitenkin näit luvussa 7, käyttäjän luomat objektit voidaan indeksoida indeksoijien avlla. 271

Osa III Koodin kirjoittaminen Tyyppimuunnoksessa käytettävät sulut eivät ole ylikuormitettavissa. Sen sijaan sinun tulee käyttää muunnos-operaattoreita, joita myös käyttäjän muunnoksiksi sanotaan. Asiasta puhutaan tämän luvun loppuosassa. Operaattoreita, joita ei C#:ssa ole määritelty, ei voida ylikuormittaa. Et voi esimerkiksi määritellä merkkejä ** tarkoittamaan potenssiin korotusta, koska C# ei määrittele **-operaattoria. Myöskään operaattorin syntaksia ei voi muokata. Et voi muuttaa binaarista *-operaattoria ottamaan kolmea parametria, koska sen oletussyntaksi tarvitsee vain kaksi paramatria. Lopuksi, operaattorien suoritusjärjestystä ei voi muuttaa. Suoritusjärjestyssäännöt ovat pysyviä, katso luvusta 10, "Lausekkeet ja operaattorit," lisää näistä säännöistä. Suunnitteluohjeita Olet nähnyt, mikä operaattorin ylikuormitus on ja miten sitä käytetään C#:ssa, joten opiskellaan tämän käyttökelpoisen ominaisuuden usein unohtuvaa asiaa: suunnitteluohjeita. Sinun on yritettävä pysyä erossa luonnollisesta taipumuksesta käyttää uutta ominaisuutta sen vuoksi, että se on olemassa. Tätä ilmiötä sanotaan joskus "ongelman etsimiseksi ratkaisuun." On aina hyvää suunnittelua muistaa sanonta "koodia luetaan useammin kuin kirjoitetaan." Pidä luokan käyttäjät mielessä, kun päätät, ylikuormitatko operaattorin ja koska sen teet. Tässä nyrkkisääntö: sinun tulee ylikuormittaa operaattori vain, jos se tekee luokan rajapinnan luonnollisemmaksi käyttää. Se esimerkiksi sopii täydellisesti tilanteeseen, jossa laskuja pitää yhdistää. Älä myöskään unohda, että sinun tulee ajatella kuin luokkasi käyttäjä. Sanotaan, että teet Invoice-luokkaa ja haluat, että käyttäjä voi antaa laskulle alennuksen. Sinä ja minä tiedämme, että laskuun lisätään hyvitysrivi, mutta kapseloinnin idea on siinä, että luokkasi käyttäjän ei tarvitse tietää luokan toteutuksen täsmällisiä yksityiskohtia. Siksi *-operaattorin ylikuormittaminen seuraavalla tavalla voi olla hyvä idea, koska se palvelee Invoice-luokan rajapintaa luonnollisella ja käyttökelpoisella tavalla: invoice *=.95; // 5% alennus. Käyttäjän muunnokset Mainitsin aiemmin, että tyyppimuunnokseen käytettäviä sulkuja ei voi ylikuormittaa ja että sen sijaan tulee käyttää muunnoksia. Lyhyesti sanottuna, käyttäjän muunnosten avulla voit määritellä tietueille tai luokille muunnoksia, joissa struct tai class muunnetaan toiseksi tietueeksi, luokaksi tai C#:n perustyypiksi. Miksi ja milloin tällaiseen on tarvetta? Sanotaan, että sinun pitää käyttää sovelluksessasi Celsius- ja Fahrenheit-lämpötila-asteikkoja siten, 272

Operaattorin ylikuormitus ja käyttäjän muunnokset Luku 13 että niiden muuntaminen toiseksi onnistuu helposti. Tekemällä käyttäjän muunnoksen voit käyttää seuraavaa syntaksia: Fahrenheit f = 98.6F; Celsius c = (Celsius)f; // Käyttäjän muunnos. Tässä ei ole mitään toiminnallista etua seuraavaan syntaksiin nähden, mutta se on luonnollisempi kirjoittaa ja helpompi lukea. Fahrenheit f = 98.6F; Celsius c = f.converttocelsius(); Syntaksi ja esimerkki Käyttäjän muunnoksen syntaksi käyttää operator-avainsanaa muunnoksen määrittelyssä seuraavasti: public static implicit operator conv-type-out ( conv-type-in operand ) public static explicit operator conv-type-out ( conv-type-in operand ) Muunnoksen syntaksiin liittyy vain muutama sääntö: Jokaisen muunnosmetodin määreen tulee olla static. Muunnoksia voi yhdessä luokassa tai tietueessa olla vaikka kuinka monta. Muunnos pitää määritellä joko sanalla implicit tai explicit. implicit-avainsana tarkoittaa, että muunnos tehdään automaattisesti eikä käyttäjän tarvitse sitä erikseen määrätä. Vastaavasti explicit-avainsana ilmoittaa, että käyttäjän tulee tehdä muunnos explisiittisesti. Kaikkien muunnosten tulee ottaa parametrinaan tyyppi, jossa muunnos määritellään tai palauttaa tyyppi, jossa muunnos määritellään. Kuten operaattorin ylikuormituksessa, käytetään operator-avainsanaa metodin otsikossa mutta ilman lisäoperaattoria. Kun ensimmäisen kerran luin nämä säännöt, minulla ei ollut harmainta aavistustakaan mitä pitää tehdä, joten katsotaan esimerkkiä. Siinä meillä on kaksi tietuetta (Celsius ja Fahrenheit), joiden avulla luokan käyttäjä voi muuntaa float-tyyppisen arvon jompaan kumpaan lämpötila-asteikkoon. Esitän ensin Celsius-muunnoksen ja kerron siitä muutamia seikkoja ja sen jälkeen näet täydellisen toimivan sovelluksen. struct Celsius 273

Osa III Koodin kirjoittaminen public Celsius(float temp) this.temp = temp; public static implicit operator Celsius(float temp) Celsius c; c = new Celsius(temp); return(c); public static implicit operator float(celsius c) return((((c.temp - 32) / 9) * 5)); public float temp; Ensimmäinen päätös, jonka näet, on tietueen käyttäminen luokan sijasta. Minulla ei ole siihen muuta todellista syytä kuin se, että luokan käyttäminen on muistin käytön kannalta tehottomampaa ja toisaalta luokkaa ei tässä välttämättä tarvita, koska Celsius-tietue ei tarvitse mitään C#:n luokkakohtaisia ominaisuuksia, kuten esimerkiksi periytymistä. Huomioi seuraavaksi, että määrittelin muodostimen, joka ottaa ainoaksi parametrikseen float-tyypin muuttujan. Tämä arvo tallennetaan temp-nimiseen jäsenmuuttujaan. Katso nyt muunnosoperaattoria, jonka määrittely on heti tietueen muodostimen jäljessä. Tämä on metodi, jota kutsutaan, kun asiakas tekee tyyppimuunnoksen float-tyypistä Celsius-tyypiksi, tai käyttää float-tyyppiä sellaisessa paikassa, esimerkiksi metodikutsussa, jossa pitäisi käyttää Celsius-tietuetta. Tämän metodin ei tarvitse tehdä paljon ja se onkin pelkkä kaavan sisältävä koodi, jota voidaan käyttää useissa perusmuunnoksissa. Yksinkertaisesti instantioin Celsius-tietueen ja sen jälkeen palautan sen. return-kutsu on se, joka aiheuttaa tietueen viimeisen metodin kutsumisen. Kuten näet, metodi yksinkertaisesti tekee matemaattisen kaavan mukaisen laskutoimituksen muuntaen Fahrenheit-arvon Celcius-arvoksi. Tässä koko sovellus, jossa on mukana myös Fahrenheit-tietue: using System; struct Celsius public Celsius(float temp) this.temp = temp; 274

Operaattorin ylikuormitus ja käyttäjän muunnokset Luku 13 public static implicit operator Celsius(float temp) Celsius c; c = new Celsius(temp); return(c); public static implicit operator float(celsius c) return((((c.temp - 32) / 9) * 5)); public float temp; struct Fahrenheit public Fahrenheit(float temp) this.temp = temp; public static implicit operator Fahrenheit(float temp) Fahrenheit f; f = new Fahrenheit(temp); return(f); public static implicit operator float(fahrenheit f) return((((f.temp * 9) / 5) + 32)); public float temp; class Temp1App public static void Main() float t; (jatkuu) 275

Osa III Koodin kirjoittaminen t=98.6f; Console.Write( Conversion of 0 to Celsius =, t); Console.WriteLine((Celsius)t); t=0f; Console.Write( Conversion of 0 to Fahrenheit =, t); Console.WriteLine((Fahrenheit)t); Jos käännät ja suoritat tämän sovelluksen, saat seuraavat tulokset: Conversion of 98.6 to Celsius = 37 Conversion of 0 to Fahrenheit = 32 Tämä toimii hyvin ja mahdollisuus toteuttaa muunnos kirjoittamalla (Celsius)98.6F on ilman muuta fiksumpi tapa kuin jonkin staattisen metodin kutsuminen. Mutta huomaa, että voit välittää parametrina näille muunnoksille vain float-tyyppisen arvon. Yllä oleva sovelluksessa seuraava ei käänny: Celsius c = new Celsius(55); Console.WriteLine((Fahrenheit)c); Koska ei myöskään ole Celsius-muunnosmetodia, joka ottaisi parametrinaan Fahrenheit-tietueen (ja päinvastoin), koodin pitää olettaa, että sille välitetty arvo on muunnettava arvo. Toisin sanoen, jos kutsun (Celsius)98.6F, saan tuloksen 37. Jos tuo arvo sitten välitetään takaisin muunnosmetodille, sillä ei ole mitään tapaa tietää, että arvo on jo muunnettu ja on loogisesti Celsius-asteina, muunnosmetodin kannalta se on vain floattyyppinen arvo. Ja siksi se muunnetaan uudelleen. Tämän vuoksi meidän pitää muokata sovellusta niin, että kukin tietue voi ottaa kelvollisena parametrinaan toisen tietueen. Kun alun perin ajattelin tämän tekemistä, kavahdin ajatusta, koska mietin miten vaikeaa sellainen olisi tehdä. Osoittautui kuitenkin, että se äärimmäisen helppoa. Tässä muutettu koodi jälkikommentein varustettuna: using System; class Temperature public Temperature(float Temp) this.temp = Temp; protected float temp; public float Temp 276

Operaattorin ylikuormitus ja käyttäjän muunnokset Luku 13 get return this.temp; class Celsius : Temperature public Celsius(float Temp) : base(temp) public static implicit operator Celsius(float Temp) return new Celsius(Temp); public static implicit operator Celsius(Fahrenheit F) return new Celsius(F.Temp); public static implicit operator float(celsius C) return((((c.temp - 32) / 9) * 5)); class Fahrenheit : Temperature public Fahrenheit(float Temp) : base(temp) public static implicit operator Fahrenheit(float Temp) return new Fahrenheit(Temp); public static implicit operator Fahrenheit(Celsius C) return new Fahrenheit(C.Temp); public static implicit operator float(fahrenheit F) return((((f.temp * 9) / 5) + 32)); (jatkuu) 277

Osa III Koodin kirjoittaminen class Temp2App public static void DisplayTemp(Celsius Temp) Console.Write( Conversion of 0 1 to Fahrenheit =, Temp.ToString(), Temp.Temp); Console.WriteLine((Fahrenheit)Temp); public static void DisplayTemp(Fahrenheit Temp) Console.Write( Conversion of 0 1 to Celsius =, Temp.ToString(), Temp.Temp); Console.WriteLine((Celsius)Temp); public static void Main() Fahrenheit f = new Fahrenheit(98.6F); DisplayTemp(f); Celsius c = new Celsius(0F); DisplayTemp(c); Ensimmäinen tekemäni muutos oli se, että muutin Celsius ja Fahrenheit -tyypit tietueesta luokaksi. Tein sen, jotta minulla olisi kaksi esimerkkiä, toinen tietueilla toteutettu ja toinen luokilla. Mutta käytännöllisempi syy oli jakaa temp-jäsenmuuttuja periyttämällä Celsius ja Fahrenheit -luokat samasta Temperature-kantaluokasta. Nyt voin myös käyttää System.Object-luokasta perittyä ToString-metodia sovelluksen tulostuksessa. Ainoa muu huomioitava muutos oli sellaisen muunnoksen lisääminen kuhunkin lämpötila-asteikkoon, joka ottaa parametrinaan toisen asteikon tyypin. Huomaa, miten samanlaista koodi kahden Celsius-muunnosmetodin välillä on: public static implicit operator Celsius(float temp) Celsius c; c = new Celsius(temp); return(c); public static implicit operator Celsius(Fahrenheit f) Celsius c; 278

Operaattorin ylikuormitus ja käyttäjän muunnokset Luku 13 c = new Celsius(f.temp); return(c); Ainoa tehtävä, joka minun piti tehdä eri tavalla, oli muuttaa välitettävä parametri ja hakea lämpötila välitetyltä objektilta kovakoodatun float-tyypin sijasta. Tämä on syy, miksi aiemmin huomautin, kuinka helppoa muunnosmetodien tekeminen sitten on, kun tuntee perusasiat. Yhteenveto Operaattorin ylikuormitus ja käyttäjän muunnokset ovat hyödyllisiä, kun tehdään hyviä rajapintoja luokalle. Kun käytät operaattorin ylikuormitusta, pidä mielessä asiaan liittyvät rajoitukset. Esimerkiksi se, että vaikka et voikaan ylikuormittaa =-operaattoria, niin kun binaarioperaattori ylikuormitetaan, sen yhdistelmäsijoitus ylikuormitetaan automaattisesti. Seuraa suunnitteluohjeita, kun päätät kunkin ominaisuuden käyttämisestä. Pidä mielessä luokan käyttäjä, kun mietit, ylikuormitatko operaattorin tai operaattoreita vai et. Tuntemalla käyttäjän ajatuksia ja tarpeita, voit käyttää näitä tehokaista ominaisuuksia ja tehdä luokan, jonka määrättyjä toimintoja voidaan suorittaa luonnollisella syntaksilla. 279