[C#] Generics / casting

Pagina: 1
Acties:

  • zoepercavia
  • Registratie: September 2001
  • Laatst online: 02-11 22:01
Aan de slag met C# 2.0 (ziet er veel belovend uit!), maar ik kom een probleem tegen met Generics.

Ik heb een generieke collectie:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public DomainObjectCollection<T> : List<T> where T : DomainObject {
...
}

//een domein object
public Person : DomainObject {
    public DomainObjectCollection<Receipt> Receipts {...}
}

//en en klasse met een methode die er mee werkt:
public PersonMapper {

//methode
public void CreateRelation(DomainObject obj, DomainObjectCollection<DomainObject>) {...}

public void Create(Person obj)
{
    CreateRelation(obj, obj.Receipts);
}

}


Dit resulteert in de foutmelding:
Cannot convert from DomainObjectCollection<Receipt> to DomainObjectCollection<DomainObject>
Maar dat is toch vaag :?

Immers erft Receipt over van DomainObject dus lijkt mij het logisch dat je DomainObjectCollection<Receipt> impliciet kan casten naar DomainObjectCollection<DomainObject>. Zeker omdat er ook nog 'where T : DomainObject' staat in die collectie.

Expliciet casten vind ik niet zo'n oplossing omdat dan de elegantie verloren gaat, of is dat in mijn geval wel nodig?

Panacea.NL als je geinteresserd bent in IT en Geneeskunde!


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 28-11 08:35

curry684

left part of the evil twins

Disclaimer: ik ken .NET 2.0 nog niet uit de praktijk maar wordt nu al botergeil als ik deze code zien ;)
zoepercavia schreef op maandag 20 juni 2005 @ 23:22:
Immers erft Receipt over van DomainObject dus lijkt mij het logisch dat je DomainObjectCollection<Receipt> impliciet kan casten naar DomainObjectCollection<DomainObject>. Zeker omdat er ook nog 'where T : DomainObject' staat in die collectie.
Lijkt me helemaal niet logisch eigenlijk? Dat zou namelijk betekenen dat je vervolgens typesafe een class TweedeHandsAuto ook in die collection zou kunnen frotten als die van DomainObject inherit, terwijl je nu net bij instantiatie hebt afgedwongen dat alle elements van Receipt moeten afleiden :)

De generic-parameter class mag je nooit wegcasten, dat zou echt hopeloos niet logisch zijn :)

[ Voor 4% gewijzigd door curry684 op 20-06-2005 23:58 ]

Professionele website nodig?


  • zoepercavia
  • Registratie: September 2001
  • Laatst online: 02-11 22:01
Yupz, Generics is fijn, net zoals partial classes :)
Van ASP.Net 2.0 wordt je nooooooooog veel blijer trouwens!

Helaas kan ik je redenering niet helemaal volgen. Voor de duidelijkheid: het 2e argument van CreateRelation kan juist elke collectie van domein objecten zijn, omdat daar de relatie gelegd wordt.

De pre-Generics code zou er als volgt uit zien:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public ReceiptCollection : DomainObjectCollection
...
}

//een domein object
public Person : DomainObject {
    public ReceiptCollection Receipts {...}
}

//en en klasse met een methode die er mee werkt:
public PersonMapper {

//methode
public void CreateRelation(DomainObject obj, DomainObjectCollection){...}

public void Create(Person obj)
{
    CreateRelation(obj, obj.Receipts);
}

}


Wat uiteraard werkt want "obj.Receipts is DomainObjectCollection". Je zou toch verwachten dat dit ook met Generics kan (of zie ik nu iets niet). Want ik me kan voorstellen is dat ik dan een DomainObjectCollection : List<DomainObject> aanmaak. Maar dat is toch vaag, het moet vast anders kunnen?

Ik wil dus werken met een generieke collectie van domein objecten (DomainObjectCollection<DomainObject>) in de methode CreateRelation... waarschijnlijk moet dit allemaal nog even precies tot me doordringen

[ Voor 10% gewijzigd door zoepercavia op 21-06-2005 00:18 ]

Panacea.NL als je geinteresserd bent in IT en Geneeskunde!


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

Alarmnummer

-= Tja =-

Het heeft te maken met variantie van geparametriseerde collecties en reads en writes daarop. Hoe het precies zat weet ik niet precies meer..

In java kan je trouwens werken met:
Collection<? extends DomainObject>

en nu mag je een
List<DomainObject>
Collection<DomainObject>
Collection<Persoon>
en List<Persoon>

gebruiken. Hoe dit in c# opgelost is weet ik niet precies, maar het zal ongetwijfeld beter zijn dan in Java.

[edit]
Ik denk dat mbravenboer dit wel goed kan uitleggen.

[ Voor 18% gewijzigd door Alarmnummer op 21-06-2005 01:01 ]


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 28-11 08:35

curry684

left part of the evil twins

zoepercavia schreef op dinsdag 21 juni 2005 @ 00:16:
Wat uiteraard werkt want "obj.Receipts is DomainObjectCollection". Je zou toch verwachten dat dit ook met Generics kan (of zie ik nu iets niet). Want ik me kan voorstellen is dat ik dan een DomainObjectCollection : List<DomainObject> aanmaak. Maar dat is toch vaag, het moet vast anders kunnen?
Nee je mist gewoon even waar de 'is a' relatie zit :)

Stel dat ik class C en B heb die beiden inheriten van A. Stel nu de volgende code:
C#:
1
2
3
4
5
6
7
8
Collection<C> mycol = new Collection<C>();
mycol.Add(new C());

// Hier zou je de error op krijgen die je nu krijgt als je niet expliciet zou casten
Collection<A> mycol2 = (Collection<A>)mycol;

// Deze regel die nu mag is echt zo illegaal als wat:
mycol2.Add(new B()s);

Door de conversie zonder expliciete cast zou je toestaan dat een collectie van C's ineens elementen B mag bevatten ondanks dat er geen relatie tussen de 2 is afgezien van een gezamenlijke baseclass. Als het object origineel als Collection<A> geinstantieerd was zouden beiden er wel in mogen, maar nu is het gewoon een bakje waar alleen C en C-derivants in mogen. En dus B absoluut niet, en dus mag je 'm volledig terecht niet terugcasten naar Collection<A> :)

De expliciete cast zal overigens een InvalidCast exception gooien :)

Ik denk overigens dat wat je wil niet kan met generics, en dat dat juist een van de dingen is waar je C++ templates bij nodig hebt. In C++ zou je namelijk meerdere typename parameters aan die functie meegeven ;) Omdat ik me nog niet in .NET generics verdiept hebt daarentegen zal ik daar nog niet te stellig over doen :P

[ Voor 12% gewijzigd door curry684 op 21-06-2005 02:15 ]

Professionele website nodig?


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

Alarmnummer

-= Tja =-

curry684 schreef op dinsdag 21 juni 2005 @ 02:12:
[nohtml]
Ik denk overigens dat wat je wil niet kan met generics, en dat dat juist een van de dingen is waar je C++ templates bij nodig hebt.
c++ Templates zijn voor zover ik weet veel minder krachtig dan generics in c#. In c# (.NET) kan runtime code gegenereerd worden van structuren waar iedere c++ compiler op vast gaat lopen omdat c++ alles compiletime willen doen. Een voorbeeld hiervan is polymorfe recursie.
In C++ zou je namelijk meerdere typename parameters aan die functie meegeven ;)
Nogal naief op aan te nemen dat dit in c# niet kan. (Als je met een typename parameter een type parameter bedoelt).
Omdat ik me nog niet in .NET generics verdiept hebt daarentegen zal ik daar nog niet te stellig over doen :P
Verstandig. Je moet niet vergeten dat alle nieuwe implementaties kunnen voortborderen op ontwerpen en fouten die in het verleden zijn gemaakt. Vooral met het dynamische karakter van platformen zoals Java en .NET zijn er tegenwoordig hele andere mogelijkheden dan met een oudere taal zoals c++. De reden dat .NET/Java generics hebben gekregen is heel anders dan die in c++. c++ heeft geen object based class hierarchie en om compiletechnisch een structuur bruikbaar te maken voor een bepaald type, zal een nieuw type gemaakt moeten worden op basis van die structuur en het opgegeven type(s). In Java/.NET zijn generics toegevoegd om beter typesafe te kunnen werken, maar generics is hier veel minder noodzakelijk.

[ Voor 35% gewijzigd door Alarmnummer op 21-06-2005 08:26 ]


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
Alarmnummer schreef op dinsdag 21 juni 2005 @ 08:14:
[...]
c++ heeft geen object based class hierarchie en om compiletechnisch een structuur bruikbaar te maken voor een bepaald type, zal een nieuw type gemaakt moeten worden op basis van die structuur en het opgegeven type(s).
Als je generics alleen ziet als een oplossing voor het miljoen-collection-types probleem (elke typesafe container moest een aparte class zijn zonder generics), dan is het een nadeel dat je geen bestaande structuur kunt hergebruiken. De meeste collections zijn toch onder water verzamelingen van object adressen.

Het wordt een ander verhaal als je compile-time reflectie gebruikt om je structuur fundamenteel te wijzigen. Een C++ collectie kan bijvoorbeeld een method foo implementeren als de aanroep van foo op alle members, dan en slechts dan als het member type een method foo heeft.

En als performance uitmaakt, dan is een vector<int> een container die net zo goed performt
als een raw array (en valarray<int> nog beter). Dat soort performance is fundamenteel onhaalbaar als je uitgaat van een root Object class.

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


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

Alarmnummer

-= Tja =-

MSalters schreef op dinsdag 21 juni 2005 @ 10:10:
[...]
Als je generics alleen ziet als een oplossing voor het miljoen-collection-types probleem (elke typesafe container moest een aparte class zijn zonder generics), dan is het een nadeel dat je geen bestaande structuur kunt hergebruiken. De meeste collections zijn toch onder water verzamelingen van object adressen.
Ik geloof niet dat ik deze suggestie heb gewekt.
Het wordt een ander verhaal als je compile-time reflectie gebruikt om je structuur fundamenteel te wijzigen. Een C++ collectie kan bijvoorbeeld een method foo implementeren als de aanroep van foo op alle members, dan en slechts dan als het member type een method foo heeft.
Ik zie de meerwaarde van 'compile-time' niet ;) C++ heeft niet de mogelijkheid (voor zover ik weet) om runtime te compileren en dat heeft een platform zoals .NET wel. In principe kan de .NET compiler mbt generics alles wat de c++ compiler kan + meer (aangezien er ook runtime bewerkingen op plaats kunnen vinden).

Je hoeft me verder niet duidelijk te maken welke meerwaarde generics/templates/geparametriseerde types/geparametriseerde polymorfisme heeft.
En als performance uitmaakt, dan is een vector<int> een container die net zo goed performt
als een raw array (en valarray<int> nog beter). Dat soort performance is fundamenteel onhaalbaar als je uitgaat van een root Object class.
Een taal zoals Java en bij c# is trouwens niet alles een object. Er zijn ook primitieve types en deze kan je dus niet plaatsen op een plek waar een object wordt verwacht, vb:

List<Object> l = ...;
l.add(10)

Dit zou in principe niet goed gaan omdat 10 dus geen object is maar een primitieve waarde (negeerd ff autoboxing). Daarom moet je in java een primitief type dus verpakken in een object :
l.add(new Integer(10));

Idd.. dit is niet goed voor de performance. Maar ik geloof dat dit probleem bij c# een stuk beter is opgelost. *weet het niet zeker meer*.. misschien kan iemand daar een lichtje op laten schijnen?

[ Voor 4% gewijzigd door Alarmnummer op 21-06-2005 10:47 ]


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 28-11 08:35

curry684

left part of the evil twins

Alarmnummer schreef op dinsdag 21 juni 2005 @ 08:14:
[...]

c++ Templates zijn voor zover ik weet veel minder krachtig dan generics in c#. In c# (.NET) kan runtime code gegenereerd worden van structuren waar iedere c++ compiler op vast gaat lopen omdat c++ alles compiletime willen doen. Een voorbeeld hiervan is polymorfe recursie.
De heersende mening is overigens dat templates veel en veel en veel krachtiger zijn omdat ze alles op compiletime al oplossen dus ik vind deze opmerking zeer opvallend ;) Nut en implementatie van polymorfe recursie zal je overigens even moeten uitleggen voordat ik kan vertellen of templates dat kunnen.

Grootste minpunt van C++ templates is overigens dat het taaltechnisch onmogelijk is om een base class af te dwingen voor een typename parameter, wat C# zo te zien dus wel native kan. Dit kan overigens met boost static asserts dan weer wel gewoon.

Professionele website nodig?


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 30-11 00:17
Alarmnummer schreef op dinsdag 21 juni 2005 @ 10:41:
Ik zie de meerwaarde van 'compile-time' niet ;)
Als je bijvoorbeeld naar real-time systemen kijkt wil je niet dat er runtime dingen gecompileerd worden.
C++ heeft niet de mogelijkheid (voor zover ik weet) om runtime te compileren en dat heeft een platform zoals .NET wel. In principe kan de .NET compiler mbt generics alles wat de c++ compiler kan + meer (aangezien er ook runtime bewerkingen op plaats kunnen vinden).
Ik snap eerlijk gezegd niet wat er nou zon groot voordeel is aan runtime compileren. Er is al vaker geopperd dat dit optimalisaties zou mogelijk maken die compiletime niet kunnen, maar toch heb ik persoonlijk nog niet veel praktijk voorbeelden gezien waarbij dit goed tot zijn recht komt.
Heb je misschien een praktijkvoorbeeld waarvan je zegt dat rtc een groot voordeel was?
Dit kan overigens met boost static asserts dan weer wel gewoon.
Hoe implementeren ze die static asserts dan? Met templates? :)

[ Voor 9% gewijzigd door farlane op 21-06-2005 10:58 ]

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.


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

Alarmnummer

-= Tja =-

curry684 schreef op dinsdag 21 juni 2005 @ 10:54:
[...]
De heersende mening is overigens dat templates veel en veel en veel krachtiger zijn omdat ze alles op compiletime al oplossen dus ik vind deze opmerking zeer opvallend ;)
De c# implementatie kan zowel runtime als compiletime code genereren. De C++ compiler kan het enkel en alleen compiletime, je moet het met me eens zijn dat in dat opzicht de c# compiler (of eigelijk de .NET omgeving) krachtiger is dan de c++ compiler.
Nut en implementatie van polymorfe recursie zal je overigens even moeten uitleggen voordat ik kan vertellen of templates dat kunnen.
vb:
code:
1
2
3
4
5
foo(List<X> l){
     List<List<X>> l2= new List<List<X>>();
     l2.add(l);
     foo(l2);
}


Dit is onmogelijk te realiseren in c++ omdat niet alle uiteindelijke types van List<X> bedacht kunnen worden. Stel dat ik de aanroep doe met een int, dan zul je dus een List<int>, List<List<int>>, List<List<List<int>>> etc etc krijgen. Hierover gaat de c++ compiler klagen.. de c# compiler heeft hier geen enkel probleem mee. Als hij runtime een nieuw geparametriseerde instantie ziet en hij heeft er nog geen code voor, dan zal hij dat ter plekke gaan compileren en toevoegen aan een centrale repository.

  • curry684
  • Registratie: Juni 2000
  • Laatst online: 28-11 08:35

curry684

left part of the evil twins

Alarmnummer schreef op dinsdag 21 juni 2005 @ 10:41:
[...]

Dit zou in principe niet goed gaan omdat 10 dus geen object is maar een primitieve waarde (negeerd ff autoboxing). Daarom moet je in java een primitief type dus verpakken in een object :
l.add(new Integer(10));

Idd.. dit is niet goed voor de performance. Maar ik geloof dat dit probleem bij c# een stuk beter is opgelost. *weet het niet zeker meer*.. misschien kan iemand daar een lichtje op laten schijnen?
De volgende C# code compileert iig moeiteloos in VS.NET 2k3:
C#:
1
2
3
4
5
6
void Add(object x)
{
    Add(x);
    Add(10);
    Add(1.5f);
}

Dit is afaik echter gewoon implicit boxing en dus alsnog geen bal beter :)

Professionele website nodig?


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 28-11 08:35

curry684

left part of the evil twins

Alarmnummer schreef op dinsdag 21 juni 2005 @ 10:59:
[...]

De c# implementatie kan zowel runtime als compiletime code genereren. De C++ compiler kan het enkel en alleen compiletime, je moet het met me eens zijn dat in dat opzicht de c# compiler (of eigelijk de .NET omgeving) krachtiger is dan de c++ compiler.
Mjah ik zie het liever als "je wordt gedwongen op een andere manier over dingen na te denken" :) C++ werkt gewoon op een ander niveau en heeft geen benul van runtime repositories etc., en moet gewoon vanuit z'n oorsprong als assembly-compiled taal alles op compiletime op moeten lossen. C# (of .NET) heeft het voordeel van de achterliggende runtime om dit soort problemen op te kunnen lossen, maar kan dat alleen omdat ze een achterliggende runtime heeft die hierdoor een flinke hap uit de potentiele performance neemt. Zoals farlane al aangeeft diskwalificeert .NET zich door dit soort kostbare side-effects al redelijk snel voor realtime programmatuur. Tzijn ook andere beestjes wat dat betreft, dus welke er krachtiger is is moeilijk tot niet te deduceren :)

Professionele website nodig?


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

Alarmnummer

-= Tja =-

farlane schreef op dinsdag 21 juni 2005 @ 10:56:
[...]
Als je bijvoorbeeld naar real-time systemen kijkt wil je niet dat er runtime dingen gecompileerd worden.
Dit is zo.. maar dan had je imho ook al niet moeten kijken naar java of .net.
Ik snap eerlijk gezegd niet wat er nou zon groot voordeel is aan runtime compileren.
De java compiler die compileerd helemaal 0.0. Die houd compiletime rekening met geparametriseerde types maar runtime bestaat er geen verschil tussen List<String> en List<Boolean> Het zijn allemaal lijsten. (Stiekum wordt wel wat type informatie meegenomen in een attribuut in de bytecode zodat je ook gecompileerde code typesafe kunt parametriseren, maar dit is weer een compiletime kwestie).

.NET daarin tegen heeft wel begrepen hoe ze generics moeten implementeren (bij java wilden ze backward compatible blijven) en daar bestaat dus runtime ook echt het verschil tussen List<String> en List<Boolean>
Er is al vaker geopperd dat dit optimalisaties zou mogelijk maken die compiletime niet kunnen, maar toch heb ik persoonlijk nog niet veel praktijk voorbeelden gezien waarbij dit goed tot zijn recht komt.
Ik weet niet in hoeverre dit runtime sneller zou zijn dan compiletime en hoe je er beter op in zou kunnen springen. Maar er zijn situaties die compiletime niet afgedekt kunnen worden.. dus dan geld -> iets is beter dan niets. Dus beter een runtime compilatie dan geen compilatie. En verder hoef het niet zo te zijn dat een c# compiler compiletime dan helemaal niets meer doet. Hij kan op dezelfde manier code genereren als de c++ dat ook kan. Hij kan runtime alleen ook nog wat doen.. dus qua generics heeft de c# een aantal mogelijkheden die een c++ niet heeft.

[ Voor 15% gewijzigd door Alarmnummer op 21-06-2005 11:09 ]


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

Alarmnummer

-= Tja =-

curry684 schreef op dinsdag 21 juni 2005 @ 11:00:
[...]

De volgende C# code compileert iig moeiteloos in VS.NET 2k3:
C#:
1
2
3
4
5
6
void Add(object x)
{
    Add(x);
    Add(10);
    Add(1.5f);
}

Dit is afaik echter gewoon implicit boxing en dus alsnog geen bal beter :)
Check ff mijn opmerking over autoboxing. Het gaat om het idee.. als autoboxing niet zou bestaan, dan zou dit voorbeeld neit gecompileerd kunnen worden.

  • zoepercavia
  • Registratie: September 2001
  • Laatst online: 02-11 22:01
Even een kleine inbraak in de overigens interessante discussie: hebben jullie misschien een idee hoe ik dit in de geest van C# moet oplossen? Het zou namelijk wat beroerd zijn als het niet zou kunnen. Maar ik zie vast wat over het hoofd want er is uiteraard goed over nagedacht.

Misschien kan dit:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
public class DomainObjectCollection<T> : List<T> where T : DomainObject {
...
public DomainObjectCollection<DomainObject> ToDomainObjectCollection {
get {
    DomainObjectCollection<DomainObject> result = new DomainObjectCollection<DomainObject>();
    foreach(DomainObject obj in this) {
            result.Add(obj);
    }
    return result;
}
}
}
}


Dan kan ik ToDomainObjectCollection gebruiken voor dat ik de collectie in CreateRelation stop. Het blijft echter erg lelijk...
Overigens mag zelfs expliciet casten niet: een DomainObjectCollection<Receipt> kan niet naar een DomainObjectCollection<DomainObject> gecast worden.

[ Voor 11% gewijzigd door zoepercavia op 21-06-2005 11:58 ]

Panacea.NL als je geinteresserd bent in IT en Geneeskunde!


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 28-11 08:35

curry684

left part of the evil twins

zoepercavia schreef op dinsdag 21 juni 2005 @ 11:57:
Overigens mag zelfs expliciet casten niet: een DomainObjectCollection<Receipt> kan niet naar een DomainObjectCollection<DomainObject> gecast worden.
Dat zei ik ook al en de reden heb ik toegelicht in [NOHTML][rml]curry684 in "[ C#] Generics / casting"[/rml][/NOHTML] ;)

Het is gewoon geen valide cast, andersom zou het wel mogen omdat je dan de mogelijkheden van het object beperkt, zoals je het nu doet geef je het object meer mogelijkheden dan het origineel heeft.

In C++ krijg ik je code overigens moeiteloos gebouwd:
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
#include <list>

using namespace std;

class DomainObject
{
};

class Receipt : public DomainObject
{
};

template <typename T>
class DomainObjectCollection : public list<T>
{
  // ...
};

//een domein object
class Person : public DomainObject 
{
public:
  DomainObjectCollection<Receipt>   Receipts;
};

//methode
template <typename T, typename V>
void CreateRelation(const T &obj, const DomainObjectCollection<V> &collection)
{
}

void Create(const Person &obj)
{
  CreateRelation(obj, obj.Receipts);
}

Hoe dit in C# 2.0 moet weet ik helaas niet :)

[ Voor 35% gewijzigd door curry684 op 21-06-2005 12:12 ]

Professionele website nodig?


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
Alarmnummer schreef op dinsdag 21 juni 2005 @ 10:59:
vb:
code:
1
2
3
4
5
foo(List<X> l){
     List<List<X>> l2= new List<List<X>>();
     l2.add(l);
     foo(l2);
}


Dit is onmogelijk te realiseren in c++ omdat niet alle uiteindelijke types van List<X> bedacht kunnen worden. Stel dat ik de aanroep doe met een int, dan zul je dus een List<int>, List<List<int>>, List<List<List<int>>> etc etc krijgen.
En? Je hebt een oneindige recursie, en C++ vind 'm tijdens compileren. Ik kan me niet voorstellen dat je die oneindige recursie in .NET pas runtime een probleem is.

Met partiele specialisatie kun je natuurlijk in C++ die recursie afkappen (i.t.t. C#), door een foo<List<List<List<X> > > te declareren.

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


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

Alarmnummer

-= Tja =-

MSalters schreef op dinsdag 21 juni 2005 @ 12:57:
[...]
En? Je hebt een oneindige recursie
Het schijnt dat je de diepte van recursie onder controle kunt houden.

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 30-11 00:17
Alarmnummer schreef op dinsdag 21 juni 2005 @ 11:06:
Dit is zo.. maar dan had je imho ook al niet moeten kijken naar java of .net.
Da klopt, maar het was maar om aan te geven dat in sommige situaties de meerwaarde van compile time mij wel duidelijk is.

Overigens zou ik graag een realtime omgeving hebben met de de uitgebreide mogelijkheden van een Java of .Net framework :)

Verder ben ik met mijn boerenverstand geneigd om je gelijk te geven wat betreft de ct/rt mogelijkheden van de C# vs C++ compilers.

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: 18:04
zoepercavia schreef op dinsdag 21 juni 2005 @ 11:57:
Even een kleine inbraak in de overigens interessante discussie: hebben jullie misschien een idee hoe ik dit in de geest van C# moet oplossen? Het zou namelijk wat beroerd zijn als het niet zou kunnen. Maar ik zie vast wat over het hoofd want er is uiteraard goed over nagedacht.
Zoals curry al zei, je geeft een DomainObject door, terwijl je een Receipt verwacht.
Een Receipt is wel een DomainObject, maar een DomainObject is geen Receipt.
Als je je definitie aanpast, zodanig dat je generic type een DomainObject verwacht, dan werkt het wel.

https://fgheysels.github.io/


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
Alarmnummer schreef op dinsdag 21 juni 2005 @ 13:04:
[...]
Het schijnt dat je de diepte van recursie onder controle kunt houden.
Ja, duhhuh. Maar dan moet je niet schrijven dat de C++ compiler er een probleem met oneindige recursie heeft. Of de recursie is oneindig, en dan heeft C++/C# een probleem at compiletime respectievelijk runtime, of de recursie eindigt, en dan heeft C++ noch C# geen probleem.

Kortom: feitelijk is er weer geen verschil. Op zich is dat niet zo gek: C++ templates zijn in feite scripts die runnen @ compile-time, en het taaltje is Turing-compleet. Dus halting problemen e.d. treden ook gewoon op.

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


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

Alarmnummer

-= Tja =-

MSalters schreef op dinsdag 21 juni 2005 @ 16:30:
[...]

Ja, duhhuh. Maar dan moet je niet schrijven dat de C++ compiler er een probleem met oneindige recursie heeft.
Ik heb nooit gezegd dat c++ een probleem met oneindige recursie heeft. c++ heeft problemen met eindige recursie. Er zijn bepaalde correcte recursieve applicaties die niet (zonder hacks) gecompileerd (en gedraaid) kunnen worden die wel met c# gedraaid kunnen worden. Jij moet niet proberen om mijn woorden te verdraaien en daar een positief argument voor c++ uit laten komen. Ik denk dat ik je duidelijk genoeg heb uitgelegd dat er dus dingen zijn die c# wel kan en c++ (zonder hacks) niet. Als je dit tot zover niet hebt begrepen dan vind ik dat erg vervelend voor je maar ik ga het niet nog een keer uitleggen.
Of de recursie is oneindig, en dan heeft C++/C# een probleem at compiletime respectievelijk runtime, of de recursie eindigt, en dan heeft C++ noch C# geen probleem.
Dat is geen juiste conclusie. Met c++ had het programma niet gecompileerd en gedraaid kunnen worden (zonder 'hacks').. Bij c# kan het gecompileerd en gedraaid worden. Dit lijkt me nogal een groot verschil.
Kortom: feitelijk is er weer geen verschil. Op zich is dat niet zo gek: C++ templates zijn in feite scripts die runnen @ compile-time, en het taaltje is Turing-compleet. Dus halting problemen e.d. treden ook gewoon op.
Wat heeft dit er nu mee te maken? Jij hebt een vreemde manier van redeneren en argumenteren.

[ Voor 10% gewijzigd door Alarmnummer op 21-06-2005 16:50 ]


  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
Dit resulteert in de foutmelding:

"Cannot convert from DomainObjectCollection<Receipt> to DomainObjectCollection<DomainObject>"


Maar dat is toch vaag :?

Immers erft Receipt over van DomainObject dus lijkt mij het logisch dat je DomainObjectCollection<Receipt> impliciet kan casten naar DomainObjectCollection<DomainObject>. Zeker omdat er ook nog 'where T : DomainObject' staat in die collectie.
Beetje ontzettende topic-kick, maar ook ik had gehoopt op GoT het antwoord te vinden op deze vraag. Dat is dus deels gelukt (als in: wel de vraag gevonden, maar geen antwoord :P ).
Inmiddels ben ik ergens (ben de link kwijt) de motivatie hiervoor tegengekomen, en deze post ik hier voor alle anderen die misschien na mij hier nog terecht komen:

De reden dat je DomainObjectCollection<Receipt> niet kan casten naar een DomainObjectCollection<DomainObject> is dat je niet wil dat het volgende gebeurt:
C#:
1
2
3
4
5
6
7
DomainObjectCollection<Receipt> rc = new DomainObjectCollection<Receipt>();

//Het volgende mag dus niet, want...
DomainObjectCollection<DomainObject> dc = receiptCollection;   //Aliasing

//...hoe moet de compiler dit type-checken?
dc.Add(new Person("Jan")); 

De laatste regel voldoet aan alle gestelde type-checks, immers, een Person is een DomainObject, en mag dus toegevoegd worden aan een collectie van DomainObjects. Toch levert dit een foutieve situatie op, want dc is in dit geval een collectie van Receipt-objecten, en een Person is geen Receipt.

Je redenatie is dat:
als een Receipt een soort van DomainObject is,
dan zal een DomainObjectCollection<Receipt> ook wel een soort van DomainObjectCollection<DomainObject> zijn.

Deze redenatie klopt niet, vervang de vage verwoording "een soort van" maar door "een subclass van", dan zie je dat die vlieger niet opgaat. Conceptueel hebben ze wel veel met elkaar te maken, maar voor de compiler zijn het gewoon verschillende types.

--edit--
Hmmz. Ik zal wel scheel zijn geweest 8)7
Pagina: 1