[C#] Constructor wil niet

Pagina: 1
Acties:

  • _Thanatos_
  • Registratie: Januari 2001
  • Laatst online: 06-03 20:19

_Thanatos_

Ja, en kaal

Topicstarter
Ik heb de volgende code (versimpeld):
C#:
1
2
3
4
5
6
7
8
9
public abstract class ClassA {
   private string field;

   public ClassA(string field) {
      this.field = field;
   }
}

public class ClassB: ClassA { }


En nu wil ik een ClassB instantiëren:
C#:
1
ClassB dingetje = new ClassB("field1");
Maar, zegt de compiler, er is geen constructor voor ClassB die 1 argument accepteert.

Uhm, denk ik dan, die is er wél. Ik heb tenslotte in ClassA zo'n constructor gemaakt en ClassB is van ClassA weer afgeleid. Door overerving neem je dan toch die constructor ook over :?

日本!🎌


  • LeX-333
  • Registratie: Maart 2004
  • Laatst online: 21-11-2016
Nee, in classB moet je opnieuw die constructor maken en vanuit die constructor de juiste constructor van classA aanroepen. Je kan niet direct de constructor van classA aanroepen.

Too many people, making too many problems


  • _Thanatos_
  • Registratie: Januari 2001
  • Laatst online: 06-03 20:19

_Thanatos_

Ja, en kaal

Topicstarter
Wat onhandig. Gewone methodes worden wel overgenomen...

Nu moet ik dus in al m'n descendant classes zoiets opnemen:
C#:
1
public ClassB(string field): base(field) { }


En ik maar denken dat koppiepeesten niet meer hoeft bij OOP. Nouja, heb ik in ieder geval weer een puntje gevonden waarop Delphi beter werkt :)

日本!🎌


  • Sybr_E-N
  • Registratie: December 2001
  • Nu online
Je maakt gebruik van een abstracte klasse. En van een abstracte klasse kun je instantie aanmaken, dus ook geen constructor aan roepen. Dat omdat een abstracte klassen geen implementatie heeft, die moet je juist definieren in je childklassen.

  • _Thanatos_
  • Registratie: Januari 2001
  • Laatst online: 06-03 20:19

_Thanatos_

Ja, en kaal

Topicstarter
die moet je juist definieren in je childklassen
Ook bij een gewone class werkt mijn idee niet... abstract heeft er niets mee te maken. Je zou zelfs zeggen dat de constructor van een abstracte class *juist* overgenomen wordt door descendants, omdat je een dergelijke class toch niet kan instantiëren.

日本!🎌


  • Orphix
  • Registratie: Februari 2000
  • Niet online
_Thanatos_ schreef op zondag 24 juli 2005 @ 17:52:
En ik maar denken dat koppiepeesten niet meer hoeft bij OOP. Nouja, heb ik in ieder geval weer een puntje gevonden waarop Delphi beter werkt :)
Nouja beter. Het idee is dat je als ontwerper van de derived class de control bij jezelf houdt, immers weet je altijd op welke manier een instantie wordt aangemaakt en kan je hierop inspelen.

Stel je hebt een base class 'Auto' en een derived class 'Mercedes'. Auto heeft een constructor: Auto(Color carColor) { }
waarmee je de kleur aangeeft. Mercedes maakt hier gebruik van en doet nog meer:
Mercedes(Color carColor) : base(carColor) {
PutStarOnGrill();
}

Stel dat de ontwikkelaar van Auto nu opeens besluit een extra constructor te maken, bv:
Auto() {
this.color = Pink;
}
Een default constructor welke de auto kleur op zwart zet.

Als base constructors nu ook toegangelijk zouden kunnen zijn, zonder expliciet gebruik van constructors in derived classes. Dan zou je dit kunnen doen:
Mercedes car = new Mercedes();

En dan zou je dus eindigen met een roze mercedes zonder ster op de grill!

  • _Thanatos_
  • Registratie: Januari 2001
  • Laatst online: 06-03 20:19

_Thanatos_

Ja, en kaal

Topicstarter
Mercedes car = new Mercedes();

En dan zou je dus eindigen met een roze mercedes zonder ster op de grill!
Dan moet je dat dus niet doen. Je bent nog altijd zelf de programmeur en ik mag aannemen dat je zelf wel weet wat wel en niet kan of mag. Bovendien is je ontwerp niet geheel correct, omdat PutStarOnGrill duidelijk niet in de constructor thuishoort. Dat de kleur "default" roze is, is prima, want een default constructor moet je toch hebben voor bijv serialisatie.

日本!🎌


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 20:53
_Thanatos_ schreef op maandag 25 juli 2005 @ 03:03:
Bovendien is je ontwerp niet geheel correct, omdat PutStarOnGrill duidelijk niet in de constructor thuishoort.
Hoezoe niet? ALs een mercedes altijd een ster op zijn grille heeft kan dat makkelijk goed zijn.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 04-05 13:09
farlane schreef op maandag 25 juli 2005 @ 09:43:
[...]

Hoezoe niet? ALs een mercedes altijd een ster op zijn grille heeft kan dat makkelijk goed zijn.
En het is natuurlijk maar een voorbeeld ;)

Hoewel er natuurlijk wel wat voor te zeggen valt dat wanneer je constructors niet overneemt automatisch de matching constructor van de parent gepakt zou kunnen worden.

Wat ik zelf ook wel jammer vind, is dat bij functies die een reference van het type van de eigen class returnen niet ook automatisch die van de afgeleide class zijn. BIjvoorbeeld dat bij een afgeleide MyTree van de class Tree de functie GetNext() ook een MyTree returnt ipv een Tree :P

Maar goed, da's ook wel weer logisch omdat functies die een Tree verwachten (en niet weten dat ze een MyTree meekrijgen) in de problemen zouden komen met het aanroepen van GetNext.

[ Voor 56% gewijzigd door riezebosch op 25-07-2005 10:35 ]

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 09:00

curry684

left part of the evil twins

_Thanatos_ schreef op zondag 24 juli 2005 @ 17:44:
Uhm, denk ik dan, die is er wél. Ik heb tenslotte in ClassA zo'n constructor gemaakt en ClassB is van ClassA weer afgeleid. Door overerving neem je dan toch die constructor ook over :?
Dit kan in geen enkele taal als er geen default constructor in de base class zit. Dwz. als het in Delphi wel kan vind ik dat een minpunt van Delphi. De compiler kan niet voor jou besluiten hoe je parameter 'field' gevuld wil hebben vanuit de derived class. Er zijn 2 logische oplossingen voor deze kwestie:
C#:
1
2
3
4
5
6
7
class ClassB : ClassA
{
  ClassB() : base("default")
  {
    ...
  }
}

of:
C#:
1
2
3
4
5
6
7
class ClassB : ClassA
{
  ClassB(string field) : base(field)
  {
    ...
  }
}

Omdat de compiler dit niet voor jou kan of mag besluiten is het een error.

[ Voor 4% gewijzigd door curry684 op 25-07-2005 10:46 ]

Professionele website nodig?


  • LeX-333
  • Registratie: Maart 2004
  • Laatst online: 21-11-2016
Er zijn hier twee problemen die veroorzaken dat een constructor altijd geimplementeerd moet worden:
1.
Beschouw een klasse hierarchie Object <- Voertuig <- Fiets <- Bakfiets en de volgende code (ok, C++, maar dat maakt het ff duidelijker omdat er dan niets engs onderwater gebeurd):
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Container
{
public:
  Container(const Object *o) : object(o) {}
  setItem(const Object *o) { object = o; }

private:
  Object *object;
};

Fietsenstalling : public Container
{
public:
  Fietsenstalling(const Fiets *f) : Container(f), fiets(f) { }
  setItem(const Fiets *f) { Container::setItem(f); fiets = f; }

private:
  Fiets *fiets;
};

int main(void)
{
  Voertuig *v = getVoertuig();
  Bakfiets *b = getBakfiets();
  Container a1(v), a2(b);
  Fietsenstalling b1(v), b2(b);

  return 0;
}

Container kan nu geinstantieerd worden met ieder object en zal dus alles accepteren, Fietsenstalling echter vereist een instantie van het type Fiets. Stel nu dat er een klasse Boekenkast is die erft van Object, kan ik dan nu ook een Fietsenstalling instantieren met een Boekenkast omdat ie dan gewoon de constructor van Container pakt? Nee natuurlijk niet, dan zou de pointer fiets niet geinitialiseerd worden en crashes veroorzaken (Want er is geen andere constructor in Fietsenstalling die aangeroepen kan worden). De code in de main functie laat ook al zien dat b1 eigenlijk fout is omdat je niet alle voertuigen in een Fietsenstalling kan zetten. Ik kan me ook niet voorstellen dat Delphi dit wel accepteerd, als dat wel zo is ben ik heel benieuwd hoe Delphi dit probleem oplost.

2.
Dit probleem gaat nog veel verder, het is namelijk ook toepasbaar op functies, in de bovenstaande code staan ook de functies setItem(). Hetzelfde is weer toepasbaar, kan ik nu de functie setItem() uit Fietsenstalling aanroepen met een Boekenkast en dat dan automatisch de functie setItem() uit Container aangeroepen wordt? Nee, doordat de setItem functie in Fietsenstalling opnieuw geimplementeerd wordt, worden alle overloads uit de base classes gemaskeerd.

Ok, dit zijn C++ voorbeelden, maar C# is eigenlijk een versimpelde variant van C++ (net als Java) en zal zich met dit soort problemen niet veel anders kunnen dan C++. Deze problemen zijn in de literatuur al stevig onderzocht en met imperatieve object georienteerde talen kun je niet veel andere zinnige oplossingen geven voor deze problemen. Natuurlijk kun je zeggen dat de programmeur maar beter op moet letten, maar als je met 100 programmeurs aan een project werkt met een grote klasse hierarchie zie je dit soort dingen wel heel makkelijk over het hoofd. Sterker nog, weinig programmeurs zijn op de hoogte van de situatie zoals ik in punt 2 omschreven heb.

Too many people, making too many problems


  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 15:47

Creepy

Tactical Espionage Splatterer

Delphi:
1
2
3
4
5
6
7
8
9
10
  TClassA = class
  private
    FBlaat: string;
  public
    constructor Create(blaat: string);
  end;

  TClassB = class(TClassA)
  public
  end;

Delphi genereert geen default contructor. Echter wordt de constructor van A wel overgenomen in B. Je kan in Delphi dan alleen een instantie van B creeeren door TClassB.create('dinges'); aan te roepen. Note: dit is alleen zo als B geen ctors heeft.

Zodra B een ctor heeft zul je de ctor's van A niet meer kunnen gebruiken om een instantie van B te creeeren.

Overigens kent (in elk geval Delphi7) nog geen abstracte classes. Wel abstracte methods. Mocht je de constructor van A abstract maken en een instantie van B willen creeeren dan krijg je compile time een warning dat je een abtracte method uit A gebruikt. Tijdens runtime krijg je een Exception te zien. Waarom de compiler een warning geeft en geen error is mij eigenlijk een raadsel.

[ Voor 28% gewijzigd door Creepy op 25-07-2005 12:13 ]

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 09:00

curry684

left part of the evil twins

_Thanatos_ schreef op zondag 24 juli 2005 @ 17:52:
Nouja, heb ik in ieder geval weer een puntje gevonden waarop Delphi beter werkt :)
Je bent er verder trouwens mee bekend dat Anders Hejlsberg, de lead architect van C#, bij z'n vorige werkgever Borland de taal Delphi heeft ontwikkeld, en hij dus vast wel een reden heeft om dit probleem op te lossen? :)
Anders Hejlsberg, a distinguished engineer at Microsoft, led the team that designed the C# (pronounced C Sharp) programming language. Hejlsberg first vaulted onto the software world stage in the early eighties by creating a Pascal compiler for MS-DOS and CP/M. A very young company called Borland soon hired Hejlsberg and bought his compiler, which was thereafter marketed as Turbo Pascal. At Borland, Hejlsberg continued to develop Turbo Pascal and eventually led the team that designed Turbo Pascal's replacement: Delphi. In 1996, after 13 years with Borland, Hejlsberg joined Microsoft, where he initially worked as an architect of Visual J++ and the Windows Foundation Classes (WFC). Subsequently, Hejlsberg was chief designer of C# and a key participant in the creation of the .NET framework. Currently, Anders Hejlsberg leads the continued development of the C# programming language.

Professionele website nodig?


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
LeX-333 schreef op maandag 25 juli 2005 @ 11:00:
Er zijn hier twee problemen die veroorzaken dat een constructor altijd geimplementeerd moet worden:
1.
Beschouw een klasse hierarchie Object <- Voertuig <- Fiets <- Bakfiets en de volgende code (ok, C++, maar dat maakt het ff duidelijker omdat er dan niets engs onderwater gebeurd):
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Container
{
public:
  Container(const Object *o) : object(o) {}
  setItem(const Object *o) { object = o; }

private:
  Object *object;
};

Fietsenstalling : public Container
{
public:
  Fietsenstalling(const Fiets *f) : Container(f), fiets(f) { }
  setItem(const Fiets *f) { Container::setItem(f); fiets = f; }

private:
  Fiets *fiets;
};

int main(void)
{
  Voertuig *v = getVoertuig();
  Bakfiets *b = getBakfiets();
  Container a1(v), a2(b);
  Fietsenstalling b1(v), b2(b);

  return 0;
}
Fout. Een FietsenStalling is geen Container en mag dus niet inheriten. Klassieke OO fout.
Pas het Liskov Substitution Principle toe: is een Fietsenstalling overal te gebruiken waar een Container te gebruiken is? Dan en slechts dan is een Fietsenstalling een Container. De werkelijkheid is dat een Fietsenstalling een Container gebruikt, cq HAS-A versus IS-A

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein

Pagina: 1