Hei,
Saisinkohan apua tätä kautta? Opiskelutehtävässä pitää luoda Hinta-ominaisuus, joka laskee yli 30 ylittävän kirjan hintaa automaattisesti 10%. Alla nykyinen koodi. Ongelma on siinä, että sekä Hinta-ominaisuutta, että alkuperäistä hinta-parametriä käytetään pääohjelmassa.
Voisin toki jättää vain tämä tehtävän tekemättä ja mennä seuraavaan, mutta tulen hulluksi jos en saa tietää missä tein virheen..
using System; class Kirja { public string nimi; public string kirjailija; public string kustantaja; public string teema; public float hinta; private float _hinta; public Kirja(string nimi, string kirjailija, string kustantaja, float _hinta, string teema) { this.nimi = nimi; this.kirjailija = kirjailija; this.kustantaja = kustantaja; this.hinta = _hinta; this.teema = teema; } public float Hinta { get { return _hinta; } set { if (value > 30) { _hinta = value * 0.9f; } else { _hinta = value; } } } } class Program { //Tästä eteenpäin en voi muokata koodia static void Main(string[] args) { Kirja k = new Kirja("Kirja", "K. Kirjoittaja", "Kustannus Oy", 21.50f, "Dekkari"); Kirja k2 = new Kirja("Toinen Kirja", "O. Kirjailija", "Kustantaja Ab", 36.90f, "Fantasia"); Console.WriteLine(k.hinta); Console.WriteLine(k2.Hinta); } }
Nyt sinulla on kaksi erillistä hintamuuttujaa (hinta ja _hinta), joista jälkimmäinen pitäisi asettaa Hinta-ominaisuuden kautta ja alennus tähän tapahtuisi asettamisen yhteydessä. Olio voi olla oudossa tilassa, kun hinta ja _hinta sisältävät aivan eri luvut. Nyt et edes aseta jälkimmäistä ollenkaan.
Tehtävässä varmaan tarkoitetaan, että luokalla olisi vain yksi ”public float hinta” (eli _hinta pitäisi ottaa pois) ja että Hinta-ominaisuus olisi vain luettavissa eli set-funktiota ei olisi ollenkaan ja get-funktiossa palautettaisiin ohjeen mukaan joko alkuperäinen tai alennettu hinta.
Ylipäänsä on äärettömän sekavaa käyttää noin samanlaisia nimiä (hinta, _hinta, Hinta) ja vieläpä niin, että sekä hinta että Hinta ovat julkiset. Oikeassa ohjelmassa nämä olisi järkevää nimetä hinnat sen mukaan, mitä ne oikeasti kuvaavat, esimerkiksi Normaalihinta ja Myyntihinta.
Epäilemättä on hankaluutta tuossa koodin pätkässä, mutta esim. nimet tehtävässä tuli annettuna, varmasti osittain senkin takia, että pitäisi osata tunnistaa erot melkein samoissa nimissä. Kiitos kommenteista, Metabolix!
Keksinkin ratkaisun kun ulkona kävellessä pyörittelin asiaa päässäni. Muokkasin muodostimen seuraavanlaiseksi, niin homma pelitti kuten pitää.
public Kirja(string nimi, string kirjailija, string kustantaja, float Hinta, string teema) { this.nimi = nimi; this.kirjailija = kirjailija; this.kustantaja = kustantaja; this.hinta = Hinta; this.Hinta = Hinta; this.teema = teema; }
Vaikka ratkaisusi tässä tapauksessa toimii, se on silti periaatteellisesti väärä, koska tuossa olisi mahdollista asettaa ulkoa päin täysin eri arvo hintaan ja Hintaan ja lisäksi Hinta käyttäytyy oudosti niin, että esimerkiksi rivi Hinta = Hinta laskee arvoa 10%:lla:
k.Hinta = 5678; k.hinta = 1234; Console.WriteLine("{0} ja {1}", k.hinta, k.Hinta); // 1234 ja 5110,2, eli arvot ovat erilliset eikä Hinta päivity itsestään. k.Hinta = k.Hinta; Console.WriteLine(k.Hinta); // 4599,18 k.Hinta = k.Hinta; Console.WriteLine(k.Hinta); // 4139,262 k.Hinta = k.Hinta; Console.WriteLine(k.Hinta); // 3725,335
Tämän takia loogisempi ja paremmin tehtävänantoa vastaava ratkaisu tehtävään olisi tuo kuvailemani, jossa alennus tehdään lukemisen yhteydessä.
public float Hinta { get { if (hinta > 30) { return 0.9f * hinta; } return hinta; } } // ... // k.Hinta = 5678; // Mahdoton rivi, Hinta voidaan vain lukea. k.hinta = 1234; Console.WriteLine("{0} ja {1}", k.hinta, k.Hinta); // 1234 ja 1110,6, eli Hinta lasketaan asetetun hinta-arvon mukaan.
Tehtävä on huolimattomasti suunniteltu: jos tehtävä tarkastetaan pelkästään tuolla antamallasi koodilla, siihen voisi lähettää ratkaisuksi jopa tämän:
public float Hinta = 0.9f * 36.90f;
Kieltämättä tuo koodi on aika huono, jos kyseessä olisi oikea ohjelma. Tuo pääohjelma kuitenkin rajoittaa minua ja nimet on varmasti tahallaan valittu sekavasti.
Minulle kuitenkin jäi vielä epäselväksi voiko hinta-parametriä käyttää sekä ominaisuuden taustalla että olioiden parametrinä itsessään? Ei ainakaan niin kauan kuin se on private, mitä vaaditaan ominaisuudelle. Kysymys kuuluu siis, että voinko pääohjelmassa jotenkin määrittää että tarkistetaanko onko hinta yli 30 vai en käyttämällä joko hinta- tai Hinta-muotoa?
Ominaisuus ei vaadi yksityistä (private) jäsentä tai ylipäänsä mitään muutakaan, oikeastaan get- ja set-funktioihin voi kirjoittaa melkein mitä tahansa. Eli kyllä, voit käyttää samaa julkista hinta-jäsentä ja ominaisuudessa palauttaa siitä alennetun hinnan. Yllä juuri esitin tähän sopivan Hinta-ominaisuuden. (Parametri tarkoittaa funktion suluissa määriteltyä muuttujaa, taisit sekoittaa termejä.)
Kurssilla ehkä neuvotaan käyttämään yksityistä jäsentä siksi, että usein ominaisuuden tarkoitus on juuri rajoittaa jäsenen käyttöä jotenkin ja silloin ei ole järkevää samaan aikaan antaa vapaata pääsyä jäseneen toisella nimellä. Esimerkiksi jos Hinta-ominaisuus hyväksyisi vain lukuja 0-1000, olisi väärin sallia toista kautta arvon muokkaus mielivaltaisesti.
uusiKoodaaja kirjoitti:
Kieltämättä tuo koodi on aika huono, jos kyseessä olisi oikea ohjelma. Tuo pääohjelma kuitenkin rajoittaa minua ja nimet on varmasti tahallaan valittu sekavasti.
Ei kannata kuvitella liikoja kurssinvetäjän omista taidoista ja huolellisuudesta. Kyseessä on tahaton typo, kirjoitusvirhe. Ja vaikka se olisi tarkoituksellinen suunnittelumoka, niin se olisi niin typerä virhe, että sinä voit koodarina korjata sen oma-aloitteisesti.
(Paitsi jos tehtävät tarkastetaan käyttäen jotain automatisoitua systeemiä, joka ehdottomasti vaatii tuollaiset nimet, jotta oman tehtävän saisi menemään läpi.)
Järkevintä olisi toki ottaa yhteyttä opettajaan ja kysyä, että miksi tehtävä vaikuttaa virheelliseltä.
Oonko ainoa jota häiritsee pahasti, että hinnan tietotyyppi on float?
Grez kirjoitti:
Oonko ainoa jota häiritsee pahasti, että hinnan tietotyyppi on float?
Koulutehtävien "harmittomista" oikoteistä tulee kaupallisen softan kulmakiviä. Kuka olisi uskonut että ihmiset oppivat esimerkistä? Tässäkin tapauksessa opettaja olisi voinut välttää aiheuttamasta epäsuoran kymmenvuotisen teknisen velan maailman ohjelmistokehitykselle käyttämällä vaikka int-tietotyyppiä ja senttejä.
Customer extends Person jne. koska asiakashan on henkilö??
vesikuusi kirjoitti:
– – int-tietotyyppiä ja senttejä.
C# sisältää decimal-tietotyypin, joka on nimenomaan kymmenjärjestelmään sopiva liukuluku, toisin kuin binäärijärjestelmässä toimiva float. Decimal-tyyppiä kannattaisi käyttää esimerkiksi juuri hintojen yhteydessä. Lukuarvon voi merkitä decimal-tyyppiseksi m-kirjaimella, esimerkiksi 0.9m
.
Metabolix kirjoitti:
(04.02.2021 22:25:17): ”– –” C# sisältää decimal-tietotyypin, joka on...
Totta turiset. On se terävä kieli!
Sinänsähän käsittääkseni Excel käyttää laskemiseen 64-bittisiä liukuluja, ja monet käyttää Exceliä raha-asioiden laskemiseen. Float on kuitenkin 32-bittinen liukuluku (vain 6-9 merkitsevää numeroa.)
Itse käyttäisin tosiaan decimal tietotyyppiä (joka on 128-bittinen liukuluku jonka kertoimen kanta on 10, eikä 2 kuten float:lla ja doublella). Mutta jos nyt haluaa prosessorin suoraan ymmärtämiä liukuluja käyttää, niin C#:ssa olisi myös double, joten sikälikin float on aika käsittämätön.
Toki jos seuraavana harjoituksena on huomata floatin ongelmat ja siirtyä johonkin järkevämpään, niin mikäs siinä.
Ciao uusiKoodaaja!
using System; class Kirja { public string nimi; public string kirjailija; public string kustantaja; public string teema; public float hinta; private float _hinta; public Kirja(string nimi, string kirjailija, string kustantaja, float _hinta, string teema) { this.nimi = nimi; this.kirjailija = kirjailija; this.kustantaja = kustantaja; Hinta = _hinta; //asetetaan parametrin arvolla -> (set) this.hinta = Hinta; //asetetaan julkisen muuttujan arvoksi <- (get) this.teema = teema; } public float Hinta { get { return _hinta > 30 ? _hinta * 0.9f : _hinta; } set { _hinta = value; } } }
class Program { //Tästä eteenpäin en voi muokata koodia static void Main(string[] args) { Kirja k = new Kirja("Kirja", "K. Kirjoittaja", "Kustannus Oy", 21.50f, "Dekkari"); Kirja k2 = new Kirja("Toinen Kirja", "O. Kirjailija", "Kustantaja Ab", 36.90f, "Fantasia"); Console.WriteLine(k.hinta); //mieti mistä arvo haetaan tässä Console.WriteLine(k2.Hinta); //ja miten arvo haetaan tässä } }
Tarkoituksella kinkkiseksi muotoiltu tehtävä, pistää miettimään 😊
Howdy ho, Neau33 v2.0?
Mitään kinkkistä ei ole tässä. Samaa tietoa ei kannata sentään tallentaa kahteen kertaan vain sen takia, että tehtävässä halutaan hakea se kahdella tavalla.
class Kirja { public float hinta; public Kirja(float _hinta) { hinta = _hinta; } public float Hinta { get { return hinta > 30 ? hinta * 0.9f : hinta; } } }
Howdy Meta(A)?
You almost figured it out, so 9.95 points for that!
But, when talking about coders there's one tiny little thing that makes you wonder if your version number increases or decreases when you're getting older. That's the main question.
What I do think my version number is something 0.2 nowadays and it keeps on going down.
Well, on my retirement days I just got a little excited again.😃
Metabolix kirjoitti:
class Kirja { public float hinta; public Kirja(float _hinta) { hinta = _hinta; } public float Hinta { get { return hinta > 30 ? hinta * 0.9f : hinta; } } }
Varsin eleganttia...
Aihe on jo aika vanha, joten et voi enää vastata siihen.