CYFROWY BARON • PROGRAMOWANIE • Zobacz wątek - Generyczne klasy bazowe i dziedziczenie

Generyczne klasy bazowe i dziedziczenie

dział ogólny

Generyczne klasy bazowe i dziedziczenie

Nowy postprzez Slynx » piątek, 30 sierpnia 2013, 19:12

Witam,
Mam drobny problem przy poprawianiu kodu. Przepisuje go na nowszy model, ale nie potrafię się uporać z jednym problemem. Jako, że opisywanie tego i wklejanie kawałków kodu stałoby się bardzo nieczytelne przedstawię to jako prosty model.

Obrazek

I teraz.
Files<T> jak przedstawione jest bazową dla dwóch innych kolekcji. Dodatkowo wewnątrz klasy zawiera listę generyczną:
Kod: Zaznacz cały
List<T> items;

I kilka(naście) metod, które są stosowane zarówno dla klasy bazowej jak i dziedziczących.
I teraz najważniejsze.
Klasa BaseClass zawiera właściwość
Kod: Zaznacz cały
public Files<BaseClass.BRecord> Files {get; set;}


Problem jest w sumie bardzo prosty. W klasach dziedziczących powinno być
Kod: Zaznacz cały
public D1Files Files {get; set;}


Problem próbowałem rozwiązać na wiele sposobów. Na początku, jako, że nie mogłem tego pogodzić w klasie bazowej było BaseFiles a w pochodnych Files. To jest złe rozwiązanie bo tylko dubluje niepotrzebne dane. Próbowałem przysłaniania ("new"), ale to powoduje wiele błędów gdy przez przypadek można się odwołać do klasy bazowej (niektóre funkcje działają na klasach bazowych, jeśli wewnątrz niej nie wykona się rzutowania na klasę dziedziczącą spowoduje to różne problemy - trudne do wykrycia). Poza tym, komplikacje wynikają również przy serializacji.
Próbowałem również implementacji Files jako pustej abstrakcyjnej klasy bazowej, tzn. wszystkie klasy - Files<BRecord>, D1Files, D2Files miały jako bazową klasę abstrakcyjną "Files". To działa do pewnego stopnia - technicznie jest poprawnie, ale każde odwołanie do takiej właściwości wymaga rzutowania na odpowiedni typ - raczej odpada.

Co chcę osiągnąć ? Najlepiej by było gdyby bazowa klasa abstrakcyjna Files zawierała wszystkie metody operujące na T : BRecord, ale to wymagało stworzenia jej jako generycznej:
Kod: Zaznacz cały
public abstract class Files<T> where T : BaseClass.BRecord
{
    private List<T> items;
    public T Get()
    {
        return items[0];
    }
}

O ile same metody można w ten sposób dodać przekazując typ przez T to stworzenie listy wymaga przekazania T już przez klasę, co uniemożliwia jej użycie jako bazowej w BaseClass i dziedziczenie w Derived1 i Derived2 z wykorzystanie typów Derived1.D1Record i Derived2.D2Record.

Jak coś przedstawiłem niejasno to pisać, mogłem o czymś zapomnieć.

Może rozwiązanie problemu jest bardzo proste, wszystko piszę sam, więc mogę mieć jakąś lukę w wiedzy lub zwyczajnie nie wpaść na pewien tok myślenia. W końcu co dwie głowy to nie jedna, więc proszę o pomoc.

Ah, zapomniałem o najważniejszym. Język to C#, choć to nie ma większego znaczenia, bo chodzi jedynie o teoretycznie rozwiązanie problemu, implementację pozostawię sobie ;)
Avatar użytkownika
Slynx
Mądrosław
Mądrosław
 
Posty: 350
Dołączył(a): piątek, 17 grudnia 2010, 21:59
Podziękował : 11
Otrzymał podziękowań: 0
System operacyjny: Windows 7 32
Kompilator: Visual C++ 2005; Visual C++ 2008; Visual C++ 2010; Visual C# 2010;
Gadu Gadu: 0
    Windows 7Chrome

Re: Generyczne klasy bazowe i dziedziczenie

Nowy postprzez polymorphism » sobota, 31 sierpnia 2013, 09:47

Making a long story short, chcesz, żeby właściwość Files z klasy bazowej BaseClass zachowywała się tak, jakby była właściwością klasy pochodnej (Derived1, Derived2). Dobrze rozumiem?
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2157
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 200
System operacyjny: Windows 8.1
Windows 10
Linux Mint 19
Kompilator: Visual Studio
Visual Studio Code
MSYS2 (MinGW, clang)
g++
clang
Gadu Gadu: 0
    Windows XPFirefox

Re: Generyczne klasy bazowe i dziedziczenie

Nowy postprzez Slynx » sobota, 31 sierpnia 2013, 10:46

"Making a long story short" - po prostu chciałem dokładnie zobrazować sytuację, żeby jakoś błędnie jej nie przedstawić, a problem nie wydaje mi się taki banalny.

chcesz, żeby właściwość Files z klasy bazowej BaseClass zachowywała się tak, jakby była właściwością klasy pochodnej (Derived1, Derived2). Dobrze rozumiem?

No nie do końca, dlatego oprócz klasy bazowej i pochodnej na schemacie przedstawiłem również kolekcję.
Właściwością klasy pochodnej już jest (co wynika z samego faktu zastosowania klasy bazowej), ale jej typ jest niewłaściwy w klasach pochodnych, a problemem, którego nie umiem pokonać to fakt, że typ jest klasą (kolekcją - wewnątrz jest lista generyczna, ale bezpośrednio nie implementuje żadnej interfejsu) generyczną gdzie T dla bazowej to BRecord, a pochodnych D1Record i D2Record.
Jeśli jeszcze niejasno wyjaśniłem to umieszczę kod z programu, może to łatwiej zobrazuje problem.

Czyli w sumie pytanie brzmiałoby - Jak zastosować generyczną klasę bazową (kolekcję - mówimy o BaseCollection ze schematu) dla T : BRecord w klasie bazowej (Y) oraz jej klasach pochodnych (Y1,Y2) gdzie T jest odpowiednio D1Record i D2Record.

Jak rozumiesz o co mi chodzi to zaproponuj rozwiązanie, jak się miniemy to poprawię.

P.S. Zaraz umieszczę kawałek kodu, będzie prościej, wskażę problem konkretnie.

KOD cpp:     UKRYJ  
public class BC
{
    public List<Hashcode.FileRecord> items;
}

public class DC : BC
{
    public List<DownloadHashcode.DownloadFileRecord> items;
}
 

Tak to powinno wyglądać. Jednak jak widać, trzeba stworzyć kopię items tak by typ był odpowiedni. W klasie pochodnej lista jest typu DownloadHashcode.DownloadFileRecord, a w bazowej Hashcode.FileRecord. Tutaj mamy przysłanianie i niepotrzebne kopiowanie listy. By działało to poprawnie trzeba dokonać zmiany.
KOD cpp:     UKRYJ  
public class BC<T> where T : Hashcode.FileRecord
{
    public List<T> items;
}

public class DC : BC<DownloadHashcode.DownloadFileRecord>
{
}
 

Teraz kod jest poprawny, typ listy w klasie pochodnej jest właściwy (FileRecord jest bazową dla DownloadFileRecord) i nie trzeba dublować listy. Jednakże...
KOD cpp:     UKRYJ  
public class Hashcode
{
    public BC<FileRecord> Files { get; set; }
}

public class DownloadHashcode
{
    public DC Files {get; set;} // tak powinien wyglądać typ Files, oczywiście Files już istnieje w klasie bazowej, więc taki zapis przysłoni Files z klasy bazowej
    public DownloadHashcode()
    {
        Files = new DC(); // Usuwając Files z klasy pochodnej (linia powyżej) taki zapis też byłby poprawny (powyższy jest niemożliwy - jak sądzę).
    }
}
 

BC<FileRecord> jest klasą bazową dla DC, więc zawiera wszystkie metody operujące na Hashcode.FileRecord, tj. bazowe od DownloadHashcode.DownloadFileRecord - i taki efekt chcę uzyskać.
Jednakże, kompilator oznacza to jako błąd.
Cannot convert source type "DC" to target type "BC<Hashcode.FileRecord>"

I to jest w sumie cały problem. Mimo, że teoretycznie jest poprawnie - BC jest bazową dla DC tak samo jak Hashcode.FileRecord dla DownloadHashcode.DownloadFileRecord.
Ostatnio edytowano sobota, 31 sierpnia 2013, 16:34 przez polymorphism, łącznie edytowano 1 raz
Powód: Zmieniłem znaczniki CODE na CPP.
Avatar użytkownika
Slynx
Mądrosław
Mądrosław
 
Posty: 350
Dołączył(a): piątek, 17 grudnia 2010, 21:59
Podziękował : 11
Otrzymał podziękowań: 0
System operacyjny: Windows 7 32
Kompilator: Visual C++ 2005; Visual C++ 2008; Visual C++ 2010; Visual C# 2010;
Gadu Gadu: 0
    Windows 7Chrome

Re: Generyczne klasy bazowe i dziedziczenie

Nowy postprzez polymorphism » sobota, 31 sierpnia 2013, 16:57

Pokaż, jak wyglądają definicje Hashcode, FileRecord, DownloadHashcode i DownloadFileRecord. Chodzi mi oczywiście o relacje między tymi klasami, zatem nie wklejaj tu metod i składowych, które nic nie wnoszą do sprawy.
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2157
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 200
System operacyjny: Windows 8.1
Windows 10
Linux Mint 19
Kompilator: Visual Studio
Visual Studio Code
MSYS2 (MinGW, clang)
g++
clang
Gadu Gadu: 0
    Windows XPFirefox

Re: Generyczne klasy bazowe i dziedziczenie

Nowy postprzez Slynx » sobota, 31 sierpnia 2013, 17:19

Problem rozwiązałem (trochę to zajęło przebudowywanie całości) poprzez zastosowanie kilku interfejsów. Wymaga to okazjonalnego rzutowania w niektórych przypadkach, ale inaczej chyba się tego zrobić nie da.
Może dorzucisz coś od siebie.

KOD cpp:     UKRYJ  
public class Hashcode
{
    public class FileRecord : IFileRecord
    {
    }
    public IFilesCollection<IFileRecord> Files { get; set; }
    public Hashcode()
    {
        Files = new FilesCollection();
    }
}

public class DownloadHashcode : Hashcode
{
    public class DownloadFileRecord : IFileRecord
    {
    }
    public DownloadHashcode()
    {
        Files = new DownloadFilesCollection();
    }
}

public class FilesCollection : IFilesCollection<IFileRecord>
{
    private readonly List<IFileRecord> items;
    public FilesCollection()
    {
        items = new List<IFileRecord>();
    }
}

public class DownloadFilesCollection : FilesCollection
{
}

public interface IFilesCollection<T> : IEnumerable<T> where T : IFileRecord
{
}

public interface IFileRecord
{
}
 

Tak jak wspomniałem, wymagane jest rzutowanie z interfejsu na jeden z typów pochodnych (jeśli funkcja znajduje się w typie pochodnym, a nie w interfejsie) np.

KOD cpp:     UKRYJ  
IFileRecord record =  downloadHashcode.Files[0];
DownloadFileRecord = record as IFileRecord;
 

I poza tym wszystko działa idealnie
Avatar użytkownika
Slynx
Mądrosław
Mądrosław
 
Posty: 350
Dołączył(a): piątek, 17 grudnia 2010, 21:59
Podziękował : 11
Otrzymał podziękowań: 0
System operacyjny: Windows 7 32
Kompilator: Visual C++ 2005; Visual C++ 2008; Visual C++ 2010; Visual C# 2010;
Gadu Gadu: 0
    Windows 7Chrome


  • Podobne tematy
    Odpowiedzi
    Wyświetlone
    Ostatni post

Powrót do Ogólne problemy z programowaniem

Kto przegląda forum

Użytkownicy przeglądający ten dział: Brak zalogowanych użytkowników i 10 gości

cron