[c#] constructoren: 'this' doet niet wat ik wil

Pagina: 1
Acties:

  • JeroenTheStig
  • Registratie: Mei 2000
  • Laatst online: 23:47
Stel ik heb de volgende constructor:

C#:
1
2
3
4
5
6
7
public ControlDecorator(Control control,
                        AbstractAction action)
{
  _control = control;
  _action = action;
  initControl();
}


Vervolgens bouw ik een constructor met een extra parameter:

C#:
1
2
3
4
5
6
7
8
9
10
public ControlDecorator(Control control,
                        AbstractAction action,
                        Size size)
{
  _control.Size = size;
  // control en action naar de andere constructor sturen:
  // natuurlijk kan ik de code uit de eerste constructor hier naartoe
  // copieren, maar bij een hoop code is dit niet echt netjes, het moet 
  // ook met 'this' kunnen
}


Nu wil ik dat bij het aanroepen van de tweede constructor eerst de code van deze constructor wordt uitgevoerd voordat ik de twee parameters 'control' en 'action' naar de andere constructor stuur. Ik heb geprobeerd om de volgende wijziging te maken:

C#:
1
2
3
4
5
6
public ControlDecorator(Control control,
                        AbstractAction action,
                        Size size) : this(control, action)
{
  _control.Size = size;
}


Het probleem is dat wanneer ik 'this(control,action)' achter de constructor plaats eerst de constructor 'ControlDecorator(Control control, AbstractAction action)' uitvoert voordat hij de rest van de constructor 'ControlDecorator(Control control, AbstractAction action, Size size)' uitvoert.

In java kon je heel mooi in de code van de constructor 'this(control,action);' plaatsen, maar in c# gaat dat niet werken.

In m'n boek kan ik ook zo één twee drie niet vinden hoe ik de volgorde kan wijzigen.

Wie kan mij helpen?

edit:
het voorbeeld op zich is niet belangrijk, ik zie namelijk dat er nullpointers op gaan treden, maar ik zou graag willen weten hoe dit opgelost kan worden

[ Voor 13% gewijzigd door JeroenTheStig op 02-07-2004 14:48 ]


Verwijderd

Misschien eens hiernaar kijken.

  • EfBe
  • Registratie: Januari 2000
  • Niet online
Wat je wilt kan niet. Constructors zijn geen overloads. Maak een init routine die je aanroept vanuit beide constructors met verschillende parameters.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


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

.oisyn

Moderator Devschuur®

Demotivational Speaker

Boktor schreef op 02 juli 2004 @ 14:25:
Het probleem is dat wanneer ik 'this(control,action)' achter de constructor plaats eerst de constructor 'ControlDecorator(Control control, AbstractAction action)' uitvoert voordat hij de rest van de constructor 'ControlDecorator(Control control, AbstractAction action, Size size)' uitvoert.
Euh ja, dat is ook precies de bedoeling :)
Nou maakt het in je voorbeeld ook niet zo heel veel uit, maar ik neem aan dat je in het echt wat ingewikkelders probeert te doen ;)
In java kon je heel mooi in de code van de constructor 'this(control,action);' plaatsen, maar in c# gaat dat niet werken.
In java is het toch echt niet anders dan in C#. Het is een syntactisch verschil; in java zet je de this in de body van de functie, in C# doe je dat in de initialiser list. Er is geen enkel verschil, ook in java mag je niet eerst andere statements specificeren voor de this of super call.

Je zult het dan idd met een (private) init functie op moeten lossen

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.


  • whoami
  • Registratie: December 2000
  • Laatst online: 23:52
EfBe schreef op 02 juli 2004 @ 19:52:
Wat je wilt kan niet. Constructors zijn geen overloads. Maak een init routine die je aanroept vanuit beide constructors met verschillende parameters.
In dat documentje dat hierboven gepost werd, staat toch dat het ook mogelijk is om dit te schrijven:

code:
1
2
3
4
5
6
7
8
9
10
public Class1( string s )
{
   // ...
}

public Class1( string s , int i )
{
     _blaat = i;
     Class1(s);
}


Ik ben ook niet bekend met deze techniek, en ik heb het ook nog niet uitgeprobeerd.

https://fgheysels.github.io/


  • gorgi_19
  • Registratie: Mei 2002
  • Laatst online: 20:27

gorgi_19

Kruimeltjes zijn weer op :9

Volgens mij gaat dat niet, althans, niet in VB.Net. Ik gok dat C# het dus ook niet gaat pikken.
Visual Basic .NET:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Public Class TempClass

    Public Sub New()

        Dim a As Integer = 1
        Me.New("blaat")

    End Sub

    Public Sub New(ByVal melp As String)

    End Sub

End Class

Bovenstaand levert een error op bij Me.New("blaat")

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Public Class TempClass

    Public Sub New()

        Me.New("blaat")
        Dim a As Integer = 1

    End Sub

    Public Sub New(ByVal melp As String)

    End Sub

End Class

Dit levert geen error op. :)

Digitaal onderwijsmateriaal, leermateriaal voor hbo


  • misfire
  • Registratie: Maart 2001
  • Laatst online: 12-10-2024
Dominique postte al een linkje, maar hier nog het voorbeeld om het duidelijker te krijgen:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class mySampleClass
    {
        public mySampleClass(): this(10)
        {
            // This is the no parameter constructor method.
            // First Constructor
        }

        public mySampleClass(int Age)
        {
            // This is the constructor with one parameter.
            // Second Constructor
        }
    }

In C# is de enige manier om andere constructors aan te roepen net na de constructor declaratie, gescheiden door een dubbele punt ( : ). Dit verschilt niet zoveel van Java: daar moet het op de eerste regel. De base constructor kan ook op deze manier aangesproken worden (met het base keyword).

Ik vind het vreemd dat allerlei mensen hier roepen dat het niet kan. Als je het niet weet, waarom dan reageren op zo'n post? :?

  • Infinitive
  • Registratie: Maart 2001
  • Laatst online: 25-09-2023
misfire schreef op 02 juli 2004 @ 23:03:
Ik vind het vreemd dat allerlei mensen hier roepen dat het niet kan. Als je het niet weet, waarom dan reageren op zo'n post? :?
Het ging er hier om dat de TS de volgorde precies andersom wilde hebben. Dus niet eerst de constructoren in het lijstje achter de dubbele punt, maar de body van de constructor zelf. Dát kan je niet op die manier representeren en daarvoor zul je gebruik moeten maken van een aparte (private) methode, zodat je wel kan bepalen wanneer die code wordt uitgevoerd.

[ Voor 6% gewijzigd door Infinitive op 02-07-2004 23:23 ]

putStr $ map (x -> chr $ round $ 21/2 * x^3 - 92 * x^2 + 503/2 * x - 105) [1..4]


  • whoami
  • Registratie: December 2000
  • Laatst online: 23:52
misfire schreef op 02 juli 2004 @ 23:03:

Ik vind het vreemd dat allerlei mensen hier roepen dat het niet kan. Als je het niet weet, waarom dan reageren op zo'n post? :?
:?
Ik denk dat iedereen hier wel weet hoe je een andere constructor kunt aanroepen.
Het ging 'm om de volgorde waarin die constructors aangeroepen werden.
Eerst goed lezen dus. ;)

https://fgheysels.github.io/


  • JeroenTheStig
  • Registratie: Mei 2000
  • Laatst online: 23:47
thanx voor de reacties. Sommige mensen vroegen zich af wat nou het probleem is. Nou, stel ik heb dit:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Foo {
  private object _x;
  private object _y;
    public Foo(object x) {
      _x = x;
      init();
    }

    public Foo(object x, object y) : this(x) {
      _y = y;
    }

    private void init() {
      if (_y == null) {
        // doe iets
      } else {
        // doe iets anders
      }
   }
}
 


dan is het de bedoeling dat hij bij de tweede constructor eerst y toekent aan _y, voor hij met de init verder gaat. Dat is eigenlijk mijn idee een beetje. Met Java was dit m.i. wel mogelijk, maar ik kan me vergissen

  • EfBe
  • Registratie: Januari 2000
  • Niet online
Constructor: het opzetten van local variables en initial state.
Meerdere constructors: het opzetten van de initial state te sturen middels meer of minder opgegeven waarden (en de niet opgegeven waarden dus een default waarde krijgen).

Het is niet recommended om constructors te chainen (en IMHO zelfs niet mogelijk in veel omstandigheden zoals je hebt ondervonden). Jij wilt code van constructor N hergebruiken in constructor N+1. Dat doe je door een private method aan te roepen die de code die wordt hergebruikt bevat.

Ga je nl. constructors linken in een call chain dan krijg je de grootst mogelijke ellende wanneer je een constructor die minder variabelen accepteert wijzigt. Niet alleen wijzig je de initial state setup code voor calls naar die constructor, je wijzigt OOK de constructor met MEERDERE variabelen, iets wat je wellicht niet wilt!

Ergo: chainen van constructors levert spagetti en we weten allemaal waar spagetti goed voor is: juist, om op te eten, niet om code mee te maken.

Aan de persoon die in deze thread riep te piepen dat mensen het niet wisten en hij wel: ik denk dat je je vergist, en dan druk ik me nog wat zacht uit.

ZO los je dit op:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Foo {
    private object _x;
    private object _y;
    
    public Foo(object x) {
        init(x, null);
    }

    public Foo(object x, object y) {
        init(x, y);
    }

    private void init(object x, object y) {
        _x = x;
        _y = y;

        if (_y == null) {
            // doe iets
        } else {
            // doe iets anders
        }
    }
}


en niet met linked constructors in een plakkerig web van spagetti

[ Voor 3% gewijzigd door EfBe op 03-07-2004 13:03 ]

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • alienfruit
  • Registratie: Maart 2003
  • Laatst online: 22-05 23:32

alienfruit

the alien you never expected

Maar is het niet een kwestie van als MSIL het ondersteunt, kan je het ook maken in C#? Natuurlijk als de MSIL het niet ondersteunt dan houd het op natuurlijk, bij mijn weten ondersteunt de MSIL het wel, heb er me er nog niet veel in verdiept heb pas sinds woensdag me ".NET Language and Compiler" boek van A!Press.
Gaan ze een QuickBasic ompiler maken in VB.NET }) Moet ik nog (Visual) Basic opnieuw gaan leren ook, had liever dat ze het in C# maakte. Om vervolgens te wachten tot de "Compiler compiles itself" milestone ;) :+

[ Voor 9% gewijzigd door alienfruit op 03-07-2004 13:14 ]


  • EfBe
  • Registratie: Januari 2000
  • Niet online
alienfruit schreef op 03 juli 2004 @ 13:13:
Maar is het niet een kwestie van als MSIL het ondersteunt, kan je het ook maken in C#? Natuurlijk als de MSIL het niet ondersteunt dan houd het op natuurlijk, bij mijn weten ondersteunt de MSIL het wel, heb er me er nog niet veel in verdiept heb pas sinds woensdag me ".NET Language and Compiler" boek van A!Press.
Gaan ze een QuickBasic ompiler maken in VB.NET }) Moet ik nog (Visual) Basic opnieuw gaan leren ook, had liever dat ze het in C# maakte. Om vervolgens te wachten tot de "Compiler compiles itself" milestone ;) :+
IL snapt niets van private, public, properties etc. , dus die moet je dan dus ook maar niet gebruiken? :). IL is het resultaat van de compilatie, dus daar heb je eigenlijk geen reet mee te maken, je hebt te maken met de hogere abstractie van IL: de .NET taal C# of een andere .NET taal.

Ieder is natuurlijk vrij om zoveel mogelijk chaining in te bouwen in zn code mbt constructors, wees wel beducht voor de consequenties, m.a.w.: het is beter om gewoon straight forward te programmeren en zo simpel mogelijk dan via allerlei wazige constructies 'omdat het mogelijk is'.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • alienfruit
  • Registratie: Maart 2003
  • Laatst online: 22-05 23:32

alienfruit

the alien you never expected

Ja, maar je kan toch die Assembly taal in C# gebruiken :? Naja, de constructors e.d. worden natuurlijk wel omgezet na geldig ILASM code, bijv constructor -> ctor e.d. Als ik het goed begrepen heb. Om misschien is de ILASM nog later dan wat ik bedoel. Naja :)

  • misfire
  • Registratie: Maart 2001
  • Laatst online: 12-10-2024
Blijkbaar heb ik inderdaad gemist dat het ging om de volgorde van de aanroep, niet zozeer hoe het moest. Excuses als ik mensen heb beledigd. Helaas ben ik het nog steeds niet eens met de uitleg van EfBe.

De volgorde van constructors wordt in de taal afgedwongen omdat er anders heel makkelijk fouten zouden worden gemaakt in het opstellen van constructors, Een constructor moet in principe uitgaan van een totaal nieuwe situatie voor het object, tenzij de constructor zelf expliciet een afhankelijkheid uitdrukt naar een andere constructor die eerst uitgevoerd wordt. Als dit niet afgedwongen zou worden dan zou de uitgangspositie van je constructor kunnen veranderen afhankelijk of je deze in andere constructors aanroept. Dat is extreem foutgevoelig. Bovendien is het zeer waarschijnlijk dat code in de eigen constructor zou botsen met de andere constructor.

Het is makkelijk om jouw voorbeeld om te schrijven naar iets dat wel klopt:

code:
1
2
3
4
5
6
7
8
9
10
11
public Foo(object x) : this(x, null) {}

    public Foo(object x, object y) : this(x) {
       _x = x;         
      _y = y;
      if (_y == null) {
        // doe iets
      } else {
        // doe iets anders
      }
    }

Je verschillende varianten van dezelfde constructor op deze manier kunnen schrijven. Als het niet lukt betekent het dat je 2 verschillende constructors wilt maken. Je kunt dan uiteraard niet met chaining werken. Merk op dat je ipv het gebruik van meerdere types constructors al snel beter Factory Methods kunt maken die de types maken die je wilt. Je kunt deze methodes o.a. namelijk een duidelijkere naam geven die het verschil beter beschrijft.

Het gebruik van constructor chaining kan geen spaghetti code veroorzaken, omdat je de situatie van afhankelijkeid die EfBe beschrijft precies omgedraaid is: de constructor met de meeste parameters zal in de meeste gevallen de daadwerkelijke implementatie bevatten. Het is zelfs omgekeerd, het gebruik van een init methode leidt tot meer spaghetti toestanden:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public Foo(object x)
{
  init(x, 1, "bla");
} 

public Foo(object x, int i)
{
  init(x, i, "bla");
}

public Foo(object x, int i, string bla)
{
  init(x, i, bla);
}

Je ziet al vrij snel dat het 2 keer nodig is om de standaard waarde "bla" aan te geven. Bij constructor chaining is dit echter niet nodig:
code:
1
2
3
4
5
6
7
8
public Foo(object x) : this (x, 1) {}

public Foo(object x, int i) : this(x, i, "bla") {}

public Foo(object x, int i, string bla)
{
  init(x, i, bla);
}

Dit is dus minder spaghetti, en imho is dit ook een sterker gebruik van de taal: je geeft volgens de semantiek van de taal aan dat deze constructors bij elkaar horen.

PS: En in MSIL code is private/public natuurlijk wel degelijk opgenomen. Ook de volgorde van aanroepen van constructors zal door de runtime waarschijnlijk worden gecheckt. Dingen als properties zijn idd syntactische suiker, maar dit soort constaints niet, omdat ze het verloop van executie kunne beïnvloeden.

[ Voor 7% gewijzigd door misfire op 03-07-2004 19:48 ]


  • EfBe
  • Registratie: Januari 2000
  • Niet online
misfire schreef op 03 juli 2004 @ 19:41:
De volgorde van constructors wordt in de taal afgedwongen omdat er anders heel makkelijk fouten zouden worden gemaakt in het opstellen van constructors, Een constructor moet in principe uitgaan van een totaal nieuwe situatie voor het object, tenzij de constructor zelf expliciet een afhankelijkheid uitdrukt naar een andere constructor die eerst uitgevoerd wordt.
... iets dat dus in tegenspraak is met de eis dat de constructor van een totaal nieuwe situatie voor het object moet uitgaan. OF je eist dat laatste OF je eist het niet, maar je kunt niet dat laatste eisen en uitzonderingen toestaan, want dan is je eis onzin.
Als dit niet afgedwongen zou worden dan zou de uitgangspositie van je constructor kunnen veranderen afhankelijk of je deze in andere constructors aanroept. Dat is extreem foutgevoelig. Bovendien is het zeer waarschijnlijk dat code in de eigen constructor zou botsen met de andere constructor.
Precies, daarom is het ook onnozel om constructors te chainen in je code.
Het is makkelijk om jouw voorbeeld om te schrijven naar iets dat wel klopt:
code:
1
2
3
4
5
6
7
8
9
10
11
public Foo(object x) : this(x, null) {}

    public Foo(object x, object y) : this(x) {
       _x = x;         
      _y = y;
      if (_y == null) {
        // doe iets
      } else {
        // doe iets anders
      }
    }
'wel klopt' ? M.a.w. wat ik zei klopt niet? Volgens mij klopte wat ik zei/voorstelde wel degelijk.

Jouw oplossing echter niet want die loopt rond. (de een roept de ander aan). En dat klopt 'wel' ?

Welterusten :)

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • misfire
  • Registratie: Maart 2001
  • Laatst online: 12-10-2024
EfBe schreef op 03 juli 2004 @ 23:18:
... iets dat dus in tegenspraak is met de eis dat de constructor van een totaal nieuwe situatie voor het object moet uitgaan. OF je eist dat laatste OF je eist het niet, maar je kunt niet dat laatste eisen en uitzonderingen toestaan, want dan is je eis onzin.
Volgens mij schreef ik wel degelijk dat daarom de constructor het expliciet moet vermelden. Dan weet je zelf namelijk tijdens het schrijven van de constructor al, omdat je het er zelf bij typt. Omdat een constructor altijd het object in een geldige invariant moet afronden is dit echter veel veiliger. Je kunt in de aanroepende constructor namelijk gewoon uit gaan van één van de geldige states van een object, net als in een gewone methode. Dit is dus een heel andere situatie als dat je een constructor aanroept die niet expliciet heeft vermeld dat er eerst iets anders moet gebeuren, terwijl je dit stiekem wel gedaan zou kunnen hebben.
Precies, daarom is het ook onnozel om constructors te chainen in je code.
Nee, daarom mag je een constructor alleen op de eerste regel van een andere constructor chainen. Dan kom je namelijk niet in die situatie.
[...]
'wel klopt' ? M.a.w. wat ik zei klopt niet? Volgens mij klopte wat ik zei/voorstelde wel degelijk.

Jouw oplossing echter niet want die loopt rond. (de een roept de ander aan). En dat klopt 'wel' ?
Ik had het niet over jouw voorbeeld, maar over dat van Boktor. Er zat idd een fout in het voorbeeld, ik had het gecopy-paste en vergeten de this(x) weg te halen, die hoorde daar niet thuis. Jouw voorbeeld klopt natuurlijk ook, alleen worden default waardes daar wel gedubbeld. Je kunt natuurlijk overloads maken van de init methode om dit te voorkomen, maar dan ben je weer terug bij af.

Ik heb er echter nog een heel verhaal bijgetypt, inclusief excuses, een uitleg dat constructor chaining juist spaghetti vermindert en betekenis toevoegt, en een design probleem dat je kunt signaleren als je constructor chaining op een correcte manier toepast. Ik neem aan dat je het daar allemaal mee eens bent, want hier reageer je niet op.

Je bent zowieso de eerste MVP die ik tegen kom die zo tegen constructor chaining is. Merk op dat constructor chaining altijd impliciet of expliciet voorkomt in het geval van inheritance, en het alleen om die reden al handig is om te weten hoe het werkt en waarom dit zo is.

Sommige dingen kun je ook alleen vanuit een constructor doen, bijvoorbeeld:
code:
1
2
3
4
5
6
7
8
9
 public class Bla
{
      private readonly int bla;

      public Bla()
      {
        bla = 10;
      }
}

Dit zou je niet met een init() methode kunnen doen, waardoor de situaties waarin readonly kunt gebruiken ook verminderd worden, en dus opnieuw je een stukje semantiek van de taal niet gebruikt.

  • EfBe
  • Registratie: Januari 2000
  • Niet online
misfire schreef op 04 juli 2004 @ 00:54:
Volgens mij schreef ik wel degelijk dat daarom de constructor het expliciet moet vermelden. Dan weet je zelf namelijk tijdens het schrijven van de constructor al, omdat je het er zelf bij typt. Omdat een constructor altijd het object in een geldige invariant moet afronden is dit echter veel veiliger. Je kunt in de aanroepende constructor namelijk gewoon uit gaan van één van de geldige states van een object, net als in een gewone methode. Dit is dus een heel andere situatie als dat je een constructor aanroept die niet expliciet heeft vermeld dat er eerst iets anders moet gebeuren, terwijl je dit stiekem wel gedaan zou kunnen hebben.
Maar als een constructor iets expliciet moet vermelden, dan roep je al ellende over je af, IMHO. En de reden waarom men van de ene constructor naar de andere wil is code sharing, en dat bereik je ook middels een init routine die wel centraal gebruikt wordt.
Ik had het niet over jouw voorbeeld, maar over dat van Boktor. Er zat idd een fout in het voorbeeld, ik had het gecopy-paste en vergeten de this(x) weg te halen, die hoorde daar niet thuis. Jouw voorbeeld klopt natuurlijk ook, alleen worden default waardes daar wel gedubbeld. Je kunt natuurlijk overloads maken van de init methode om dit te voorkomen, maar dan ben je weer terug bij af.
Waar worden default waarden gedubbeld? De constructor met minder parameters geeft een default waarde mee aan Init. Als je wilt voorkomen dat je defaults wijzigen en dan dus een heel scala aan constructors door moet, dan gebruik je een constant definitie en heb je het opgelost.
Je bent zowieso de eerste MVP die ik tegen kom die zo tegen constructor chaining is. Merk op dat constructor chaining altijd impliciet of expliciet voorkomt in het geval van inheritance, en het alleen om die reden al handig is om te weten hoe het werkt en waarom dit zo is.
Ik ben tegen alle vormen van moeilijk doen. En van de ene constructor naar de andere is IMHO moeilijk doen wat fouten op kan leveren. Met inheritance heb je een andere situatie omdat je daar de base class' constructor aanroept voor het initialiseren van dat deel van de class waar de derived class geen zeggenschap over heeft, en dus een legitiem deel van de constructor is. In dezelfde class van de ene constructor naar de andere is IMHO een andere situatie. Het lijkt op het gebruik van een serie overloads en 1 virtual method die door alle overloads wordt aangeroepen. Echter ga je daar altijd van minder parameters naar meer parameters, en niet andersom, wat hier het geval was. Verder zijn overloads van een bepaalde method semantisch hetzelfde. Bij constructors is dat niet het geval. Een constructor is bv voor deserialization, de ander voor een cleane class en wordt bv door framework classes gebruikt en weer een ander initialiseert de class met een handjevol parameters.
Sommige dingen kun je ook alleen vanuit een constructor doen, bijvoorbeeld:
code:
1
2
3
4
5
6
7
8
9
 public class Bla
{
      private readonly int bla;

      public Bla()
      {
        bla = 10;
      }
}

Dit zou je niet met een init() methode kunnen doen, waardoor de situaties waarin readonly kunt gebruiken ook verminderd worden, en dus opnieuw je een stukje semantiek van de taal niet gebruikt.
Dat is waar, vergeet echter niet dat het gebruik van meerdere constructors een gebaar is aan de gebruiker van de class om dingen makkelijker te maken. En dat je een a twee regeltjes moet dupliceren in 2 constructors ivm readonly variabelen... die je hopelijk vult met een private constant... ik zie het probleem niet zo.

[ Voor 5% gewijzigd door EfBe op 04-07-2004 10:09 ]

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • misfire
  • Registratie: Maart 2001
  • Laatst online: 12-10-2024
EfBe schreef op 04 juli 2004 @ 10:07:
Maar als een constructor iets expliciet moet vermelden, dan roep je al ellende over je af, IMHO. En de reden waarom men van de ene constructor naar de andere wil is code sharing, en dat bereik je ook middels een init routine die wel centraal gebruikt wordt.
Ik weet niet over welke ellende je het hebt, maar met een init methode heb je precies dezelfde situatie (je lust door door naar 1 algemeen stuk code). Daar heb je dus exact dezelfde ellende, behalve dat je dan niet meer in het constructor gedeelte wordt beschermd op de manier die ik hierboven uitlegde.
Waar worden default waarden gedubbeld? De constructor met minder parameters geeft een default waarde mee aan Init. Als je wilt voorkomen dat je defaults wijzigen en dan dus een heel scala aan constructors door moet, dan gebruik je een constant definitie en heb je het opgelost.
Dan moet je weer een constante maken, en overal een expliciete verwijzing doen. Constructor chaining is imho dan een stuk eleganter. Doe je dit soort dingen ook met methode overloads oid? Met chaining geef je de onderlinge relatie sterker aan, en je hoeft defaults maar 1 keer op te geven.
Ik ben tegen alle vormen van moeilijk doen. En van de ene constructor naar de andere is IMHO moeilijk doen wat fouten op kan leveren. Met inheritance heb je een andere situatie omdat je daar de base class' constructor aanroept voor het initialiseren van dat deel van de class waar de derived class geen zeggenschap over heeft, en dus een legitiem deel van de constructor is.
De dwang om expliciet in de constructor chaining aan te geven heeft dezelfde reden. De base class wordt altijd éérst geïnitialiseerd.
In dezelfde class van de ene constructor naar de andere is IMHO een andere situatie. Het lijkt op het gebruik van een serie overloads en 1 virtual method die door alle overloads wordt aangeroepen. Echter ga je daar altijd van minder parameters naar meer parameters, en niet andersom, wat hier het geval was.
Het grappige was dus dat door het gebruik van chaining Boktor er achter kwam dat zijn constructor mijn meerdere parameters leidend was, en de constructor met minder parameters volgend. Het is nooit zo dat je meerdere constructors hebt die hetzelfde doen niet kunt chainen. Dit kan altijd, en als dit niet kan dan wil je dus twee verschillende constructors maken. Voor overloaded methodes geldt precies hetzelfde verhaal, het is daarom ook zeer verstandig om daar op dezelfde manier te chainen.
Verder zijn overloads van een bepaalde method semantisch hetzelfde. Bij constructors is dat niet het geval. Een constructor is bv voor deserialization, de ander voor een cleane class en wordt bv door framework classes gebruikt en weer een ander initialiseert de class met een handjevol parameters.
Method overloads hoeven net zo min semantisch hetzelfde te zijn als constructors. Het is echter wel good practice voor zowel method als constructor overloads om hetzelfde te doen. Constructors voor serialisatie zijn lang niet altijd nodig, constructors voor je eigen framework kun je internal houden. Als je toch verschillende soorten constructors nodig hebt, kijk dan eens naar het Factory Methods patroon.

Ik ben verder wel een beetje uitgepraat over dit onderwerp, we worden het denk ik toch niet eens ben ik bang, maar het is altijd leuk om een beetje over dit soort dingen te discussiëren. :)

Ik vind het wel interessant waarom je zo tegen chaining bent, je bent echt de eerste die ik er over hoor. Eén van de voordelen van .NET en Java vind ik juist dat ieder taal element een duidelijk toepasbare situatie heeft, en daarmee duidelijkheid schept, fouten voorkomt en het de programmeur makkelijk maakt. Zijn er nog andere taal elementen van C# die jij onder de categorie moeilijk doen plaatst?

Verwijderd

Ik ben alleen bekend met Java en C++, maar ik denk dat ik het definitieve antwoord + verklaring kan geven voor het probleem:

Het is pas mogelijk een veld van een object te bewerken zodra het object *bestaat*, en een object bestaat pas zodra de constructor helemaal klaar is. Daarom moet in Java ook een aanroep van een andere constructor vanuit de ene constuctor altijd als eerste gebeuren, en waarschijnlijk ook in C#. Dit kan dus nooit:

(java)

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
class A {
   int a, b;
       
       public A () { // constructor
          b = 3;       // constructie is nog niet klaar, en b bestaat dus nog niet
          this (5);   // maak constructie af
       }

       public A(int i) {
           (...)
       }

}

  • EfBe
  • Registratie: Januari 2000
  • Niet online
misfire schreef op 04 juli 2004 @ 20:56:
[...]
Ik weet niet over welke ellende je het hebt, maar met een init methode heb je precies dezelfde situatie (je lust door door naar 1 algemeen stuk code). Daar heb je dus exact dezelfde ellende, behalve dat je dan niet meer in het constructor gedeelte wordt beschermd op de manier die ik hierboven uitlegde.
Nee, dat bedoel ik niet. Ik bedoel: 2 constructors, a en b, en a is degene met minder parameters. Dan ga je vanuit b eerst a aanroepen en daarna nog wat doen. Dat houdt in dat wanneer a wijzigt, je ook indirect b wijzigt, en dit is niet logisch, omdat constructors op zichzelf staan (je kunt constructors hebben die andere dingen doen met je object, zoals de deserialization constructor bv). Bij een centrale init method heb je dit dus niet.
[...]
Dan moet je weer een constante maken, en overal een expliciete verwijzing doen. Constructor chaining is imho dan een stuk eleganter. Doe je dit soort dingen ook met methode overloads oid? Met chaining geef je de onderlinge relatie sterker aan, en je hoeft defaults maar 1 keer op te geven.
Constructors zijn geen overloads zoals ik al heb gezegd. Je kunt zeer verschillende constructors hebben in 1 class. Het is echter semantisch onjuist om method overloads iets anders te laten doen.
[...]
De dwang om expliciet in de constructor chaining aan te geven heeft dezelfde reden. De base class wordt altijd éérst geïnitialiseerd.
Weet je dat zeker? Ik weet dat niet zeker, en dat interesseert me eigenlijk ook helemaal niet, want het is niet van belang voor mn huidige constructor. C# genereert zelf de call naar de base class constructor in de code, in VB.NET moet je die zelf aanroepen. Ik heb dus geen idee wanneer die call plaatsvindt, het kan net zo goed zijn dat die call na afloop komt. Is dat dan belangrijk? Het zou niet belangrijk moeten zijn.
Het grappige was dus dat door het gebruik van chaining Boktor er achter kwam dat zijn constructor mijn meerdere parameters leidend was, en de constructor met minder parameters volgend. Het is nooit zo dat je meerdere constructors hebt die hetzelfde doen niet kunt chainen. Dit kan altijd, en als dit niet kan dan wil je dus twee verschillende constructors maken. Voor overloaded methodes geldt precies hetzelfde verhaal, het is daarom ook zeer verstandig om daar op dezelfde manier te chainen.
Als je methods maakt die dezelfde naam dragen maar iets verschillends doen dan ben je baggercode aan het schrijven en zijn we gauw klaar. Constructors hebben dezelfde naam dus impliceren EN geen bagger code EN wel overloading, wat niet het geval is. M.a.w.: er is een uitzondering van toepassing en je zult dus op moeten passen en IMHO t.a.t. moeten vermijden dat je afhankelijkheden in je constructor inbouwt mbt andere constructors in je class of base class.
[...]
Method overloads hoeven net zo min semantisch hetzelfde te zijn als constructors. Het is echter wel good practice voor zowel method als constructor overloads om hetzelfde te doen. Constructors voor serialisatie zijn lang niet altijd nodig, constructors voor je eigen framework kun je internal houden. Als je toch verschillende soorten constructors nodig hebt, kijk dan eens naar het Factory Methods patroon.
Ik ken het factory method pattern niet, (alleen (abstract) factory pattern) maar dat terzijde. Constructors impliceren dat er methodoverloading van toepassing is en dit is niet het geval. Method overloading met semantisch verschillende methods is OOK prutswerk, doe het dan ook niet met constructors.
Ik vind het wel interessant waarom je zo tegen chaining bent, je bent echt de eerste die ik er over hoor. Eén van de voordelen van .NET en Java vind ik juist dat ieder taal element een duidelijk toepasbare situatie heeft, en daarmee duidelijkheid schept, fouten voorkomt en het de programmeur makkelijk maakt. Zijn er nog andere taal elementen van C# die jij onder de categorie moeilijk doen plaatst?
Ik vind dit niet een taal element van C#, meer een foutief gebruik van de beschikbare taalelementen. De programmeur doet dus moeilijk, niet de taal. Als men per se wil, dan moet men maar moeilijk gaan doen, anderen hebben daar dan weer voordeel van zeg ik altijd maar weer (behalve de opdrachtgever van de moeilijkdoener :)).

In C# zitten wel een paar constructies waarvan ik denk: dat is zeuren. Met stip op 1 is het 'break;' statement in switch/case constructies. Het is totaal overbodig:
code:
1
2
3
4
5
6
7
8
9
10
11
12
switch(foo)
{
    case "bar":
        // do something;
        break;
    case "bla":
        // do something;
        break;
    default:
        // etc.
        break;
}

Al deze breaks kunnen weg, want er is nl. geen fall through. M.a.w.: de break MOET worden geplaatst maar heeft totaal geen functionele waarde want executie was toch al opgehouden bij de plaats waar 'break' staat. De reden dat dit er in zit? "Because C++ programmers now feel more at home" (c) Eric Gunnerson.

Mja :)

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com

Pagina: 1