[C#] Constructor behavior icm inheritance

Pagina: 1
Acties:

  • whoami
  • Registratie: December 2000
  • Laatst online: 09:01
Ik heb net ff wat zitten spelen met C# en eens nagegaan hoe constructors in een structuur van classes die van elkaar overerven zich gedragen.

Stel deze code:

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
30
31
32
33
34
35
class Base
{

    public Base()
    {
        MyMethod();
    }

    public virtual void MyMethod()
    {
        Console.WriteLine ("In Base class method");
    }
}

class Derived : Base
{
    public Derived()
    {
        MyMethod();
    }

    public override void MyMethod()
    {
         Console.WriteLine ("In Derived");
    }
}

class Program()
{
    public static void Main()
    {
        Base b = new Base();
        Base d = new Derived();
    }
}


Nu, wat is de output van dit simpel programmaatje?
Mijn eerste idee was dit:
code:
1
2
In Base class Method
In Derived


Echter, niets is minder waar. Dit is de output:
code:
1
2
3
In Base class Method
In Derived
In Derived


Als je door de code stept mbhv de debugger, dan zie je dat bij de creatie van het Derived object, de constructor van de Base class ook aangeroepen wordt voordat de constructor van Derived zelf wordt aangeroepen.
Base roept de MyMethod method aan, en de overriden method van Derived wordt uitgevoerd. Daarna wordt de constructor van Derived zelf uitgevoerd, en wordt de MyMethod ook nog eens uitgevoerd.

Nu vraag ik me echter af:
Waarom wordt die constructor van Base eigenlijk aangeroepen? Ik geef in m'n code nl. nergens aan dat die constructor moet aangeroepen worden; er staat nl. niet:
code:
1
2
3
4
public Derived() : Base()
{
    //
}

Is dit eigenlijk logisch? Derived is eigenlijk een Base, en dan kan je stellen dat die constructor moet aangeroepen worden. Als je geen standaard constructor opneemt in beide classes, maar die vervangt door een one-argument constructor, dan klaagt de compiler als je de base-constructor niet expliciet aanroept.

https://fgheysels.github.io/


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

whoami schreef op 15 augustus 2004 @ 23:10:
Waarom wordt die constructor van Base eigenlijk aangeroepen? Ik geef in m'n code nl. nergens aan dat die constructor moet aangeroepen worden
De constructor wordt impliciet aangeroepen als jij het niet expliciet doet. Stel dat dit niet automatisch zou gebeuren, dan zou het kunnen voorkomen dat een object niet volledig uitgeconstruct is.
Als je geen standaard constructor opneemt in beide classes, maar die vervangt door een one-argument constructor, dan klaagt de compiler als je de base-constructor niet expliciet aanroept.
De compiler kan natuurlijk nooit zekerweten hoe in het geval van een of meerdere argumenten, deze van een waarde voorzien moeten worden. Dus het moet nu echt expliciet door de programmeur worden opgegeven.

De output is trouwens ook wel te verklaren:
De 1e regel is eenvoudig, de base class.

De volgende 2 regels: "in derived". Eerst word de constructor van derived aangeroepen, die meteen de constructor van base aanroept voordat hij zelf ook maar iets mag doen. In de constructor van base wordt de methode aangeroepen die in derived overriden is dus die moet worden uitgevoerd. Nadat de constructor van base is afgerond ga je nu beginnen met de rest van de derived constructor en die roept op zijn beurt weer die methode aan. Het gevolg is dus dat je 2 keer krijgt te zien "in derived".

[ Voor 29% gewijzigd door Alarmnummer op 15-08-2004 23:28 ]


  • whoami
  • Registratie: December 2000
  • Laatst online: 09:01
Mjah, eigenlijk heb je gelijk. Die constructor moet impliciet aangeroepen worden, aangezien de members van Base natuurlijk ook geinitialiseerd moeten worden.
Dat er dan 2x 'Derived' komt te staan, ivm de virtual/overriden methods snap ik wel. ;)

https://fgheysels.github.io/


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Ik snap de twee keer Derived niet. Als het zo belangrijk is dat members worden geinitialiseerd, waarom roept C# dan Derived::myMethod aan voordat de Derived members zjn geinitialiseerd?

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


  • whoami
  • Registratie: December 2000
  • Laatst online: 09:01
Aangezien die MyMethod virtual is, en overriden is in Derived; en er een Derived object gemaakt wordt, wordt de overriden method in Derived uitgevoerd.

Echter, mijn bovenstaand voorbeeld is gewoon bad programming practice. Als je FxCop over dat projectje heen laat, dan laat hij dit weten:
Constructors should not call virtual methods defined by the class

Base..ctor()' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences:

https://fgheysels.github.io/


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

MSalters schreef op 16 augustus 2004 @ 12:44:
Ik snap de twee keer Derived niet. Als het zo belangrijk is dat members worden geinitialiseerd, waarom roept C# dan Derived::myMethod aan voordat de Derived members zjn geinitialiseerd?
Dat doet ie niet, lees nog maar eens goed :)

Dit programmaatje zal in C++ exact dezelfde output geven.


Erm my err, in C++ zal ie dit geven:
code:
1
2
3
In Base class Method
In Base class Method
In Derived

Ik las even verkeerd :) Ik vind dit inderdaad vreemd gedrag dan van C#....

[ Voor 21% gewijzigd door curry684 op 16-08-2004 13:57 ]

Professionele website nodig?


  • whoami
  • Registratie: December 2000
  • Laatst online: 09:01
Mjah, .... er is voor beiden wat te zeggen eigenlijk:

De constructor van Base wordt aangeroepen, maar de virtual method MyMethod wordt daarin aangeroepen. Echter, die method is overriden in Derived, en aangezien je een Derived object creeërt, wordt de Derived::MyMethod aangeroepen.
Echter, je kan ook zeggen dat hij op dat moment in de constructor van Base zit, en dat hij eigenlijk Base::MyMethod moet aanroepen....

Wat doet managed C++ daar trouwens mee, of VB.NET ?

https://fgheysels.github.io/


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

whoami schreef op 16 augustus 2004 @ 13:51:
Echter, je kan ook zeggen dat hij op dat moment in de constructor van Base zit, en dat hij eigenlijk Base::MyMethod moet aanroepen....
C++ moet wel, omdat de vtable 'tijdens de constructor' wordt gebouwd. Ergo terwijl ie in Base::Base zit bestaat wat hem betreft Derived nog niet en is Base::MyMethod dus de 'hoogste' virtual uit de hierarchie.
Wat doet managed C++ daar trouwens mee, of VB.NET ?
MC++ zal zich aan dezelfde regels moeten onderwerpen als C++ wegens portability, VB.NET weet ik niet.

Professionele website nodig?


  • d00d
  • Registratie: September 2003
  • Laatst online: 16-09-2025

d00d

geen matches

Impliciet is natuurlijk ieder object een afgeleide van Object, de volgende code geeft dan ook als resultaat:

Derived.ToString
Base.ToString
Derived.ToString

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System;

public class Base {
    public override string ToString() {
        return "Base.ToString";
    }
}

public class Derived : Base {
    public override string ToString() {
        return "Derived.ToString";
    }
}

public class TestApp {
    public static void Main(string[] args) {
        Object a = new Derived();
        Base b = new Base();
        Base c = new Derived();
        Console.WriteLine("{0}\n{1}\n{2}", a, b, c);
    }
}


"public class Base" is hetzelfde als "public class Base : Object", en het verwonderd mij niets dat de ToString van de "most derived" class wordt aangeroepen, zoals het hoort.
MC++ zal zich aan dezelfde regels moeten onderwerpen als C++ wegens portability, VB.NET weet ik niet.
Zou het niet zo zijn dat MC++ zich net als C# en VB.NET zich allemaal aan dezelfde standaard moeten houden. Objecten moeten immers zonder problemen kunnen worden uitgewisseld?

42.7 percent of all statistics are made up on the spot.


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

d00d: lees de vraag nog eens goed :) Wat jij illustreert is standaard gedrag ja, je hebt het over virtual methods op een constructed object. Waar we het over hebben is het gedrag van virtual methods tijdens constructie :)

Professionele website nodig?


  • whoami
  • Registratie: December 2000
  • Laatst online: 09:01
d00d schreef op 16 augustus 2004 @ 14:29:
Impliciet is natuurlijk ieder object een afgeleide van Object, de volgende code geeft dan ook als resultaat:

Derived.ToString
Base.ToString
Derived.ToString
Dit is dan ook helemaal logisch. Dat is gewoon polymorfisch gedrag, zoals het hoort.

Echter, in mijn voorbeeld werden die methods opgeroepen vanuit de constructor (wat eigenlijk niet echt 'good practice is'.)
Zou het niet zo zijn dat MC++ zich net als C# en VB.NET zich allemaal aan dezelfde standaard moeten houden. Objecten moeten immers zonder problemen kunnen worden uitgewisseld?
Hmm... Idd.... MC++ zou eigenlijk net hetzelfde moeten doen als C#.

https://fgheysels.github.io/


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

whoami schreef op 16 augustus 2004 @ 14:32:
[...]

Hmm... Idd.... MC++ zou eigenlijk net hetzelfde moeten doen als C#.
Ze compileren beiden naar IL, maar IL is afaik dermate lowlevel dat er niet de actie van 'constructie' in vast wordt gelegd, maar de sequentie van acties. En in dat geval zou ik persoonlijk van MC++ de language-based compatibility met ISO-C++ verwachten.

Professionele website nodig?


  • whoami
  • Registratie: December 2000
  • Laatst online: 09:01
Mjah, maar om aan de .NET regels te voldoen moet een type in .NET eigenlijk voldoen aan het CTS (Common Type System).
Het lijkt me toch dat dergelijke zaken daar in moeten gedefinieerd zijn.

https://fgheysels.github.io/


  • whoami
  • Registratie: December 2000
  • Laatst online: 09:01
Ik heb me net even aan MC++ gewaagd (ieks) (al heeft curry inm'n notes gezet dat ik me ver van C++ moet houden), en dat vertoond hetzelfde gedrag als C#:

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
__gc class Base
{
public:
    Base()
    {
    MyMethod();
    }

    virtual void MyMethod()
    {
            Console::WriteLine("Base");
    }
};



__gc class Derived : Base
{
public:
    Derived()
    {
         MyMethod();
    }

    virtual void MyMethod()
    {
    Console::WriteLine("Derived");
    }

};

int _tmain()
{
    // TODO: Please replace the sample code below with your own.
    Console::WriteLine(S"Hello World");

    Base b = new Base();
    Base d = new Derived();

    Console::ReadLine();

    return 0;
}


Dit geeft als output:
In Base
In Derived
In Derived

[ Voor 7% gewijzigd door whoami op 16-08-2004 14:50 ]

https://fgheysels.github.io/


  • d00d
  • Registratie: September 2003
  • Laatst online: 16-09-2025

d00d

geen matches

Je heb gelijkt, ik doe niks in de constructor, dom voorbeeld...
whoami schreef op 16 augustus 2004 @ 14:32:
Dit is dan ook helemaal logisch. Dat is gewoon polymorfisch gedrag, zoals het hoort.

Echter, in mijn voorbeeld werden die methods opgeroepen vanuit de constructor (wat eigenlijk niet echt 'good practice is'.)
Maar wat jij hier laat zien is ook 'gewoon' polymorfisch gedrag zoals gedefinieerd door de CLR.

42.7 percent of all statistics are made up on the spot.


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

whoami schreef op 16 augustus 2004 @ 14:47:
Ik heb me net even aan MC++ gewaagd (ieks) (al heeft curry inm'n notes gezet dat ik me ver van C++ moet houden), en dat vertoond hetzelfde gedrag als C#:
offtopic:
die note is getrashed bij je vertrek, maar ik ga er zo wel eentje zetten over dat je de edit-knop moet gebruiken :P
Dit geeft als output:

[...]
Dan is blijkbaar het constructiegedrag toch in IL vastgelegd. Eng :/

Professionele website nodig?


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
Wel raar, je kunt dus in de MyMethod van je derived kennelijk gebruik maken van ongeinitialiseerde variabelen uit Derived, omdat zijn constructor nog niet is voltooid? FF proberen ....

Ja dus, deze code :
C#:
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
30
31
32
33
34
    public class Base
    {
        public Base()
        {
            MyMethod();
        }

        public virtual void MyMethod()
        {
            System.Console.Out.WriteLine("Base");
        }
    }

    public class Derived : Base
    {
        string b = null;

        public Derived()
        {
            b = "Blaat";

            MyMethod();
        }

        public override void MyMethod()
        {
            System.Console.Out.WriteLine("Derived");

            if( b != null )
                System.Console.Out.WriteLine("b constructed");
            else
                System.Console.Out.WriteLine("b NOT constructed");
        }
    }


en dit

C#:
1
2
Base b      = new Base();
Derived d   = new Derived();


Geeft deze output:
Base
Derived
b NOT constructed
Derived
b constructed

[ Voor 68% gewijzigd door farlane op 16-08-2004 15:09 ]

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.


  • whoami
  • Registratie: December 2000
  • Laatst online: 09:01
Waarom eng?

https://fgheysels.github.io/


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

Omdat MC++ daarmee de C++-standaard overtreedt om exact de reden die [rml]farlane in "[ C#] Constructor behavior icm inheritanc..."[/rml] geeft :)

Professionele website nodig?


  • 12_0_13
  • Registratie: April 2004
  • Laatst online: 12-02 13:19
Misschien is de compiler zo slim om te checken of hij iets moet initaliseren, ziet dat het niet hoeft, en dus maar toestaat meteen de derived methods te gebruiken?

Overigens sluit ik me erbij aan dat dit gedrag op zijn minst bedendelijk is, en ik liever de gewone C++ standaard heb.

  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

12_0_13 schreef op 16 augustus 2004 @ 15:06:
Misschien is de compiler zo slim om te checken of hij iets moet initaliseren, ziet dat het niet hoeft, en dus maar toestaat meteen de derived methods te gebruiken?
Uh zodra een compiler denkt slim te moeten zijn knikker ik 'm het raam uit hoor 8)7

1 standaard, 1 uitvoering, nooit onverwachte resultaten :)

Professionele website nodig?


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-05 23:07

.oisyn

Moderator Devschuur®

Demotivational Speaker

curry684 schreef op 16 augustus 2004 @ 14:52:
Dan is blijkbaar het constructiegedrag toch in IL vastgelegd. Eng :/
Dat is iets wat een IL nodig heeft om z'n eigen integriteit te waarborgen. .Net definieert standaard gedrag mbt objecten, het zou raar zijn als je daar omheen zou kunnen werken als je een andere IL compiler gebruikt.

Aan de andere kant vind ik dit gedefinieerde gedrag wel erg fout. Op het moment dat je in Base zit is this ook gewoon een Base, geen Derived. Derhalve zou het niet mogen dat Derived::MyMethod aangeroepen wordt, this is immers nog geen Derived totdat de constructor van Derived doorlopen is. Op het moment dat Derived::MyMethod vanuit Base::Base wordt aangeroepen bevindt het zich dus in een inconsistente staat.

Ik vraag me trouwens af hoe Whidbey hier tegenover staat, ivm de betere samenvoeging van native C++ en C++/CLR

[ Voor 8% gewijzigd door .oisyn op 16-08-2004 15:58 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 13:33

Creepy

Tactical Espionage Splatterer

* Creepy wil nog een interresant linkje aandragen: http://www.interact-sw.co.../08/12/destructordeviance

"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


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-05 23:07

.oisyn

Moderator Devschuur®

Demotivational Speaker

Dat heeft hier weinig mee te maken, de mensen daar verwarren de term destructor met finalizer, terwijl dat 2 compleet verschillende dingen zijn. En ja, MS heeft een grote fout gemaakt door de finalizer-syntax in C# hetzelfde te laten zijn als de destructor-syntax in C++, een grove fout die ze ook publiekelijk hebben toegegeven. Met .Net 2.0 komt er wel ondersteuning voor echte destructors zoals C++ die kent. Vandaar dat ik mij in mijn vorige post ook afvroeg hoe het probleem in deze draad zich met .net 2.0 gaat gedragen

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

.oisyn schreef op 16 augustus 2004 @ 15:56:
[...]


Dat is iets wat een IL nodig heeft om z'n eigen integriteit te waarborgen. .Net definieert standaard gedrag mbt objecten, het zou raar zijn als je daar omheen zou kunnen werken als je een andere IL compiler gebruikt.
Het is een kwestie van waar je de scheiding legt... dat de CLR uniform gedrag voor objecten definieert en vereist is zeker waar, maar dat houdt imho niet dat de brontaal zijn taalspecifieke elementen niet mag uitdragen. Mag ik in C++ plots switchen op strings omdat het in C# mag? Mag ik in COBOL.NET plots non-global storage definieren? En leuker nog op het object-gebied: mag C++ nog wel destructors hebben die in geen enkele andere taal functioneren?

Imho bepaalt de brontaal de volgorde van executie van de in die taal uitgevoerde functionaliteit. Dat dit op dit vlak problemen oplevert zodra je een MC++ class van een C# class afleidt die van een MC++ class afgeleid is, klopt. Maar heb je niet hetzelfde probleem als je een C# class afleidt van een MC++ class met een virtual destructor?

Professionele website nodig?


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-05 23:07

.oisyn

Moderator Devschuur®

Demotivational Speaker

curry684 schreef op 16 augustus 2004 @ 16:03:
Maar heb je niet hetzelfde probleem als je een C# class afleidt van een MC++ class met een virtual destructor?
Volgens mij kun je een MC++ class geen destructor geven, alleen een finalizer :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

.oisyn schreef op 16 augustus 2004 @ 16:29:
[...]

Volgens mij kun je een MC++ class geen destructor geven, alleen een finalizer :)
Hmmm ergo ze wijken gewoon op alle punten van de standaard af om maar binnen de CLR te passen :X

* curry684 roept gatsie en duikt weer lekker terug C# in :)

Professionele website nodig?


  • whoami
  • Registratie: December 2000
  • Laatst online: 09:01
.oisyn schreef op 16 augustus 2004 @ 15:56:
[...]

Ik vraag me trouwens af hoe Whidbey hier tegenover staat, ivm de betere samenvoeging van native C++ en C++/CLR
Ik heb het ook met .NET 2.0 geprobeerd (De Express producten zijn toch .NET 2.0, net zoals Whidbey dacht ik), en daar heb je hetzelfde gedrag zoals eerder in dit topic vermeld.

https://fgheysels.github.io/


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-05 23:07

.oisyn

Moderator Devschuur®

Demotivational Speaker

[rml].oisyn in "[ C#] Constructor behavior icm inheritanc..."[/rml]

Ik heb onlangs een presentatie zitten kijken van ik geloof Herb himself over de nieuwe C++/CLR bindings in whidbey. Hij begon ongeveer zo
Who of you know the managed extensions currently available for C++ in Visual Studio .Net.

...

Ok, the most of you do. We're very, VERY sorry
Overigens klopte wat ik in mijn vorige post zei niet helemaal. Je mag een MC++ class best een destructor geven, maar aangezien .Net van voor 2.0 nog geen destructors kent heeft die ook alleen nut in MC++ zelf. Uit de MSDN:
If a __gc class derives from a __gc class authored in another language, the Finalize method, if any, of the base class is treated as a destructor, and is called automatically at the end of the destructor of the derived class.

An object of a __gc class can only be created by a call to new, which returns a pointer to the object. During execution of a program, the common language runtime deletes unused objects and compacts the runtime heap. The garbage collector will call the destructor of any deleted objects. The order in which destructors are called is unpredictable.

A destructor can be invoked directly by the user or via operator delete. It is equivalent to explicitly calling the destructor as described above (Section 4.2). No memory is freed until a subsequent garbage collection cycle. The destructor will not be called again if the memory for the object is reclaimed during a subsequent garbage collection cycle.
Er staat alleen niets over de omgekeerde situatie, een __gc class die in een andere taal wordt gesubclassed... Maar aangezien je in een andere .Net taal geen destructors kunt definieren lijkt mij dat ook weinig nut hebben

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 22-05 16:53
Weet iemand ook de reden waarom MS in zijn oneindige wijsheid heeft besloten dat dit op een dergelijke manier moet afwijken van de C++ standaard ? Ik kan nog niet echt voordelen ontdekken namelijk ... sterker nog ....

[edit]
Ik lees nu ( via de link die Creepy gaf ) dat de Java runtime eenzelfde gedrag heeft. Dat zou wel verklaren waarom .Net dit ook doet. :)

[ Voor 31% gewijzigd door farlane op 16-08-2004 23:36 ]

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.


  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
farlane schreef op 16 augustus 2004 @ 23:31:
Weet iemand ook de reden waarom MS in zijn oneindige wijsheid heeft besloten dat dit op een dergelijke manier moet afwijken van de C++ standaard ? Ik kan nog niet echt voordelen ontdekken namelijk ... sterker nog ....

[edit]
Ik lees nu ( via de link die Creepy gaf ) dat de Java runtime eenzelfde gedrag heeft. Dat zou wel verklaren waarom .Net dit ook doet. :)
Ik hoop dat MS wel een beter reden heeft dan dat Java dat ook doet ;). Ik vindt het inderdaad wel vreemd dat de derived methode aangeroepen wordt. Niet zozeer omdat dat tegen de c++ regels ingaat ( Je bent tenslotte bezig met een nieuwe programmeertaal dus kan je het beter meteen goed doen ), maar meer omdat je inderdaad nog helemaal geen derived type hebt dus hoe kan je daar nou een methode van aanroepen.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

Heeft iemand al gechecked wat er zoal fout gaat als je de mogelijke problemen uit gaat lokken eilijk?

Zeker met C++ moet dat toch Access Violations galore zijn? :?

Professionele website nodig?


Verwijderd

In de IL, eigenlijk de CIL is dit gedrag wel heel duidelijk:

Dit is de construtor van de klasse base, waar de constructor van de klasse Object wordt aangeroepen en de virtuele methode MyMethod();
code:
1
2
3
4
5
6
7
8
9
10
11
.method public hidebysig specialname rtspecialname 
        instance void  .ctor() cil managed
{
  // Code size       13 (0xd)
  .maxstack  1
  IL_0000:  ldarg.0
  IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
  IL_0006:  ldarg.0
  IL_0007:  callvirt   instance void ConsoleApplication1.Base::MyMethod()
  IL_000c:  ret
} // end of method Base::.ctor


Dit is de CIL van de klasse Derived waar de constructor van de basisklasse (Base) wordt aangeroepen, waar op dus ook MyMethod(); wordt uitgevoerd, en daarna pas MyMethod();
code:
1
2
3
4
5
6
7
8
9
10
11
.method public hidebysig specialname rtspecialname 
        instance void  .ctor() cil managed
{
  // Code size       13 (0xd)
  .maxstack  1
  IL_0000:  ldarg.0
  IL_0001:  call       instance void ConsoleApplication1.Base::.ctor()
  IL_0006:  ldarg.0
  IL_0007:  callvirt   instance void ConsoleApplication1.Base::MyMethod()
  IL_000c:  ret
} // end of method Derived::.ctor


Hiermee vertel ik dus weinig nieuws :+. Maar het is mss toch interessant om eens aan te halen dat ildasm.exe voor het verklaren van dit gedrag een zeer handige tool is.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-05 23:07

.oisyn

Moderator Devschuur®

Demotivational Speaker

De "fout" zit 'm in het feit dat een object bij constructie vanuit de virtual machine al gelijk het type meekrijgt dat het moet zijn, alvorens de betreffende constructor aangeroepen wordt. De VM maakt dus een nieuw object en maakt daar een Derived van, en roept vervolgens Derived::Derived aan. Een this is Derived in Base::Base zal dan ook wel true opleveren

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
.oisyn schreef op 17 augustus 2004 @ 01:16:
De "fout" zit 'm in het feit dat een object bij constructie vanuit de virtual machine al gelijk het type meekrijgt dat het moet zijn, alvorens de betreffende constructor aangeroepen wordt. De VM maakt dus een nieuw object en maakt daar een Derived van, en roept vervolgens Derived::Derived aan. Een this is Derived in Base::Base zal dan ook wel true opleveren
in base levert "this is derived" inderdaad true op als je een Derived construct.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • whoami
  • Registratie: December 2000
  • Laatst online: 09:01
Nu zou ik wel eens willen weten waarom er voor dit gedrag gekozen is, zowel door Sun als door MS.
Een zoektocht op google leverde niet direct iets bruikbaars op.

https://fgheysels.github.io/


  • eghie
  • Registratie: Februari 2002
  • Niet online

eghie

Spoken words!

Visual Basic .NET:
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
30
    Public Class Base

        Public Sub New()
            MyMethod()
        End Sub

        Public Overridable Sub MyMethod()
            Console.WriteLine("In Base class method")
        End Sub

    End Class

    Public Class Derived
        Inherits Base

        Public Sub New()
            MyMethod()
        End Sub

        Public Overrides Sub MyMethod()
            Console.WriteLine("In Derived class method")
        End Sub

    End Class

    Sub Main()
        Dim b As New Base
        Dim d As New Derived
        Console.ReadLine()
    End Sub

Dit is in VB.NET en dit is de output:
code:
1
2
3
In Base class method
In Derived class method
In Derived class method

Hierbij zie je dus dat VB.NET dezelfde output geeft als C# en MC++.

Ik denk eigenlijk dat hij nooit meteen de constructor pakt als je object instatieërt. Volgens mij kijkt hij eerst naar z'n methoden/variablen/etc. Kijk maar naar de volgende code:
C#:
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class Base
    {

        public Base()
        {
            MyMethod();
        }

        public virtual void MyMethod()
        {
            Console.WriteLine ("In Base class method");
        }
    }

    class Derived : Base
    {
        string b = "blaat";

        public Derived()
        {
            MyMethod();
        }

        public override void MyMethod()
        {
            Console.WriteLine ("In Derived");

            if (b != null)
            {
                Console.WriteLine("B is constructed and has the following value: " + b);
            }
            else
            {
                Console.WriteLine("B is NOT constructed");
            }
        }
    }

    class Class1
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            //
            // TODO: Add code to start application here
            //
            Base b = new Base();
            Base d = new Derived();
            Console.ReadLine();
        }
    }

Dit is de output:
code:
1
2
3
4
5
In Base class method
In Derived
B is constructed and has the following value: blaat
In Derived
B is constructed and has the following value: blaat

En als je hierbij de debugger volgt zul je ook zien dat hij eerst de variablen enzo aanmaakt en pas als de constructor word aangeroepen dat hij dan eerst de base classe gaat aanmaken.

Verwijderd

farlane schreef op 16 augustus 2004 @ 23:31:
Weet iemand ook de reden waarom MS in zijn oneindige wijsheid heeft besloten dat dit op een dergelijke manier moet afwijken van de C++ standaard ? Ik kan nog niet echt voordelen ontdekken namelijk ... sterker nog ....
Een reden zou kunnen zijn dat het de drang is om alles makkelijker te willen maken, vooral voor beginners. Vooral beginners willen juist vaak dat-gewoon-de-virtual-method aangeroepen wordt. C# doet dat dus, en komt tegemoet aan de eisen van dergelijke lieden. Echter, als je er wat over nadenkt (zoals in deze thread), dan kom je erachter dat het de zaken juist gecompliceerder maakt.

Waarom het ook voor MC++ geldt? Het is het gedrag van C#, tot nu toe eigenlijk de enige 'first class citisen' taal op de CLR. De designer van C# objecten houdt er rekening mee dat virtual calls in de constructor een call naar een derived object opleveren. Als een MC++ class inherit van een C# class, zal dus ook dat gedrag vertoont moeten worden.

Andersom, in een C++ class houdt de designer er geen rekening mee dat een virtual call naar een derived gaat in de ctor. Echter, als een C# programmeur van een (M)C++ class will inheritten, dan wil deze dat alle calls die in de ctor v/d base gemaakt worden naar zijn of haar class gaan. Aangezien C# 'de baas' is op het .NET platform, bepaald deze taal dus ook de semantics van andere talen op het platform.

Je zou zeggen dat in het geval van een MC++ class die van een andere MC++ class inherit, wel de standaard C++ volgorde kan worden aangehouden. Echter, je kunt als designer van een MC++ class nooit zeggen dat alleen andere MC++ classen hiervan mogen inheritten.

Taal neutraliteit op het .NET platform (tot zover), is dus een grote grap. Je kunt er helemaal niet alle talen naar compileren, maar elke taal moet eerst getransformeerd worden tot een C# achtig geheel met iets andere syntax.

Overigens blijkt uit de laatste berichten van onze vriend Herb, dat het nu wel de bedoeling is om 100% standaard C++* in zijn geheel als een assembly te compileren (dus zonder native code). Dit is dan, in tegenstelling tot wat eerst werd gedacht, inclusief MI en templates, en ik neem dus aan ook het goede ctor gedrag.

*, behalve alle standaard libraries, zodat je in de praktijk nog steeds niet echt standaard C++ kunt compileren. Echter, third parties (dinkumware bv) mogen natuurlijk wel een lib als assembly uitbrengen, en zijn daar dan ook al mee bezig.

  • whoami
  • Registratie: December 2000
  • Laatst online: 09:01
Verwijderd schreef op 17 augustus 2004 @ 14:33:
[...]


Een reden zou kunnen zijn dat het de drang is om alles makkelijker te willen maken, vooral voor beginners. Vooral beginners willen juist vaak dat-gewoon-de-virtual-method aangeroepen wordt. C# doet dat dus, en komt tegemoet aan de eisen van dergelijke lieden. Echter, als je er wat over nadenkt (zoals in deze thread), dan kom je erachter dat het de zaken juist gecompliceerder maakt.
Zoals je reeds aangeeft: dit gedrag versimpelt het IMO echter niet; het laat zelfs 'fout' gedrag toe. Gelukkig bestaat er zo'n tool als FxCop, en meldt die FxCop dan ook dat je iets doet wat je beter niet doet.
Waarom het ook voor MC++ geldt? Het is het gedrag van C#, tot nu toe eigenlijk de enige 'first class citisen' taal op de CLR. De designer van C# objecten houdt er rekening mee dat virtual calls in de constructor een call naar een derived object opleveren. Als een MC++ class inherit van een C# class, zal dus ook dat gedrag vertoont moeten worden.
Idd, dit is gewoon de CLS / CLR die dit specifieert. Als je language interop wilt hebben, dan zijn dat dingen die in die talen hetzelfde moeten zijn.
Andersom, in een C++ class houdt de designer er geen rekening mee dat een virtual call naar een derived gaat in de ctor. Echter, als een C# programmeur van een (M)C++ class will inheritten, dan wil deze dat alle calls die in de ctor v/d base gemaakt worden naar zijn of haar class gaan. Aangezien C# 'de baas' is op het .NET platform, bepaald deze taal dus ook de semantics van andere talen op het platform.
Eigenlijk mag je nooit virtual methods aanroepen vanuit je constructor. (Zie ook de FxCop rule ConstructorsShouldNotCallBaseClassVirtualMethods.
C# is niet de baas op het .NET platform; de CLR/CLS/CTS zijn de baas op het platform.
Taal neutraliteit op het .NET platform (tot zover), is dus een grote grap. Je kunt er helemaal niet alle talen naar compileren, maar elke taal moet eerst getransformeerd worden tot een C# achtig geheel met iets andere syntax.
Nee hoor, als je een taal wilt hebben die binnen .NET kan lopen, dan moet die taal aan de CLR/CLS/CTS voldoen.
Taal interop binnen .NET werkt dus enkel met .NET talen (logisch). AFAIK kan je geen C# class maken die inherit van een non-managed C++ class bv.

https://fgheysels.github.io/


Verwijderd

eghie schreef op 17 augustus 2004 @ 14:19:
En als je hierbij de debugger volgt zul je ook zien dat hij eerst de variablen enzo aanmaakt en pas als de constructor word aangeroepen dat hij dan eerst de base classe gaat aanmaken.
Initialiseren van member variabelen gebeurt inderdaad voordat de constructor wordt aangeroepen. Echter, dit bekent wel dat je dan alleen maar simpele waarden kunt toekennen. Iets als:

C++:
1
2
3
4
5
6
7
8
9
10
11
class Foo {

DBConnection dbCOnnection;

public: Foo() {

 dbConnection = DB.getConnection( "myname", "mypassword" );

}

};


zal dus op deze manier niet werken.

Verwijderd

whoami schreef op 17 augustus 2004 @ 14:40:
Zoals je reeds aangeeft: dit gedrag versimpelt het IMO echter niet; het laat zelfs 'fout' gedrag toe. Gelukkig bestaat er zo'n tool als FxCop, en meldt die FxCop dan ook dat je iets doet wat je beter niet doet.
Inderdaad, maar dat is juist het hele concept. Beginners klagen altijd dat sommige dingen onlogisch zijn, omdat ze er gewoon niet goed over nagedacht hebben. Om dan maar aan die beginners tegemoet te komen geven de fabrikanten toe. Veel mensen kunnen dan het produkt beginnen te gebruiken. Als ze eenmaal een gevorderde gebruiker zijn, zien ze in dat hun eisen in het begin best wel dom worden. Op dat moment zijn ze echter wel slim genoeg om er (met enig zuchten mischien) omheen te kunnen werken. Ergo: een fabrikant zal eerder aan de eisen van beginners willen voldoen dan aan gevorderden. Achter zowel C++ als C zit niet primair een fabrikant, dus daar speelt dit issue wat minder.

In OS interface ontwerp zie je dit ook. Vooral in Windows XP. Al dat overdreven makkelijker willen maken voor beginners maakt het alleen maar moeilijker voor gevorderden.
Idd, dit is gewoon de CLS / CLR die dit specifieert. Als je language interop wilt hebben, dan zijn dat dingen die in die talen hetzelfde moeten zijn.
Moeilijk punt. Er valt zowel wat voor als tegen te zeggen. Als je high level dingen wilt doen als de ene taal laten inheritten van de andere, dan moet zulk soort gedrag inderdaad wel vast staan. Aan de andere kant zitten zulke dingen natuurlijk niet in IL zelf gedefineerd. Het is gewoon het gedrag van de bestaande (CLR conforme) compilers die dit doen.

Eventueel zou je een flag in je specificatie kunnen opnemen dat inheritten door andere talen verbied. Je hebt al de support voor het C# sealed keyword, dus zo moeilijk kan dat niet zijn.
C# is niet de baas op het .NET platform; de CLR/CLS/CTS zijn de baas op het platform.
Nouja, via-via wel. C# is namelijk weer de baas over CLR/CLS/CTS. Of anders gezegd, de CLR en consorten zijn ontwikkeld naar de specificaties van C#, niet andersom. MS heeft (naar alle waarschijnlijkheid) niet eerst de CLR ontworpen en toen pas gekeken wat voor taal daar het beste op zou kunnen draaien.

Hoewel ik geen expert ben op het gebied van de CLR en dus mogelijk nu wat blaat, komt het op mij over (als ik de feature set bekijk), dat de CLR precies die functionaliteit heeft die C# nodig heeft. Er zitten geen dingen in die niet gebruikt worden in C# (zodat MI dus nu niet mogelijk is). Zoals gezegd, ik ben zelf geen CLR expert, maar veel andere mensen denken er ook zo over.

Of was het je nog niet opgevallen dat elke taal die naar IL gecompiled wordt opeens erg op C# gaat lijken? Er is (AFAIK) nog geen enkele bestaande taal die gewoon zo, zonder taal aanpassingen gecompiled kon worden. In theorie zou dat wel moeten kunnen voor een VM.
Taal interop binnen .NET werkt dus enkel met .NET talen (logisch). AFAIK kan je geen C# class maken die inherit van een non-managed C++ class bv.
Maar moet language interop dan zo'n groot ideaal zijn dat het vereist dat talen (neem bv visual basic) compleet overhoopt gehaalt moeten worden?

  • whoami
  • Registratie: December 2000
  • Laatst online: 09:01
Verwijderd schreef op 17 augustus 2004 @ 15:04:
[...]


Inderdaad, maar dat is juist het hele concept. Beginners klagen altijd dat sommige dingen onlogisch zijn, omdat ze er gewoon niet goed over nagedacht hebben. Om dan maar aan die beginners tegemoet te komen geven de fabrikanten toe. Veel mensen kunnen dan het produkt beginnen te gebruiken. Als ze eenmaal een gevorderde gebruiker zijn, zien ze in dat hun eisen in het begin best wel dom worden. Op dat moment zijn ze echter wel slim genoeg om er (met enig zuchten mischien) omheen te kunnen werken. Ergo: een fabrikant zal eerder aan de eisen van beginners willen voldoen dan aan gevorderden. Achter zowel C++ als C zit niet primair een fabrikant, dus daar speelt dit issue wat minder.

In OS interface ontwerp zie je dit ook. Vooral in Windows XP. Al dat overdreven makkelijker willen maken voor beginners maakt het alleen maar moeilijker voor gevorderden.
Ik denk niet dat dit een argument is dat achter die beslissing kan zitten.
Het kan gewoon niet zijn dat een programmeertaal ontworpen wordt met als achterliggende gedachte dat bepaalde features op zo'n manier geimplementeerd worden omdat beginnende programmeurs dan in eerste instantie het boeltje beter snappen. Mocht dat zo zijn, dan ben je gewoon verkeerd bezig. Dan ben je gewoon bezig verkeerde dingen aan te leren.
Een dergelijke taal zou je ook nooit serieus mogen nemen.

Daarnaast kan je de gebruikers van een OS ook niet vergelijken met programmeurs. (Tuurlijk, die laatste gebruiken ook datzelfde OS, maar er wordt toch van hen geacht dat ze wat meer achterliggende kennis hebben).
Maar moet language interop dan zo'n groot ideaal zijn dat het vereist dat talen (neem bv visual basic) compleet overhoopt gehaalt moeten worden?
VB.NET kan je niet vergelijken met het 'oude' VB. VB.NET is er imho enkel aan toegevoegd om de huidige VB6 programmeurs naar .NET te lokken, en dat ze een taal zouden hebben met een syntax waar ze zich thuis invoelen.
De verschillen tussen VB.NET en VB zijn zo groot, en de gelijkenissen met C# zijn zo klein, dat ze imho VB.NET beter niet ontwikkeld hadden.

Trouwens, volgens mij hebben ze weldegelijk eerst het nagedacht over het platform, de CLR, etc.... en hebben ze dan een nieuwe taal ontworpen (C#) die speciaal op dat platform gericht was, en niet andersom zoals jij stelt.
Ik zou hier echter wat research moeten voor doen, maar de tijd ontbreekt me een beetje.

[ Voor 22% gewijzigd door whoami op 17-08-2004 15:13 ]

https://fgheysels.github.io/


  • d00d
  • Registratie: September 2003
  • Laatst online: 16-09-2025

d00d

geen matches

Ik snap nog steeds niets van alle commotie. Ik denk dat .Net hier op de juiste manier de OO regels toepast. Als je in de constructor een virtuele method aanroept (wat je dus beter niet kunt doen volgens FxCop), dan WEET je toch dat de most-derived method zal worden gebruikt. Als je dat niet wilt dan moet je MyMethod dus niet virtual maken, simpel zat!

42.7 percent of all statistics are made up on the spot.


Verwijderd

whoami schreef op 17 augustus 2004 @ 15:09:

Trouwens, volgens mij hebben ze weldegelijk eerst het nagedacht over het platform, de CLR, etc.... en hebben ze dan een nieuwe taal ontworpen (C#) die speciaal op dat platform gericht was, en niet andersom zoals jij stelt.
Ik zou hier echter wat research moeten voor doen, maar de tijd ontbreekt me een beetje.
Wat ik wel denk is dat C# zelf voornamelijk gebasseerd is op de bestaande investeringen die MS gedaan had ik java, zoals het bijna door iedereen vergeten 100% pure java AFC (Application Foundation Framework). Omdat MS geen Java meer mocht gebruiken, moesten ze toch iets doen met die bestaande code.

De oplettende lezer is het mischien wel opgevallen dat alle beta documentatie van het .NET framework vergezeld ging van code examples in Java syntax. Dit was lang voor er uberhaupt een beta versie van de J# compiler uit was.

Kort door de bocht, -denk ik- dat MS de Java syntax iets verbogen heeft naar C#, en daardan weer de CLR omheen gebouwt heeft. Echte bewijzen heb ik niet, alleen maar mijn eigen gevoel, en natuurlijk de meningen van anderen die er ook zo over denken.

Oh, en dan heb ik bij deze meteen de eerste taal te pakken die wel zonder syntax aanpassingen naar IL compileerd: Java. (hoewel er natuurlijk alleen voor hele oude libs support is, en de Java 1.5 syntax ook niet meegenomen zal worden door de MS J# compiler)

Verwijderd

d00d schreef op 17 augustus 2004 @ 15:21:
Ik snap nog steeds niets van alle commotie. Ik denk dat .Net hier op de juiste manier de OO regels toepast. Als je in de constructor een virtuele method aanroept (wat je dus beter niet kunt doen volgens FxCop), dan WEET je toch dat de most-derived method zal worden gebruikt. Als je dat niet wilt dan moet je MyMethod dus niet virtual maken, simpel zat!
Maar MyMethod kan ook door andere methods worden aangeroepen dan alleen de constructor. In C++ WEET je dat die method wordt aangeroepen die in dezelfde class zit als waarin de ctor zit, virtual of niet. Simpel zat!

De regel is bedriegelijk eenvoudig. In de ctor van base is this echt een base. Meer is het niet eigenlijk. Als je in termen van casten blijft denken, kun je het ook opvatten als dat elke toegang naar een member wordt voorafgegaan door this->base:: , bv this->base::MyMethod();

  • curry684
  • Registratie: Juni 2000
  • Laatst online: 12-05 22:23

curry684

left part of the evil twins

Verwijderd schreef op 17 augustus 2004 @ 15:04:
[...]

Maar moet language interop dan zo'n groot ideaal zijn dat het vereist dat talen (neem bv visual basic) compleet overhoopt gehaalt moeten worden?
VB had al 5 jaar eerder compleet overhoop gehaald moeten worden :X Voor de rest valt het best mee eilijk zolang je niet aan uitwassen als COBOL.NET begint :)

Professionele website nodig?


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Verwijderd schreef op 17 augustus 2004 @ 15:54:
[...]

De regel is bedriegelijk eenvoudig. In de ctor van base is this echt een base. Meer is het niet eigenlijk. Als je in termen van casten blijft denken, kun je het ook opvatten als dat elke toegang naar een member wordt voorafgegaan door this->base:: , bv this->base::MyMethod();
Heb je het nu over C++?
Dat is dan erg bedriegelijk, want this->base::MyMethod is een qualified name lookup, en een "kale" MyMethod naam is een unqualified name lookup. Dat heeft grote gevolgen in templates.

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


Verwijderd

MSalters schreef op 17 augustus 2004 @ 17:06:
[...]

Heb je het nu over C++?
Dat is dan erg bedriegelijk, want this->base::MyMethod is een qualified name lookup, en een "kale" MyMethod naam is een unqualified name lookup. Dat heeft grote gevolgen in templates.
Klopt, mischien was de wijze van uitdrukken inderdaad bedriegelijk want natuurlijk gebeurt zoiets (auto qualification oid) niet. Het ging er alleen om, om te communiceren dat in de ctor van base ook de functies in de class base worden aangeroepen, oftewel de vtable is nog niet ingevuld met entries voor derived classes.
Je kunt het dus 'een beetje opvatten als'. Maar 'een beetje opvatten als' is natuurlijk vaag uitgedrukt en neemt niet alle gevallen mee.
Pagina: 1