Perintä
Edellisessä osiossa tarkasteltiin johdettujen luokkien käsitettä. Tämä ominaisuus, jossa luokka perii ominaisuuksia toiselta luokalta, tunnetaan nimellä perintä.
Vaikka perinnän käsite on jo tuttu, käsitellään sitä nyt hieman laajemmin, jotta ymmärrys syvenee.
Kertauksena seuraavassa on esimerkki perinnästä:
index.cs
1234567891011121314151617181920212223242526272829303132333435363738394041424344#pragma warning disable CS0169 // To disable some unnecessary compiler warnings for this example. Using this is not a recommended practice. using System; class Mammal { int age; float weight; // kilogram (1 kg = 2.2 pounds) } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { Cat myCat = new Cat(); Dog myDog = new Dog(); myCat.speak(); myDog.speak(); } }
Direktiiviä #pragma warning disable "warning code"
voidaan käyttää tiettyjen kääntäjän varoitusten poistamiseen käytöstä. Varoitusten poistamista ei kuitenkaan yleensä suositella, sillä niiden ohittaminen voi johtaa ohjelmassa odottamattomaan käyttäytymiseen.
Yllä olevassa koodissa on yksi yliluokka nimeltä Mammal
sekä kaksi periytettyä luokkaa nimeltä Cat
ja Dog
.
Huomaa, että yhdelläkään luokalla ei ole erikseen määriteltyä konstruktoria, mikä tarkoittaa, että nämä luokat käyttävät oletuskonstruktoria, kun olio luodaan.
Oletuskonstruktori tarjotaan automaattisesti ohjelmointikielen toimesta, jos luokalle ei ole määritelty yhtään konstruktoria. Oletuskonstruktori on käytännössä tyhjä konstruktori, joka ei sisällä koodia, esimerkiksi public className() {}
on esimerkki oletuskonstruktorista. Koska se ei alustaa mitään attribuuttia erikseen, kaikki attribuutit saavat oletusarvot – joita kutsutaan myös nolla-arvoiksi.
Luodaan konstruktori Mammal
-luokalle manuaalisesti, joka alustaa Mammal
-olion tietyillä tiedoilla:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243444546#pragma warning disable CS0169 // To disable some unnecessary compiler warnings for this example. Using this is not a recommended practice. using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); } }
Jos yrität kääntää tämän ohjelman, konsoliin ilmestyy virheilmoituksia. Näiden virheiden ymmärtämiseksi sinun täytyy ensin tuntea kaksi tärkeää konstruktoreihin liittyvää käsitettä.
Ensimmäinen on se, että kun määrittelet konstruktorin luokalle, kyseisellä luokalla ei enää ole oletuskonstruktoria, ja näin ollen määrittelemäsi konstruktori toimii luokan pääkonstruktorina, joka tässä tapauksessa on:
index.cs
12345public Mammal(int age, float weight) { this.age = age; this.weight = weight; }
Siksi uuden olion luomisen yhteydessä on aina annettava konstruktorin vaaditut argumentit oikeassa järjestyksessä:
index.cs
1234567// Incorrect ways to create 'Mammal', will show an error Mammal m1 = new Mammal(); Mammal m1 = new Mammal(10); Mammal m1 = new Mammal(42.0f); // Correct way to create 'Mammal', will execute fine. Mammal m1 = new Mammal(10, 42.0f);
Toiseksi, johdetuilla luokilla voi myös olla konstruktoreita, mutta ennen kuin johdetun luokan konstruktori suoritetaan, suoritetaan myös kantaluokan (vanhemman) konstruktori:
index.cs
1234567891011121314151617181920212223242526272829303132333435#pragma warning disable CS0169 // To disable some unnecessary warnings, using this is not a recommended practice. using System; class Mammal { int age; float weight; // kg // No attribute is initialized explicitly in this constructor // Hence, all attributes will take up "zero" values // It is similar to a "default" constructor except it outputs a message public Mammal() { Console.WriteLine("Mammal Constructor Called"); } } class Dog : Mammal { string breed; public Dog() { Console.WriteLine("Dog Constructor Called"); } } class ConsoleApp { static void Main() { Dog myDog = new Dog(); } }
Kun suoritat tämän koodin, huomaat, että WriteLine()
-metodi kutsutaan 'Mammal'-konstruktorista, joka on yliluokka. Tämä tarkoittaa, että on sääntö, että kantaluokan konstruktori (tunnetaan myös nimellä base constructor) kutsutaan aina ennen johdetun luokan konstruktoria.
Tämä sääntö pätee myös monitasoisessa periytymisessä:

Yllä olevassa kaaviossa Kitten
-konstruktori kutsuu Cat
-konstruktoria ennen omaansa, mutta koska Cat
on myös johdettu luokka, se kutsuu Mammal
-konstruktoria ennen itseään, ja Mammal
kutsuu Animal
-konstruktoria ennen omaa konstruktoriaan. Näin ollen ensimmäinen konstruktori, joka suoritetaan, on yliluokan konstruktori — eli Animal
-luokan konstruktori, ja siitä jatketaan alaspäin.
Jos yliluokan konstruktori ei ota argumentteja, kääntäjä kutsuu sitä automaattisesti. Tästä syystä yllä olevassa esimerkissä 'Mammal'-konstruktoria kutsuttiin automaattisesti. Tarkastellaan kuitenkin uudelleen virheellistä koodia:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); } }
Yllä olevassa koodissa saat kaksi virhettä, jotka käytännössä tarkoittavat, että et ole manuaalisesti kutsunut pohjaluokan konstruktoreita — koska se vaatii joitakin argumentteja, sinun täytyy kutsua se manuaalisesti. Perussyntaksi yliluokan konstruktorin manuaaliseen kutsumiseen on seuraava:
index.cs
12345678910class DerivedClassName : ParentClassName { // ... attributes // ... methods public DerivedClassName(int arg1, int arg2, ...) : base(arg1, arg2, ...) { // code here } }
Esimerkki:
index.cs
1234567891011121314151617181920212223242526272829303132using System; class ExampleParentClass { int value1; int value2; public ExampleParentClass(int value1, int value2) { this.value1 = value1; } } class ExampleDerivedClass : ExampleParentClass { int value3; // The value1 and value2 arguments are passed to the base class's contructor public ExampleDerivedClass(int value1, int value2, int value3) : base (value1, value2) { this.value3 = value3; } } class ConsoleApp { static void Main() { var testObject = new ExampleDerivedClass(5, 7, 9); } }
Tämän syntaksin avulla voit välittää kaikki tarvittavat tiedot Mammal
-konstruktorille Cat
- ja Dog
-konstruktoreiden kautta korjataksesi aiemmin esiintyneen virheen:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; } public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public Cat(int age, float weight, string furPattern) : base(age, weight) { this.furPattern = furPattern; } public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); // Creating a "Dog" object with some data Dog d1 = new Dog(10, 42.5f, "Dobermann"); Console.WriteLine("Executed Successfully"); } }
Toinen tärkeä konstruktoreiden ominaisuus on, että voit ylikuormittaa konstruktoreita samalla tavalla kuin ylikuormitamme muitakin metodeja. Voit luoda useita konstruktoreita, joilla on vaihteleva määrä argumentteja:
index.cs
12345678910111213141516171819202122232425class Mammal { int age; float weight; // kg // 1st constructor public Mammal() { // We leave it empty for this example // Since it's empty, it mimics the "default" constructor } // 2nd constructor public Mammal(int age) { this.age = age; } // 3rd constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; } }
Tässä tapauksessa Mammal
-luokalla on kolme konstruktoria. Voit siis alustaa tai luoda nisäkäsolion kolmella eri tavalla, ja kääntäjä valitsee käytettävän konstruktorin argumenttien määrän ja tyypin perusteella:
index.cs
1234// All Correct var m1 = new Mammal(); var m2 = new Mammal(10); var m3 = new Mammal(10, 42.5f);
Tämä tarkoittaa myös sitä, että voit kutsua mitä tahansa kolmesta konstruktorista perityn luokan konstruktoreista. Esimerkiksi kaikki seuraavat ovat sallittuja:
index.cs
123456789101112131415161718// Using 3rd base constructor public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; } // Using 2nd base constructor public Dog(int age, string breed) : base(age) { this.breed = breed; } // Using 1st base constructor // If the base constructor has no arguments then it is automatically called (similar to the default constructor), so we don't necessarily need to write 'base()' public Dog(string breed) { this.breed = breed; }
Kootaan yhteen yllä olevat kaksi koodinpätkää ja lisätään joitakin Console.WriteLine
-lauseita nähdäksemme, missä järjestyksessä konstruktorit suoritetaan käytännössä:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394using System; class Mammal { int age; float weight; // kg // 1st Constructor public Mammal() { // We leave it empty for this example // Since it's empty, it mimics the "default" constructor // The attributes are initialized with zero values Console.WriteLine("Mammal - Constructor 1 Called"); } // 2nd Constructor public Mammal(int age) { this.age = age; Console.WriteLine("Mammal - Constructor 2 Called"); } // 3rd Constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; Console.WriteLine("Mammal - Constructor 3 Called"); } } class Dog : Mammal { string breed; public Dog() { Console.WriteLine("Dog - Constructor 1 Called"); } // Using 1st Mammal constructor // We don't necessarily need to write 'base()' in this case // It automatically finds and calls the constructor with no arguments public Dog(string breed) { this.breed = breed; Console.WriteLine("Dog - Constructor 2 Called"); } // Using 2nd Mammal constructor public Dog(int age, string breed) : base(age) { this.breed = breed; Console.WriteLine("Dog - Constructor 3 Called"); } // Using 3rd Mammal constructor public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; Console.WriteLine("Dog - Constructor 4 Called"); } public void speak() { Console.WriteLine("Woof!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object using different constructors Mammal m1 = new Mammal(10, 42.0f); Mammal m2 = new Mammal(10); Mammal m3 = new Mammal(); Console.WriteLine("----------"); // Seperator, for ease of reading output // Creating a "Dog" object using different constructors Dog d1 = new Dog(10, 42.0f, "Dobermann"); Console.WriteLine(""); Dog d2 = new Dog(10, "Dobermann"); Console.WriteLine(""); Dog d3 = new Dog("Dobermann"); Console.WriteLine(""); Dog d4 = new Dog(); } }
Nyt kun tunnet perinnän eri ominaisuudet, sinun tulisi myös tietää, miten ja milloin niitä käytetään oikein. Seuraavat asiat kannattaa pitää mielessä harkittaessa perintään perustuvaa luokkarakennetta:
Tasapaino yksinkertaisuuden ja joustavuuden välillä: Konstruktorin ylikuormitus mahdollistaa useiden erilaisten konstruktorien luomisen, jotka ottavat vastaan erilaisia argumentteja, mutta liiallinen käyttö voi tehdä koodista monimutkaista ja vaikeasti ylläpidettävää. Paras käytäntö on pitää luokan koodi lyhyenä, ytimekkäänä ja selkeänä. Vältä liian monien konstruktorien luomista luokalle, jotta yksinkertaisuuden ja joustavuuden välinen tasapaino säilyy.
Pidä konstruktorit yksinkertaisina: Konstruktorien tulisi pääasiassa vastata olion alustamisesta perusdatalla. Paras käytäntö on välttää tarpeetonta prosessointia ja monimutkaista logiikkaa konstruktorissa. Jos laskentaa tai logiikkaa tarvitaan, on parempi luoda sitä varten erillinen metodi.
;Huono käytäntö:;
index.cs
123456789101112131415161718192021222324252627class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; if (accountType == "Savings") { // Plus 1 Percent this.balance = balance + balance * 0.01; } else if (accountType == "HighYieldSavings") { // Plus 5 percent this.balance = balance + balance * 0.05; } else { this.balance = balance; } } }
Hyvä käytäntö:
index.cs
123456789101112131415161718192021222324252627282930class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; this.balance = balance; monthlyInterest(); } // This method might be used in other places too private void monthlyInterest() { if(accountType == "Savings") { // Plus 1 Percent balance += balance * 0.01; } else if(accountType == "HighYieldSavings") { // Plus 5 percent balance += balance * 0.05; } } }
Tärkeiden attribuuttien alustaminen: Kaikki olion tärkeät attribuutit tulee alustaa oikeilla arvoilla, jotta ne toimivat oikein – vaikka kyseessä olisi parametriton konstruktori.
Huono käytäntö:
index.cs
123456789101112131415public class Car { private string brand; private string model; private int year; private double price; // Constructor does not initialize important attributes // It is also generally not a good idea to have constructors without any arguments if they're not needed. public Car() { // No initialization of attributes Console.WriteLine("Car Created"); } }
Hyvä käytäntö:
index.cs
123456789101112131415161718192021222324252627282930313233343536373839public class Car { private string brand; private string model; private int year; private double price; // Good: Constructor initializes important attributes // It also checks if the values are correct // In this case the if-else statements are not unnecessary since they are important for ensuring that the object functions correctly. public Car(string brand, string model, int year, double price) { this.brand = brand; this.model = model; // Validate and set the year // The first public car was created in 1886 :) if (year > 1886) { this.year = year; } else { Console.WriteLine("Invalid year. Setting year to default."); this.year = DateTime.Now.Year; // Set to current year as default } // Validate and set the price if (price >= 0) { this.price = price; } else { Console.WriteLine("Invalid price. Setting price to default."); this.price = 0; // Set to a default value } } }
1. Mikä ominaisuus mahdollistaa useiden rakentajien luomisen luokalle?
2. Saatat tarvita jotakin aiemmissa osioissa käsitellyistä käsitteistä tässä kysymyksessä. Alla olevassa koodissa on virhe riveillä 15 ja 16. Tarkastele koodia huolellisesti ja päätä, mikä on tehokas korjaus tälle virheelle:
Kiitos palautteestasi!
Kysy tekoälyä
Kysy tekoälyä
Kysy mitä tahansa tai kokeile jotakin ehdotetuista kysymyksistä aloittaaksesi keskustelumme
Can you explain more about constructor overloading?
What happens if I don't call the base constructor in a derived class?
Can you give an example of multilevel inheritance with constructors?
Awesome!
Completion rate improved to 2.04
Perintä
Pyyhkäise näyttääksesi valikon
Edellisessä osiossa tarkasteltiin johdettujen luokkien käsitettä. Tämä ominaisuus, jossa luokka perii ominaisuuksia toiselta luokalta, tunnetaan nimellä perintä.
Vaikka perinnän käsite on jo tuttu, käsitellään sitä nyt hieman laajemmin, jotta ymmärrys syvenee.
Kertauksena seuraavassa on esimerkki perinnästä:
index.cs
1234567891011121314151617181920212223242526272829303132333435363738394041424344#pragma warning disable CS0169 // To disable some unnecessary compiler warnings for this example. Using this is not a recommended practice. using System; class Mammal { int age; float weight; // kilogram (1 kg = 2.2 pounds) } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { Cat myCat = new Cat(); Dog myDog = new Dog(); myCat.speak(); myDog.speak(); } }
Direktiiviä #pragma warning disable "warning code"
voidaan käyttää tiettyjen kääntäjän varoitusten poistamiseen käytöstä. Varoitusten poistamista ei kuitenkaan yleensä suositella, sillä niiden ohittaminen voi johtaa ohjelmassa odottamattomaan käyttäytymiseen.
Yllä olevassa koodissa on yksi yliluokka nimeltä Mammal
sekä kaksi periytettyä luokkaa nimeltä Cat
ja Dog
.
Huomaa, että yhdelläkään luokalla ei ole erikseen määriteltyä konstruktoria, mikä tarkoittaa, että nämä luokat käyttävät oletuskonstruktoria, kun olio luodaan.
Oletuskonstruktori tarjotaan automaattisesti ohjelmointikielen toimesta, jos luokalle ei ole määritelty yhtään konstruktoria. Oletuskonstruktori on käytännössä tyhjä konstruktori, joka ei sisällä koodia, esimerkiksi public className() {}
on esimerkki oletuskonstruktorista. Koska se ei alustaa mitään attribuuttia erikseen, kaikki attribuutit saavat oletusarvot – joita kutsutaan myös nolla-arvoiksi.
Luodaan konstruktori Mammal
-luokalle manuaalisesti, joka alustaa Mammal
-olion tietyillä tiedoilla:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243444546#pragma warning disable CS0169 // To disable some unnecessary compiler warnings for this example. Using this is not a recommended practice. using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); } }
Jos yrität kääntää tämän ohjelman, konsoliin ilmestyy virheilmoituksia. Näiden virheiden ymmärtämiseksi sinun täytyy ensin tuntea kaksi tärkeää konstruktoreihin liittyvää käsitettä.
Ensimmäinen on se, että kun määrittelet konstruktorin luokalle, kyseisellä luokalla ei enää ole oletuskonstruktoria, ja näin ollen määrittelemäsi konstruktori toimii luokan pääkonstruktorina, joka tässä tapauksessa on:
index.cs
12345public Mammal(int age, float weight) { this.age = age; this.weight = weight; }
Siksi uuden olion luomisen yhteydessä on aina annettava konstruktorin vaaditut argumentit oikeassa järjestyksessä:
index.cs
1234567// Incorrect ways to create 'Mammal', will show an error Mammal m1 = new Mammal(); Mammal m1 = new Mammal(10); Mammal m1 = new Mammal(42.0f); // Correct way to create 'Mammal', will execute fine. Mammal m1 = new Mammal(10, 42.0f);
Toiseksi, johdetuilla luokilla voi myös olla konstruktoreita, mutta ennen kuin johdetun luokan konstruktori suoritetaan, suoritetaan myös kantaluokan (vanhemman) konstruktori:
index.cs
1234567891011121314151617181920212223242526272829303132333435#pragma warning disable CS0169 // To disable some unnecessary warnings, using this is not a recommended practice. using System; class Mammal { int age; float weight; // kg // No attribute is initialized explicitly in this constructor // Hence, all attributes will take up "zero" values // It is similar to a "default" constructor except it outputs a message public Mammal() { Console.WriteLine("Mammal Constructor Called"); } } class Dog : Mammal { string breed; public Dog() { Console.WriteLine("Dog Constructor Called"); } } class ConsoleApp { static void Main() { Dog myDog = new Dog(); } }
Kun suoritat tämän koodin, huomaat, että WriteLine()
-metodi kutsutaan 'Mammal'-konstruktorista, joka on yliluokka. Tämä tarkoittaa, että on sääntö, että kantaluokan konstruktori (tunnetaan myös nimellä base constructor) kutsutaan aina ennen johdetun luokan konstruktoria.
Tämä sääntö pätee myös monitasoisessa periytymisessä:

Yllä olevassa kaaviossa Kitten
-konstruktori kutsuu Cat
-konstruktoria ennen omaansa, mutta koska Cat
on myös johdettu luokka, se kutsuu Mammal
-konstruktoria ennen itseään, ja Mammal
kutsuu Animal
-konstruktoria ennen omaa konstruktoriaan. Näin ollen ensimmäinen konstruktori, joka suoritetaan, on yliluokan konstruktori — eli Animal
-luokan konstruktori, ja siitä jatketaan alaspäin.
Jos yliluokan konstruktori ei ota argumentteja, kääntäjä kutsuu sitä automaattisesti. Tästä syystä yllä olevassa esimerkissä 'Mammal'-konstruktoria kutsuttiin automaattisesti. Tarkastellaan kuitenkin uudelleen virheellistä koodia:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); } }
Yllä olevassa koodissa saat kaksi virhettä, jotka käytännössä tarkoittavat, että et ole manuaalisesti kutsunut pohjaluokan konstruktoreita — koska se vaatii joitakin argumentteja, sinun täytyy kutsua se manuaalisesti. Perussyntaksi yliluokan konstruktorin manuaaliseen kutsumiseen on seuraava:
index.cs
12345678910class DerivedClassName : ParentClassName { // ... attributes // ... methods public DerivedClassName(int arg1, int arg2, ...) : base(arg1, arg2, ...) { // code here } }
Esimerkki:
index.cs
1234567891011121314151617181920212223242526272829303132using System; class ExampleParentClass { int value1; int value2; public ExampleParentClass(int value1, int value2) { this.value1 = value1; } } class ExampleDerivedClass : ExampleParentClass { int value3; // The value1 and value2 arguments are passed to the base class's contructor public ExampleDerivedClass(int value1, int value2, int value3) : base (value1, value2) { this.value3 = value3; } } class ConsoleApp { static void Main() { var testObject = new ExampleDerivedClass(5, 7, 9); } }
Tämän syntaksin avulla voit välittää kaikki tarvittavat tiedot Mammal
-konstruktorille Cat
- ja Dog
-konstruktoreiden kautta korjataksesi aiemmin esiintyneen virheen:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758using System; class Mammal { int age; float weight; // kg public Mammal(int age, float weight) { this.age = age; this.weight = weight; } } class Dog : Mammal { string breed; public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; } public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { string furPattern; public Cat(int age, float weight, string furPattern) : base(age, weight) { this.furPattern = furPattern; } public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object with some data Mammal m1 = new Mammal(10, 42.0f); // Creating a "Dog" object with some data Dog d1 = new Dog(10, 42.5f, "Dobermann"); Console.WriteLine("Executed Successfully"); } }
Toinen tärkeä konstruktoreiden ominaisuus on, että voit ylikuormittaa konstruktoreita samalla tavalla kuin ylikuormitamme muitakin metodeja. Voit luoda useita konstruktoreita, joilla on vaihteleva määrä argumentteja:
index.cs
12345678910111213141516171819202122232425class Mammal { int age; float weight; // kg // 1st constructor public Mammal() { // We leave it empty for this example // Since it's empty, it mimics the "default" constructor } // 2nd constructor public Mammal(int age) { this.age = age; } // 3rd constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; } }
Tässä tapauksessa Mammal
-luokalla on kolme konstruktoria. Voit siis alustaa tai luoda nisäkäsolion kolmella eri tavalla, ja kääntäjä valitsee käytettävän konstruktorin argumenttien määrän ja tyypin perusteella:
index.cs
1234// All Correct var m1 = new Mammal(); var m2 = new Mammal(10); var m3 = new Mammal(10, 42.5f);
Tämä tarkoittaa myös sitä, että voit kutsua mitä tahansa kolmesta konstruktorista perityn luokan konstruktoreista. Esimerkiksi kaikki seuraavat ovat sallittuja:
index.cs
123456789101112131415161718// Using 3rd base constructor public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; } // Using 2nd base constructor public Dog(int age, string breed) : base(age) { this.breed = breed; } // Using 1st base constructor // If the base constructor has no arguments then it is automatically called (similar to the default constructor), so we don't necessarily need to write 'base()' public Dog(string breed) { this.breed = breed; }
Kootaan yhteen yllä olevat kaksi koodinpätkää ja lisätään joitakin Console.WriteLine
-lauseita nähdäksemme, missä järjestyksessä konstruktorit suoritetaan käytännössä:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394using System; class Mammal { int age; float weight; // kg // 1st Constructor public Mammal() { // We leave it empty for this example // Since it's empty, it mimics the "default" constructor // The attributes are initialized with zero values Console.WriteLine("Mammal - Constructor 1 Called"); } // 2nd Constructor public Mammal(int age) { this.age = age; Console.WriteLine("Mammal - Constructor 2 Called"); } // 3rd Constructor public Mammal(int age, float weight) { this.age = age; this.weight = weight; Console.WriteLine("Mammal - Constructor 3 Called"); } } class Dog : Mammal { string breed; public Dog() { Console.WriteLine("Dog - Constructor 1 Called"); } // Using 1st Mammal constructor // We don't necessarily need to write 'base()' in this case // It automatically finds and calls the constructor with no arguments public Dog(string breed) { this.breed = breed; Console.WriteLine("Dog - Constructor 2 Called"); } // Using 2nd Mammal constructor public Dog(int age, string breed) : base(age) { this.breed = breed; Console.WriteLine("Dog - Constructor 3 Called"); } // Using 3rd Mammal constructor public Dog(int age, float weight, string breed) : base(age, weight) { this.breed = breed; Console.WriteLine("Dog - Constructor 4 Called"); } public void speak() { Console.WriteLine("Woof!"); } } class ConsoleApp { static void Main() { // Creating a "Mammal" object using different constructors Mammal m1 = new Mammal(10, 42.0f); Mammal m2 = new Mammal(10); Mammal m3 = new Mammal(); Console.WriteLine("----------"); // Seperator, for ease of reading output // Creating a "Dog" object using different constructors Dog d1 = new Dog(10, 42.0f, "Dobermann"); Console.WriteLine(""); Dog d2 = new Dog(10, "Dobermann"); Console.WriteLine(""); Dog d3 = new Dog("Dobermann"); Console.WriteLine(""); Dog d4 = new Dog(); } }
Nyt kun tunnet perinnän eri ominaisuudet, sinun tulisi myös tietää, miten ja milloin niitä käytetään oikein. Seuraavat asiat kannattaa pitää mielessä harkittaessa perintään perustuvaa luokkarakennetta:
Tasapaino yksinkertaisuuden ja joustavuuden välillä: Konstruktorin ylikuormitus mahdollistaa useiden erilaisten konstruktorien luomisen, jotka ottavat vastaan erilaisia argumentteja, mutta liiallinen käyttö voi tehdä koodista monimutkaista ja vaikeasti ylläpidettävää. Paras käytäntö on pitää luokan koodi lyhyenä, ytimekkäänä ja selkeänä. Vältä liian monien konstruktorien luomista luokalle, jotta yksinkertaisuuden ja joustavuuden välinen tasapaino säilyy.
Pidä konstruktorit yksinkertaisina: Konstruktorien tulisi pääasiassa vastata olion alustamisesta perusdatalla. Paras käytäntö on välttää tarpeetonta prosessointia ja monimutkaista logiikkaa konstruktorissa. Jos laskentaa tai logiikkaa tarvitaan, on parempi luoda sitä varten erillinen metodi.
;Huono käytäntö:;
index.cs
123456789101112131415161718192021222324252627class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; if (accountType == "Savings") { // Plus 1 Percent this.balance = balance + balance * 0.01; } else if (accountType == "HighYieldSavings") { // Plus 5 percent this.balance = balance + balance * 0.05; } else { this.balance = balance; } } }
Hyvä käytäntö:
index.cs
123456789101112131415161718192021222324252627282930class Customer { string name; string accountType; double balance; public Customer (string name, string accountType, double balance) { this.name = name; this.accountType = accountType; this.balance = balance; monthlyInterest(); } // This method might be used in other places too private void monthlyInterest() { if(accountType == "Savings") { // Plus 1 Percent balance += balance * 0.01; } else if(accountType == "HighYieldSavings") { // Plus 5 percent balance += balance * 0.05; } } }
Tärkeiden attribuuttien alustaminen: Kaikki olion tärkeät attribuutit tulee alustaa oikeilla arvoilla, jotta ne toimivat oikein – vaikka kyseessä olisi parametriton konstruktori.
Huono käytäntö:
index.cs
123456789101112131415public class Car { private string brand; private string model; private int year; private double price; // Constructor does not initialize important attributes // It is also generally not a good idea to have constructors without any arguments if they're not needed. public Car() { // No initialization of attributes Console.WriteLine("Car Created"); } }
Hyvä käytäntö:
index.cs
123456789101112131415161718192021222324252627282930313233343536373839public class Car { private string brand; private string model; private int year; private double price; // Good: Constructor initializes important attributes // It also checks if the values are correct // In this case the if-else statements are not unnecessary since they are important for ensuring that the object functions correctly. public Car(string brand, string model, int year, double price) { this.brand = brand; this.model = model; // Validate and set the year // The first public car was created in 1886 :) if (year > 1886) { this.year = year; } else { Console.WriteLine("Invalid year. Setting year to default."); this.year = DateTime.Now.Year; // Set to current year as default } // Validate and set the price if (price >= 0) { this.price = price; } else { Console.WriteLine("Invalid price. Setting price to default."); this.price = 0; // Set to a default value } } }
1. Mikä ominaisuus mahdollistaa useiden rakentajien luomisen luokalle?
2. Saatat tarvita jotakin aiemmissa osioissa käsitellyistä käsitteistä tässä kysymyksessä. Alla olevassa koodissa on virhe riveillä 15 ja 16. Tarkastele koodia huolellisesti ja päätä, mikä on tehokas korjaus tälle virheelle:
Kiitos palautteestasi!