Toon posts:

VB.NET: typecasting at runtime

Pagina: 1
Acties:

Verwijderd

Topicstarter
Ik ben mij maar weer eens in VB aan het verdiepen om een programma bij een klant uit te breiden.

Ik heb drie klassen

Persoon, Werknemer en klant.

Werknemer en klant zijn natuurlijk afgeleid van de klasse persoon.

Nu wil ik in de klasse persoon een functie delete toevoegen waarmee ik een persoon uit de database kan verwijderen. Mijn probleem is dat deze persoon een werknemer of klant kan zijn en bij beide er nog extra informatie wordt opgeslagen die ook verwijderd moet worden. Beide klassen hebben dus een overloaded delete functie die daarvoor zorgt.

Echter, de bestaande code geeft de persoon als object mee naar verschillende functies waardoor in die functies het niet bekend is of het om een werknemer of klant gaat. In eerste instantie dacht ik dat dit geen probleem mocht zijn doordat je iets als

cType(PersoonObject,PersoonObject.getType)

zou kunnen doen om het object naar het oorspronkelijke object terug te kunnen converteren. Helaas niet dus :( cType vereist dat het type al vast staat tijdens het ontwerp. Dus heeft er iemand een idee om wel dynamisch de juiste klasse te kunnen bepalen en de goede delete functie aan te kunnen roepen?

  • Gerco
  • Registratie: Mei 2000
  • Laatst online: 01:49

Gerco

Professional Newbie

Zou je niet gewoon in de Persoon klasse een method delete() kunnen maken en die dan overriden in de subclasses? De method in de subclass doet dan zoiets (please excuse my syntax, vb.net is niet mijn ding meer):
Visual Basic .NET:
1
2
3
4
Public Overrides Sub Delete ()
  Database.Delete de extra info
  MyBase.Delete()
End Sub


In dat geval hoeft de class die de boel wil deleten helemaal niet te weten of het een Persoon, Werknemer of Eenhoorn is, de class zelf zorgt wel voor het goed deleten van de info.

[ Voor 25% gewijzigd door Gerco op 08-11-2005 13:36 ]

- "Als ik zou willen dat je het begreep, legde ik het wel beter uit!" | All number systems are base 10!


Verwijderd

Topicstarter
Oops, de persoon, werknemer en klant waren een voorbeeld. Inderdaad zou het dan met een override moeten kunnen :o

Helaas heb ik ook nog andere objecten (zeg een huis) die niet van dezelfde basisklasse zijn afgeleid maar wel over een delete functie beschikken.

Nu kan ik deze klassen gaan ombouwen naar versies die wel dezelfde basisklassen hebben met een delete method ( en zeg een insert method, soms een spring omhoog method enz), maar handiger is het om de klasse at runtime te kunnen bepalen en te converteren.

Dus als iemand een idee heeft?

  • whoami
  • Registratie: December 2000
  • Laatst online: 25-04 12:00
Het gaat hier over VB.NET neem ik aan; aangezien dit de enige VB tot nu toe is die inheritance kent.

Ik snap jouw probleem eigenlijk niet zo goed.
Persoon heeft een (virtual) method delete. Deze override je in je inherited classes, en je voegt er extra functionaliteit aan toe.

Je class 'Huis' heeft ook een delete method, maar dan volg ik je ff niet meer .... Wat is het probleem dan ?

Je kan ook een interface maken die het contract definieert waar bepaalde classes moeten aan voldoen. Zeg bv een IDeletable interface die een method 'Delete' bevat.
Alle classes die je dan uit een DB wilt kunnen verwijderen, laat je deze interface implementeren.

Je kunt at runtime bepalen of een object een bepaalde interface implementeert dmv de as operator (of de is operator), dit is wel C#, maar VB.NET zal er wel een equivalent voor hebben.

https://fgheysels.github.io/


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:35

.oisyn

Moderator Devschuur®

Demotivational Speaker

Verwijderd schreef op dinsdag 08 november 2005 @ 13:14:
In eerste instantie dacht ik dat dit geen probleem mocht zijn doordat je iets als

cType(PersoonObject,PersoonObject.getType)

zou kunnen doen om het object naar het oorspronkelijke object terug te kunnen converteren. Helaas niet dus :( cType vereist dat het type al vast staat tijdens het ontwerp.
Je maakt hier een cruciale denkfout, namelijk dat het object dat je hebt ook daadwerkelijk van het type is van de variabele waarin het object staat opgeslagen. Dit is natuurlijk niet zo, als een Persoon een Werknemer is dan is je PersoonObject ook gewoon een Werknemer. Het is niet dat het een Persoon is alleen omdat PersoonObject van dat type is.

Het punt dat je geen Delete functie aan kunt roepen die in Werknemer gedefinieerd is komt omdat de compiler (dus tijdens compiletime) niet zeker weet of die Persoon die je daar hebt ook een Werknemer is. En aangezien er geen Delete functie in Persoon bestaat snapt ie niet wat jij nou wilt. Daarom moet je die functie aanroepen op het type wat die functie wel kent, een werknemer dus. Je moet dus tegen de compiler zeggen dat jij zeker weet dat die PersoonObject een Werknemer is, en dat doe je met CType.

Als jij dus GetType mee gaat geven als 2e parameter voor CType, dan weet de compiler nog niets aangezien dan nog niet bekend is wat het daadwerkelijke type nou is, en dat zorgt er ook niet voor dat ie dan ineens wel snapt waar die Delete vandaan moet komen :)

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.


Verwijderd

Topicstarter
Ik weet dat als ik een werknemer herdefinieer naar een Persoon dat het dan gewoon een werknemer blijft. Probleem is meer dat VB mij niet geloofd en het zelf nog een keer wil controleren. En dat gaat niet goed. VB kan het echter wel. Ik heb namelijk een Quick fix gevonden in de vorm van de volgende code

code:
1
CompilerServices.Conversions.ChangeType(Persoon, persoon.GetType).Delete()


Hoewel het niet al te netjes is om deze functie aan te roepen, lost het wel het probleem op. Refactoren van de code blijft natuurlijk een betere oplossiing maar dan wordt het van snel even aanpassen een lang project. En daar is nou net geen geld voor.

Blijft wel de vraag over waarom cType zo eigenwijs is. :) De compiler zelf kan het namelijk wel, dus het zal wel zelfbescherming van de programmeurs zijn. Aan de ene kant goed, aan de andere kant zijn dit soort zaken zo nu en dan gewoon noodzakelijk.

[ Voor 12% gewijzigd door Verwijderd op 08-11-2005 17:23 ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:35

.oisyn

Moderator Devschuur®

Demotivational Speaker

Je leest/begrijpt niet wat ik zeg, maar goed :)

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®
Ik snap het niet helemaal wat je bedoeld. Ik doe even een voorbeeldje in C# omdat ik de syntax van VB.NET niet ken.
In je voorbeeldje met persoon heb je het dus zo
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Persoon{
    public virtual void Delete(){ /*Implementatie*/ }
}
class Werknemer : Persoon /*Werknemer extends Persoon*/{
    public override void Delete(){ /*Implementatie*/ }
}
class Klant : Persoon /*Werknemer extends Persoon*/{
    public override void Delete(){ /*Implementatie*/ }
}

//Ergens anders in de code
public void DoeIetsMetPersoon( Persoon p )
{
    //andere code hier
    p.Delete();
}

Wat zou de reden hier zijn om te moeten Casten? het haalt niet uit wat voor type persoon je doorgeeft. Er wordt altijd de goede methode aangeroepen.

Nou zeg je dat je objecten doorgeeft die niet extenden van Persoon dus dan heb je ongeveer zoiets
C#:
1
2
3
4
5
public void DeleteIets( Object p )
{
    //andere code hier
    p.Delete();//Dit kan dus niet want object heeft geen delete
}

jij wilt daar op een object ( Je weet immers niet welk type het is. Het zou ook een string of iets dergelijks kunnen zijn ) Delete aanroepen. Maar de compiler zegt dan terecht dat dat niet kan omdat Object geen delete methode heeft.

Jij wilt dan als oplossing gebruiken:
Cast object naar het type waarvan het is en voer dan Delete uit.

Maar dan hou je nog steeds hetzelfde probleem. Want dan geef ik een string mee dan krijg je het volgende

Je hebt Object s ( Wat een string is in dit voorbeeld )
cast p naar string

dus dan heb je String s2
roep Delete aan op s2 ( Hier gaat het dus mis want dat kan niet ).

Je moet compile time gewoon aan kunnen geven wat voor type het is al wil je er een methode op aanroepen. Als je perse wilt dat je alle objecten die je aan die methode meegeeft kunt deleten moet je bijvoorbeeld een interface IDeletable maken en de code als volgt maken. Je hebt dan geen gemeenschappelijke base class maar al je classes voldoen wel aan een interface waarvan je dus compiletime kan garanderen dat eraan voldaan wordt
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
interface IDeletable
{
    void Delete();
}

class Persoon : IDeletable {
    public virtual void Delete(){ /*Implementatie*/ }
}
//Deze implementeerd IDeletable dus ook omdat hij van Persoon erft
class Werknemer : Persoon/*Werknemer extends Persoon*/{
    public override void Delete(){ /*Implementatie*/ }
}
//Deze implementeerd IDeletable dus ook omdat hij van Persoon erft
class Klant : Persoon/*Werknemer extends Persoon*/{
    public override void Delete(){ /*Implementatie*/ }
}

class IetsAnders : IDeletable{
    public void Delete(){ /*Implementatie*/ }
}


//Ergens anders in de code
public void DeleteIets( IDeletable d )
{
    d.Delete();
}


Je probeert namenlijk te casten naar een type waarvan je zeker weet dat het een Delete methode heeft. Ook al hoeft het helemaal geen gemeeschappelijk base class te hebben. Daarvoor heb je dus interfaces. Je hebt dan een Interface om tegen aan te praten en aangezien al je classes waarbij je het wilt gebruiken blijkbaar al aan die interface voldoen hoef je alleen maar bij de declaratie aan te geven dat ze eraan voldoen en bij het stukje code waar je het wilt gebruiken zeggen dat het om een IDeletable ( kan natuurlijk ook een andere naam zijn is alleen maar een voorbeeld ) gaat.

mmm als ik het zo terug lees zeg ik het allemaal een beetje vaag dus ik hoop dat het een beetje duidelijk is.

[ Voor 12% gewijzigd door Woy op 08-11-2005 18:18 ]

“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.”


Verwijderd

Verwijderd schreef op dinsdag 08 november 2005 @ 13:14:
Ik heb drie klassen
Persoon, Werknemer en klant.

Werknemer en klant zijn natuurlijk afgeleid van de klasse persoon.

Echter, de bestaande code geeft de persoon als object mee naar verschillende functies waardoor in die functies het niet bekend is of het om een werknemer of klant gaat. In eerste instantie dacht ik dat dit geen probleem mocht zijn doordat je iets als

cType(PersoonObject,PersoonObject.getType)
volgens mij probeer je dit te doen...
code:
1
2
3
4
5
6
7
8
9
10
dim myWerknemer as Werknemer = nothing
dim myKlant as Klant = nothing

if (typeof PersoonObject is Werknemer) then
  myWerknemer = cType(PersoonObject,Werknemer) 
  myWerknemer.DoeIets1()
else if (typeof PersoonObject is Klant) then
  myKlant = cType(PersoonObject,Klant)
  myKlant.DoeIetsAnders()
end if

Verwijderd

Topicstarter
Klopt min of meer.
Maar maak van die twee classes eens een stuk of 20 en laat het eens zeer regelmatig voorkomen. Dan wordt je niet blij met blokken if typeof code. Het werkt maar zodra er een klasse bij komt mag je overal de code gaan aanpassen.

Doel was om niet van dit soort blokken te genereren maar het door 1 regel code te vervangen die ook nog eens futureproof is. Logisch lijkt:

cType(persoonobject,persoonobject.gettype)

Je weet tenslotte dat een object altijd van het type is wat het object zelf aangeeft wat het is. De type conversie zou dus altijd moeten lukken/ Als je dan ook nog weet dat de functie die je aanroept bestaat zou het moeten werken.
Helaas kan VB hier niet mee overweg. Dat probleem zit niet zozeer in de compiler aangezien die dat wél kan via de hierboven gegeven andere aanroep, maar puur in de manier waarop Microsoft in haar wijsheid de cType functie heeft geschreven.

Dat interfaces hiervoor een oplossing kunnen zijn is mogelijk, maar het is op dit moment niet reeel om de bestaande classes aan te passen. En eerlijk gezegd zie ik dat ook niet op de langere termijn gebeuren. Voorlopig houd ik het dan ook maar op de compilerservices aanroep. Die lost het probleem van de blokken if TypeOf code op en maakt de aanroep universeel. Ook hoeven er geen grote verbouwingen aan de classes plaats te vinden zodat er geen andere problemen bijkomen.
Bij .Net versie 3.0 zien we wel weer verder.

  • pjvandesande
  • Registratie: Maart 2004
  • Laatst online: 16-04 11:36

pjvandesande

GC.Collect(head);

Verwijderd schreef op woensdag 09 november 2005 @ 01:00:
Bij .Net versie 3.0 zien we wel weer verder.
Ik begin nu al te bidden dat dat er nooit komt!

Maar vooruit, zijn het nou dezelfde methods die je aanroept. Hebben ze dezelfde naam?
Wat zijn nu precies de methods die je wilt aanroepen.

Volgens mij kan het namelijk een stuk beter en zie je iets over het hoofd. Een abstracte class of een interface zou hier volgens mij wonderen doen!

  • whoami
  • Registratie: December 2000
  • Laatst online: 25-04 12:00
Ik denk dat jij je gewoon eens moet lezen wat polymorphisme is.

https://fgheysels.github.io/


  • bigbeng
  • Registratie: Augustus 2000
  • Laatst online: 26-11-2021
Verwijderd schreef op woensdag 09 november 2005 @ 01:00:
Doel was om niet van dit soort blokken te genereren maar het door 1 regel code te vervangen die ook nog eens futureproof is. Logisch lijkt:

cType(persoonobject,persoonobject.gettype)
Dit is dus helemaal niet logisch, omdat je namelijk compiletime en runtime door elkaar haalt. De compiler kan niet bepalen of het type van het object altijd de methode delete bevat. Je conversie zou in theorie dus altijd mogelijk zijn, maar je aanroep van de delete zou dan dus worden:
code:
1
cType(persoonobject,persoonobject.gettype) .delete()

En dit is dus precies waar de compiler over gaat stuiteren, omdat hij niet kan bepalen of de .delete() wel altijd bestaat.

Je kunt bij .NET met reflection runtime bepalen of een object een bepaalde methode of property heeft, maar reflection is meestal (en zeker in jouw geval) een gevolg van slecht ontwerp. Bovendien weet ik niet of je reflection kunt gebruiken in VB.NET.

Een aantal mensen hebben het al geroepen, maar ik doe het ook maar even. Interfaces zijn precies hiervoor uitgevonden, een contract waaraan je klasse moet voldoen. Je kunt naar deze interface casten en dan de gewenste methode aanroepen. Maar goed, ik begreep uit jouw eerdere reacties dat het aanpassen van al je klasses (met slechts een implements regeltje bij de class definition) teveel moeite is om je applicatie future proof te maken.

  • whoami
  • Registratie: December 2000
  • Laatst online: 25-04 12:00
Bovendien weet ik niet of je reflection kunt gebruiken in VB.NET.
Waarom zou je dat niet kunnen ?
Reflection is een onderdeel van .NET, dus kan je het ook in vb.net gebruiken.

https://fgheysels.github.io/


  • bigbeng
  • Registratie: Augustus 2000
  • Laatst online: 26-11-2021
whoami schreef op woensdag 09 november 2005 @ 09:31:
[...]

Waarom zou je dat niet kunnen ?
Reflection is een onderdeel van .NET, dus kan je het ook in vb.net gebruiken.
Omdat ik het niet wist ;) en geen zin had om te gaan googlen. Volgens mij is operator overloading ook een onderdeel van .NET, maar kon VB.NET dat stiekum toch niet aan.
Het was dus gewoon effe mezelf indekken :Y)

  • pjvandesande
  • Registratie: Maart 2004
  • Laatst online: 16-04 11:36

pjvandesande

GC.Collect(head);

Reflection zijn gewoon wat classes, operator overloading is een future van de taal.

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Verwijderd schreef op woensdag 09 november 2005 @ 01:00:
Logisch lijkt:

cType(persoonobject,persoonobject.gettype)
Wat voor nut heeft het om te casten naar iets waar je van te voren niet weet wat het is? Casten is eigenlijk alleen een hint voor de compiler om te zeggen wat voor type het is en waar hij zijn methodes kan vinden.

ik snap trouwens ook niet waarom
code:
1
CompilerServices.Conversions.ChangeType(Persoon, persoon.GetType).Delete()

werkt. Want ChangeType heeft als return type gewoon Object en een Object heeft geen Delete Methode.

Ik snap dan ook niet goed wat ChangeType doet want het lijkt me sterk dat ChangeType echt iets aan een object veranderd.

“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.”


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:35

.oisyn

Moderator Devschuur®

Demotivational Speaker

rwb: dat snapte ik idd ook niet, maar vb.net kent late binding wat alleen bij Object van toepassing is. Omdat hij een Persoon heeft wordt er niet ge-late-bind (wat een woord 8)7). Maar omdat die ChangeType functie een Object returnt werkt het dan ineens wel.

Dit werkt overigens alleen als Option Strict op On staat, welke default op Off staat. Ik gok dat als hij Option Strict Off bovenaan z'n code zet dat het dan niet meer werkt op die manier.
9.8 Late-Binding Expressions

When the target of a member access expression, invocation expression, or index expression is of type Object, the processing of the expression may be deferred until run time. Deferring processing this way is called late binding. Late binding allows Object variables to be used in a typeless way, where all resolution of members is based on the actual run-time type of the value in the variable. If strict semantics are specified by the compilation environment or by Option Strict, late binding causes a compile-time error. Note that, unlike the early-bound case, invoking or accessing a Shared member late bound will cause the invocation target to be evaluated at run time.

Note If the expression is an invocation expression for a member defined on System.Object, late binding will not take place.

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®
ow dan had hij dus ook gewoon
Visual Basic .NET:
1
cType( myPersoon, typeof( Object ) ).Delete()

Kunnen doen. Ik snap niet waarom ze dat nog mogenlijk gemaakt hebben in VB.NET. Dan is meteen het nut van een Type-Safe systeem weer weg.

Maar nou snap ik het nut van die ChangeType nog niet. Ik kan dan ook wel een ChangeType implementeren
C#:
1
2
3
public static Object ChangeType ( Object Expression, Type TargetType ){
    return Expression;
}

[ Voor 33% gewijzigd door Woy op 09-11-2005 11:03 ]

“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.”


  • OMX2000
  • Registratie: Januari 2001
  • Laatst online: 18:31

OMX2000

By any means necessary...

rwb schreef op woensdag 09 november 2005 @ 10:59:
ow dan had hij dus ook gewoon
Visual Basic .NET:
1
cType( myPersoon, typeof( Object ) ).Delete()

Kunnen doen. Ik snap niet waarom ze dat nog mogenlijk gemaakt hebben in VB.NET. Dan is meteen het nut van een Type-Safe systeem weer weg.
Kan wel eens handig zijn als je oude COM componenten wilt gebruiken.

Dè developers podcast in je moerstaal : CodeKlets Podcast


  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
OMX2000 schreef op woensdag 09 november 2005 @ 11:03:
[...]
Kan wel eens handig zijn als je oude COM componenten wilt gebruiken.
Hoezo zou dat handig zijn dan? Ja het is mischien soms wat minder type werk het meteen niet meer Type Safe.

“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.”


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:35

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ik denk dat changetype wel daadwerkelijk het runtime type van het object aanpast.

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.


  • bigbeng
  • Registratie: Augustus 2000
  • Laatst online: 26-11-2021
Loop je dan niet het risico dat bij overerving een andere versie van je overridden methodes afgaat?

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:35

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ik denk dat dat juist de bedoeling is ;)

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.

Pagina: 1