Discussion:
dziedziczenie enum
(Wiadomość utworzona zbyt dawno temu. Odpowiedź niemożliwa.)
Przemek Biernat
2012-03-26 08:01:24 UTC
Permalink
Czy ktoś może mi wyjaśnić dlaczego twórcy języków C++ i C# nie
uwzględnili w swoich dziełach możliwości dziedziczenia enum'ów? Czy
istnieje jakiś język w którym takie operacje są możliwe? Jeśli tak,
zacznę pisać w takim języku. Może w D jest coś takiego?
Paweł Kierski
2012-03-26 08:15:06 UTC
Permalink
Post by Przemek Biernat
Czy ktoś może mi wyjaśnić dlaczego twórcy języków C++ i C# nie
uwzględnili w swoich dziełach możliwości dziedziczenia enum'ów? Czy
istnieje jakiś język w którym takie operacje są możliwe? Jeśli tak,
zacznę pisać w takim języku. Może w D jest coś takiego?
Tradycyjne pytanie - co chcesz osiągnąć za pomocą dziedziczenia enumów?
Przy okazji - jak w ogóle rozumiesz dziedziczenie enumów?
Jakoś tak? :

enum BaseColors
{
RED, GREEN, BLUE
};

enum RainbowColors : public BaseColors
{
YELLOW, ORANGE, INDIGO, VIOLET
};

BaseColors color = RainbowColors::VIOLET; // to by miało być poprawne?
--
Paweł Kierski
***@pkierski.net
Przemek Biernat
2012-03-26 18:58:57 UTC
Permalink
Post by Paweł Kierski
enum BaseColors
{
   RED, GREEN, BLUE
};
enum RainbowColors : public BaseColors
{
   YELLOW, ORANGE, INDIGO, VIOLET
};
BaseColors color = RainbowColors::VIOLET; // to by miało być poprawne?
--
     Paweł Kierski
Takie coś niepoprawne oczywiście. Ale takie coś:

RainbowColors color = BaseColors::RED

jak najbardziej

Poprostu enum o wężnym zakresie zawiera się w tym który go rozwija
hierarchię niżej. Wskaźnik na base enum może przechowywać faktycznie
child enum czyli taki pseudo polimorfizm na enumy, plus wsystkie
zalety z templatami akurat w C++ enum z templatami chodzą chyba dobrze
ale w C# już jest kiszka, bo nie da się zdefiniować że typ generyczny
ma być enumem jakiegoś ogólniejszego typu i wewnątrz templata rzutować
na int tylko trzeba przekazać do templata inta co jest denerwujące
(chodzi o definiowaniu w klauzuli where). Pozatym nie tylko enumy są
jakieś niedorobione. Na przykład zawieranie się klas, dlaczego nie da
się napisać, akurat podaję w C# bo najbardziej widać:

public class abstract ClassBase
{
public abstract class InsideBase;

}

public class abstract
{
public override class InsideChild
{

}
}

gdzie:

public class InsideChild : InsideBase

Tak już są zaawansowane te języki a jak przychodzi co do czego to
programując czegoś brakuje. Albo w C# elementy statyczne nie mogą być
skutecznie templatowane w klauzuli where, akurat co w C++ działa chyba
ok, bo tam template jest kompilowany wtedy kiedy jest użyty, ale nie
jestem pewien czy statyki śmigają w templatach w C++ bo akurat takiego
czegoś tam nie robiłem.
Przemek Biernat
2012-03-26 19:18:49 UTC
Permalink
Post by Przemek Biernat
public class abstract ClassBase
{
     public abstract class InsideBase;
}
public class abstract
{
    public override class InsideChild
    {
    }
}
public class InsideChild : InsideBase
Sorry mała pomyłka chodziło o coś takiego:


public class abstract ClassBase
{
     public abstract class InsideBase;

}

public class abstract
{
    public override class InsideChild : InsideBase
    {

    }

}

gdzie:

public class InsideChild : InsideBase
Seweryn Habdank-Wojewódzki
2012-03-27 08:53:32 UTC
Permalink
Witam,
Post by Przemek Biernat
RainbowColors color = BaseColors::RED
A jaki problem to rozwiazuje? Enum to sa nazwane inty. Jak zaimplementujesz
podwojne dziedziczenie, a potem powrot w diamencie do jednej klasy.

Moim zdaniem poprawne rozwiazanie to uzycie wzorca strategii, a nie biadolenie,
ze C# lub C++ nie maja dziedziczenia po enumach.

Dodatkowo podany przyklad jest bardzo problematyczny z punktu widzenia projektu. Te same problemy ma wiele projektow API dla GUI. Nawarstwiaja sie
kolejne klasy, ktore maja coraz szersze API (bez stosownych interfejsow)
a potem nie mozna w sensowny sposob uzywac wskaznikow na klasy interfejsow.

Poczytaj o strategii.

Pozdrawiam,
Seweryn Habdank-Wojewodzki.
Przemek Biernat
2012-03-27 09:59:14 UTC
Permalink
Post by Seweryn Habdank-Wojewódzki
Witam,
Post by Przemek Biernat
RainbowColors color = BaseColors::RED
A jaki problem to rozwiazuje? Enum to sa nazwane inty. Jak zaimplementujesz
podwojne dziedziczenie, a potem powrot w diamencie do jednej klasy.
Moim zdaniem poprawne rozwiazanie to uzycie wzorca strategii, a nie biadolenie,
ze C# lub C++ nie maja dziedziczenia po enumach.
Dodatkowo podany przyklad jest bardzo problematyczny z punktu widzenia projektu. Te same problemy ma wiele projektow API dla GUI. Nawarstwiaja sie
kolejne klasy, ktore maja coraz szersze API (bez stosownych interfejsow)
a potem nie mozna w sensowny sposob uzywac wskaznikow na klasy interfejsow.
Poczytaj o strategii.
Pozdrawiam,
Seweryn Habdank-Wojewodzki.
Wiem co to jest wzorzec strategia i jak mniej więcej go stosować choć
tutaj jakoś nie widzę zastosowania, ale może inaczej patrzę na
problem. Dobra podam przykład, takie studium przypadku. Założmy że mam
taką strukturę, będę pisał w C# a raczej w jakimś pseudokodzie żeby
było czytelniej.

class Pojazd
{
public int LiczbaKol { get; set; }
public int Kolor { get; set; }
}

class Samochod : Pojazd
{
public int Pojemnosc { get; set; }
public int LiczbaKoniMech { get; set; }
}

class Rower : Pojazd
{
public int RozmiarObreczy { get; set; }
public int LiczbaPrzelozen { get; set; }
}

i teraz mam listę w której są samochody i rowery

List<Pojazd> lista;

i powiedzmy że mam aplikację klient-serwer na kliencie mam listę i
chcę ją przesłać na serwer w niezmienionej postaci, więc sobie
serializuję binarnie i pakuję przez TcpIp więc:

class Pojazd
{
public virtual void Serialize(BinaryWriter writer)
{
writer.Write(LiczbaKol);
writer.Write(Kolor);
}

public virtual void Load(BinaryReader reader)
{
LiczbaKol = reader.ReadInt32();
Kolor = reader.ReadInt32();
}
}

class Samochod : Pojazd
{

public int Pojemnosc { get; set; }
public int LiczbaKoniMech { get; set; }

public virtual void Serialize(BinaryWriter writer)
{
base.Serialize(writer);

writer.Write(Pojemnosc);
writer.Write(LiczbaKoniMech);
}

public virtual void Load(BinaryReader reader)
{
base.Load(reader);

Pojemnosc = reader.ReadInt32();
LiczbaKoniMech = reader.ReadInt32();
}
}

class Rower : Pojazd
{
public int RozmiarObreczy { get; set; }
public int LiczbaPrzelozen { get; set; }

public virtual void Serialize(BinaryWriter writer)
{
base.Serialize(writer);

writer.Write(RozmiarObreczy);
writer.Write(LiczbaPrzelozen);
}

public virtual void Load(BinaryReader reader)
{
base.Load(reader);

RozmiarObreczy = reader.ReadInt32();
LiczbaPrzelozen = reader.ReadInt32();
}
}

no i dobra jest serializacja i nawet udało się przesłać po sieci po
dwóch stronach mam to samo. Jak sobie zaimplementuję jeszcze oddzielne
klasy potomne po stronie serwera i klienta to nawet mogę mieć różne
zachowanie a dane te same. Ale kurde wyobraźcie sobie że mam jakieś
10-20 propertasów w każdym obiekcie, jak się pomylę w kolejności to
kiszka, dwa razy trzeba pisać i wogóle utrzymanie kłopotliwe, a jak
klas jest sporo to do dupy dwukrotnie. Można by użyć refleksji i
przejechać po property ale w C++ już nie, a powiedzmy że ja nie lubię
reflekcji i chce pisać w C# tak jak w C++ :). No to co trzeba zrobić
property w obiekcie więc:

class Property
{
public string Name;
public abstract object Value { get; set; }
}

class IntProperty : Property
{
private int _value;

public override object Value { get { return _value; } set{ value
= _value as int } }
}

itd...

no i klasa obiektu bazowego

class MyObject
{
Dictionary<int, Property> _properties

public void Serialize(BinaryWriter writer)
{
foreach(KeyValuePair<int, Poperty> pair in _properties)
{
writer.Write(pair.Key);
pair.Value.Serialize(writer);
}
}

// analogicznie dla Load
}

no i konkretny Obiekt

enum PojazdProp
{
PP_LiczbaKol,
PP_Kolor,
}

class Pojazd
{
public int LiczbaKol
{
get {return _properties[(int)PP_LiczbaKol].Value as int }
set { _properties[(int)PP_LiczbaKol].Value = value }
}

itd...
}

No i teraz mój problem polega na tym w słowniku properties muszę użyć
inta a ja chciałbym wpakować tam enuma i jak dojdzie hierarchia
Samochód Rower to wszystko się popieprzy, identyfikatory się pokryją i
będzie kupa, pozatym rzutowanie na inta w tylu miejscach zaciemnia
kod. Może da się sprawę serializacji rozwiązać inaczej ale ja
wymyśliłem takie coś i taką mam architekturę softu. Jedną bibliotekę
dzielę między serwer i klient a zachowanie po dwóch stronach mam
różne :) Oczywiście przykład jest uproszczony bo jeszcze trzeba
rozwiązać sprawę inicjacji properties ( w konstruktorze lub podczas
przypisywania wartości) ale generalnie maszynka działa bo sprawdzałem,
gdyby tylko te enumy dało się rozszerzać byłoby świetnie.

Przemek Biernat
2012-03-26 20:30:43 UTC
Permalink
Post by Paweł Kierski
Post by Przemek Biernat
Czy ktoś może mi wyjaśnić dlaczego twórcy języków C++ i C# nie
uwzględnili w swoich dziełach możliwości dziedziczenia enum'ów? Czy
istnieje jakiś język w którym takie operacje są możliwe? Jeśli tak,
zacznę pisać w takim języku. Może w D jest coś takiego?
Tradycyjne pytanie - co chcesz osiągnąć za pomocą dziedziczenia enumów?
Przy okazji - jak w ogóle rozumiesz dziedziczenie enumów?
enum BaseColors
{
   RED, GREEN, BLUE
};
enum RainbowColors : public BaseColors
{
   YELLOW, ORANGE, INDIGO, VIOLET
};
BaseColors color = RainbowColors::VIOLET; // to by miało być poprawne?
--
     Paweł Kierski
W zasadzie to mogłoby być poprawne, czemu nie, jeśli weźmiemy pod
uwagę wskaźniki. C# ma semantykę referencji jak wiadomo :) (odkryłem
amerykę :)). W C++ mogłoby to wyglądać tak:

BaseColors* color = &RainbiowColors::VIOLET;

ale chyba nie jest to możliwe to zaimplementowania w kompilatorze :)
Na pierwszy rzut oka cholernie nieprzydatne :) ale pomyślcie nad tym,
myślę że kilka problemów mogłoby rozwiązać, na przykład przy
serializacji struktur polimorficznych, ale tak żeby działo się to pół
automatycznie :), może jeszcze wkleję jakiś fragment kodu który
zaprezentuje o co mi chodzi.
Przemek Biernat
2012-03-26 20:30:32 UTC
Permalink
Post by Paweł Kierski
Post by Przemek Biernat
Czy ktoś może mi wyjaśnić dlaczego twórcy języków C++ i C# nie
uwzględnili w swoich dziełach możliwości dziedziczenia enum'ów? Czy
istnieje jakiś język w którym takie operacje są możliwe? Jeśli tak,
zacznę pisać w takim języku. Może w D jest coś takiego?
Tradycyjne pytanie - co chcesz osiągnąć za pomocą dziedziczenia enumów?
Przy okazji - jak w ogóle rozumiesz dziedziczenie enumów?
enum BaseColors
{
   RED, GREEN, BLUE
};
enum RainbowColors : public BaseColors
{
   YELLOW, ORANGE, INDIGO, VIOLET
};
BaseColors color = RainbowColors::VIOLET; // to by miało być poprawne?
--
     Paweł Kierski
W zasadzie to mogłoby być poprawne, czemu nie, jeśli weźmiemy pod
uwagę wskaźniki. C# ma semantykę referencji jak wiadomo :) (odkryłem
amerykę :)). W C++ mogłoby to wyglądać tak:

BaseColors* color = &RainbiowColors::VIOLET;

ale chyba nie jest to możliwe to zaimplementowania w kompilatorze :)
Na pierwszy rzut oka cholernie nieprzydatne :) ale pomyślcie nad tym,
myślę że kilka problemów mogłoby rozwiązać, na przykład przy
serializacji struktur polimorficznych, ale tak żeby działo się to pół
automatycznie :), może jeszcze wkleję jakiś fragment kodu który
zaprezentuje o co mi chodzi.
Jacek Czerwinski
2012-03-26 08:31:19 UTC
Permalink
Post by Przemek Biernat
Czy ktoś może mi wyjaśnić dlaczego twórcy języków C++ i C# nie
uwzględnili w swoich dziełach możliwości dziedziczenia enum'ów? Czy
istnieje jakiś język w którym takie operacje są możliwe? Jeśli tak,
zacznę pisać w takim języku. Może w D jest coś takiego?
Ja jestem urobiony "ewangelizatorami" Javy i tam jest w pewnym sensie
odpowiedź na Twoje pytanie.

Wprawdzie sam 'końcowy' enum jest final i nie podlega dziedziczeniu, ale
może implementować interfejs, i drugi enum z tego wspólnego przodka
dziedziczyć.

Joshua Bloch pisze, argumentacja jest (podobnie jak Piotr sygnalizuje)
że bardzo rzadko jest potrzeba prawdziwego dziedziczenia
Jako wyjątki od tego argumentu, podaje przykład ekstrakodów (np.
BIOS/DOS-a) a ktoś chce poszerzyć tę listę.

podaje przykład ekstrakodów np. BIOS/DOS-a a ktoś chce poszerzyć tę listę.

interface IBasicCode {
int getCokolwiek()
}

enum CorporationCode implements IBasicCode ...

enum FreelancerCode implements IBasicCode ...

void excecute(IBasicCode c)
{

}

Dziedziczenie jak dziedziczenie, ale dopiero na javowskim enumie
zobaczyłem, jaki enum powinien być.
Przemek Biernat
2012-03-26 20:11:22 UTC
Permalink
Post by Jacek Czerwinski
interface IBasicCode {
  int getCokolwiek()
}
enum CorporationCode implements IBasicCode ...
enum FreelancerCode implements IBasicCode ...
void excecute(IBasicCode c)
{
}
No właśnie o coś takiego mi chodzi, osobiście wolałbym taką składnię:

enum VehicleType
{
Car = 0 = Car,
MotorBike = 10 = MotorBike
}

enum CarType : VehicleType
{
CT_RedCar = 1 = KlasaRedCar,
CT_BlueCar = 2 = KlasaBlueCar,
CT_GreenCar = 3 = KlasaGreenCar,
CT_YellowCar = 4 = KlasaYellowCar,
}

enum MotorBikeType : VehicleType
{
MTB_RedBike = 11 = KlasaRedBike
MTB_BlueBike = 12 = KlasaBlueBike
MTB_GreenCar = 13 = KlasaGreenBike
MTB_YellowCar = 14 = klasaYellowCar
}

gdzie

class Vehicle;
class KlasaRedCar : Vehicle
itd...
class KlasaRedBike: Vehicle
itd...


i można by pisać coś takiego w kodzie zamiast trzaskać koślawe switch
case'y

Vehicle GetVehicle(VehicleType type)
{
return (Vehicle)type;
}

a wywołanie

Vehicle vehicle = GetVehicle(CT_RedCar);

zakładając że każda klasa dziedzicząca z Vehicle ma konstruktor
bezparametrowy.
Dlaczego enum nie może być typem referencyjnym.
Przemek Biernat
2012-03-26 20:11:08 UTC
Permalink
Post by Jacek Czerwinski
interface IBasicCode {
  int getCokolwiek()
}
enum CorporationCode implements IBasicCode ...
enum FreelancerCode implements IBasicCode ...
void excecute(IBasicCode c)
{
}
No właśnie o coś takiego mi chodzi, osobiście wolałbym taką składnię:

enum VehicleType
{
Car = 0 = Car,
MotorBike = 10 = MotorBike
}

enum CarType : VehicleType
{
CT_RedCar = 1 = KlasaRedCar,
CT_BlueCar = 2 = KlasaBlueCar,
CT_GreenCar = 3 = KlasaGreenCar,
CT_YellowCar = 4 = KlasaYellowCar,
}

enum MotorBikeType : VehicleType
{
MTB_RedBike = 11 = KlasaRedBike
MTB_BlueBike = 12 = KlasaBlueBike
MTB_GreenCar = 13 = KlasaGreenBike
MTB_YellowCar = 14 = klasaYellowCar
}

gdzie

class Vehicle;
class KlasaRedCar : Vehicle
itd...
class KlasaRedBike: Vehicle
itd...


i można by pisać coś takiego w kodzie zamiast trzaskać koślawe switch
case'y

Vehicle GetVehicle(VehicleType type)
{
return (Vehicle)type;
}

a wywołanie

Vehicle vehicle = GetVehicle(CT_RedCar);

zakładając że każda klasa dziedzicząca z Vehicle ma konstruktor
bezparametrowy.
Dlaczego enum nie może być typem referencyjnym.
f***@gazeta.pl
2012-03-26 08:47:38 UTC
Permalink
Czy kto=B6 mo=BFe mi wyja=B6ni=E6 dlaczego tw=F3rcy j=EAzyk=F3w C++ i C# ni=
e
uwzgl=EAdnili w swoich dzie=B3ach mo=BFliwo=B6ci dziedziczenia enum'=F3w? C=
zy
istnieje jaki=B6 j=EAzyk w kt=F3rym takie operacje s=B1 mo=BFliwe? Je=B6li =
tak,
zaczn=EA pisa=E6 w takim j=EAzyku. Mo=BFe w D jest co=B6 takiego?
ciekawa uwaga - zdaje sie ze wlasnie dziedziczenie enumow mialoby wiecej
sensu niz 'dziedziczenie' struktur - bo dziedziczenie struktur ma rodzaj
odpowiednika w zwyklej agregacji - tylko niejako w druga stronę np

miau(kot);
miau(costam.kot);
miau(costamB.kot);

w przypadku enuma nie ma natomiast takiego odpowiednika i tego rodzaju
rzeczy ew moglyby sie naprawde przydac (musialbym sie jednak zastanowic)
--
Wysłano z serwisu Usenet w portalu Gazeta.pl -> http://www.gazeta.pl/usenet/
Loading...