Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[VB2005] Dynamisches Erzeugen von abgeleiteten Klassen

4 views
Skip to first unread message

Wolf Wiegand

unread,
Jul 16, 2011, 3:13:07 PM7/16/11
to
Hallo,

folgendes Szenario¹: Software für eine Kraftfahrzeugvermietung. Es gibt
eine abstrakte Basisklasse 'cFahrzeug' und abgeleitete Klassen 'cAuto',
'cLkw' und 'cMotorrad'. Der Code zum Speichern und Laden der Kfz-Daten
steht als shared- bzw. static-Methode in der Basisklasse:

Public Shared Function LeseFahrzeug(kennzeichen as String) As IFahrzeug

Dieser Code kann nicht in den abgeleiteten Klassen stehen, da beim
Aufruf der Methode nicht bekannt ist, zu welchem Fahrzeugtyp das
Kennzeichen gehört.

Jetzt habe ich das Problem, dass ich in der LeseFahrzeug-Methode das
korrekte Objekt erzeugen muss, also entweder cAuto, cLkw oder cMotorrad.
Dazu speichere ich den Typ des jeweiligen Kfz ebenfalls in der Datenbank
und erzeuge dann beim Lesen per Reflection den korrekten Typ:

Dim ret as IFahrzeug
dim typ as String = LeseTyp(kennzeichen)

ret = DirectCast(CreateInstance(typ, False, BindingFlags.Default, _
Nothing, New Object() {sDatenFuerKonstruktor}, _
CultureInfo.InvariantCulture, Nothing), Interfaces.IFahrzeug)

Irgendwie gefällt mir aber nicht, dass ich hierfür Reflection einsetzen
muss, da es nicht wirklich elegant und vergleichsweise langsam ist.

Die offensichtliche Alternative mit Select Case:

Select Case sTyp
Case "Firma.Vermietung.cAuto"
ret = new cAuto
Case "Firma.Vermietung.cLkw"
ret = new cLkw
[...]
End Select

Gefällt mir aber auch nicht so, da ich jetzt schon weiß, dass ich
vergessen werde, diese Stelle ebenfalls anzupassen, sobald die Software
um cSchneemobil erweitert werden soll. Und elegant ist auch etwas anderes.

Gibt es noch weitere Wege, das gewünschte zu erreichen? Gerne auch
Hinweise, wenn man dieses Szenario völlig anders abdecken konnte oder
sollte.

Danke,

Wolf

¹: Tatsächlich geht es natürlich nicht um eine Autovermietung, aber
Autos scheinen ja das kanonische Standardbeispiel für Vererbung zu sein :-)
--
Oh Gravity, thou art a heartless bitch.

Peter Fleischer

unread,
Jul 17, 2011, 12:14:38 AM7/17/11
to
Hi Wolf,
in der Datenbank steht vermutlich der Name der gewünschten Klasse als
Zeichenkette. Um aus der Zeichenkette den zu instanziierenden Typ zu
bekommen, musst Du Reflection nutzen, wenn Du kein Select Case oder
If-Konstrukte nutzen willst.

Hier mal eine Demo, wie ich es machen würde:

Option Strict On

Imports System.Reflection

Module Module1

Sub main()
Dim f As IFahrzeug
f = cFahrzeug.LeseFahrzeug("ConsoleApplication1.Fahrzeuge.cMotorrad")

If f IsNot Nothing Then
Console.WriteLine("Klasse: {0}", f.Klasse)
End If

Console.WriteLine("--- fertig ---")
Console.ReadKey()
End Sub

End Module

Public Interface IFahrzeug
ReadOnly Property Klasse As String
End Interface

Public MustInherit Class cFahrzeug
Implements IFahrzeug

Public Shared Function LeseFahrzeug(ByVal kennzeichen As String) As
IFahrzeug
Dim t As Type = GetFahrzeugtyp(kennzeichen)
If t Is Nothing Then Return Nothing
Return CType(Activator.CreateInstance(t), IFahrzeug)
End Function

Private Shared Function GetFahrzeugtyp(ByVal kennzeichen As String) As
Type
'
Dim gewuenschterTyp As String = kennzeichen ' aus DB laden
'
Dim ass As [Assembly] = [Assembly].GetExecutingAssembly
Return ass.GetType(gewuenschterTyp)
End Function

Public MustOverride ReadOnly Property Klasse As String Implements
IFahrzeug.Klasse

End Class

Namespace Fahrzeuge

Public Class cAuto
Inherits cFahrzeug

Public Overrides ReadOnly Property Klasse As String
Get
Return "Auto"
End Get
End Property

End Class

Public Class cLkw
Inherits cFahrzeug

Public Overrides ReadOnly Property Klasse As String
Get
Return "Lkw"
End Get
End Property

End Class

Public Class cMotorrad
Inherits cFahrzeug

Public Overrides ReadOnly Property Klasse As String
Get
Return "Motorrad"
End Get
End Property

End Class

End Namespace


--
Viele Gruesse
Peter

Armin Zingler

unread,
Jul 16, 2011, 4:18:37 PM7/16/11
to
Am 16.07.2011 21:13, schrieb Wolf Wiegand:
> Gibt es noch weitere Wege, das gewünschte zu erreichen? Gerne auch
> Hinweise, wenn man dieses Szenario völlig anders abdecken konnte oder
> sollte.

Du hast die beiden programmatischen Möglichkeiten mit ihren Vor-/Nachteilen
eigentlich schon genannt.

Mich würde bei dem Beispiel eher interessieren, warum der Datentyp in der Datenbank
steht. Gibt es denn nicht unterschiedliche Tabellen für die unterschiedlichen Datentypen,
d.h. eine Auto- und eine LKW-Tabelle (zusätzlich zur gemeinsamen Fahrzeugtabelle)?
Ich meine, wenn Autos und Lkw in derselben Tabelle stehen obwohl sie zwar gemeinsame
aber auch jeweils eigene Felder haben, dann sollte man zuerst das Datenbank-Design
angehen (Normalisierung). Und gibt es dann mal unterschiedliche Tabellen, dann
muss im Code ohnehin abhängig von der Tabelle, in der sich das Fahrzeug
befindet, differenziert werden, welche (Objekt-)Instanz erzeugt wird.
Irgendwo muss es also einen "Select Case" geben, an welcher Stelle auch immer.
Automatisiert, also ohne etwas am Code ändern zu müssen, wenn die Schneemobile
noch hinzukommen, sehe ich keine Möglichkeit ohne Reflection.

Meine Wahl, da ich Reflection nur dann verwende, wenn es gar nicht anders geht,
wäre also die "select case"-Variante. Einen zusätzlichen Typ musst du sowieso
im ganzen Projekt berücksichtigen, also überall, wo du nicht mit IFahrzeug
arbeitest sondern mit dem konkreten Typ. Da ist das Laden aus der Datenbank
nur eine Sache, die man vergessen könnte.

--
Armin

Peter Fleischer

unread,
Jul 17, 2011, 8:40:25 AM7/17/11
to
Hi Armin,
wenn ein Select Case genutzt wird, muss alles bekannt sein. Wenn Klassen
später hinzukommen (in separaten dll), dann braucht man bei der Nutzung von
Reflection keinen Select Case und damit auch keine Änderungen im
"Hauptprogramm". Man kann also dynamisch weitere Typen hinzufügen.

--
Viele Gruesse
Peter

Armin Zingler

unread,
Jul 17, 2011, 8:42:51 AM7/17/11
to

Warum wiederholst du nochmal, was ich schon geschrieben habe?

--
Armin

Peter Fleischer

unread,
Jul 17, 2011, 11:39:42 AM7/17/11
to
Hi Armin,
Du hattest gefragt und ich geantwortet:

> Mich w�rde bei dem Beispiel eher interessieren, warum der Datentyp in der
> Datenbank steht.

--
Viele Gruesse
Peter

Armin Zingler

unread,
Jul 17, 2011, 12:05:52 PM7/17/11
to
Am 17.07.2011 17:39, schrieb Peter Fleischer:
> Hi Armin,
> Du hattest gefragt und ich geantwortet:
>
>> Mich w�rde bei dem Beispiel eher interessieren, warum der Datentyp in der
>> Datenbank steht.

Das kannst du doch nicht beantworten sondern Wolf. Um den richtigen Instanztyp
zu erkennen, muss nicht der Klassenname in der Datenbank stehen. Das hat
auch nichts damit zu tun, ob du im Programm einen flexiblen Automatismus per
Reflection nutzt oder nicht.


--
Armin

Peter Fleischer

unread,
Jul 17, 2011, 1:06:48 PM7/17/11
to
Hi Armin,
natürlich kann der Klassenname als Zeichenkette in der Datenbank stehen (s.
mein Beispiel). Wenn sich die Klassen in anderen dll's befinden, die später
dem Anwender übergeben werden, dann muss natürlich noch eine Zeichenkette
für die dazugehörende Assembly in die Datenbank eingetragen wird. Im
einfachsten Fall ist das der vollqualifizierte Name (Assembly, Namensraum,
Klasse).

--
Viele Gruesse
Peter

Armin Zingler

unread,
Jul 17, 2011, 1:39:18 PM7/17/11
to
Am 17.07.2011 19:06, schrieb Peter Fleischer:
> Hi Armin,
> natürlich kann der Klassenname als Zeichenkette in der Datenbank stehen (s.
> mein Beispiel).

Klar kann er das. Habe auch nichts Gegenteiliges behauptet sondern hatte
gefragt, warum er dort steht. Wenn es darum geht, zu erkennen, um welchen Typ
es sich handelt, gibt es auch noch andere Möglichkeiten ohne
den Klassennamen in der DB abzulegen. Wenn es z.B. zusätzlich zum Satz in einer
gemeinsamen Fahrzeugtabelle nur einen Satz in einer Auto-Tabelle gibt, dann wäre die
Information in der Fahrzeugtabelle, dass es sich um ein Auto handelt (und nicht
um einen LKW) handelt, redundant. Deswegen hatte ich bei Wolf _nachgefragt_, wie
es derzeit im (imaginären) Fall gelöst ist und ausgeführt, wie es bei einer
normalisierten Datenbank aussehen könnte. Daran ist auch nicht's verkehrt, auch
wenn ich mir's nochmal durchlese. Verstehe deine Kritik immer nicht.

> Wenn sich die Klassen in anderen dll's

Von anderen Dlls ist auch gar nicht die Rede, zumindest nicht von mir
und auch nicht von Wolf.

--
Armin

Peter Fleischer

unread,
Jul 17, 2011, 1:54:10 PM7/17/11
to
Hi Armin,
solange sich Wolf nicht äußert, können wir nur rätseln. Ich gehe davon aus,
dass die Ablage eines Klassennamens in einer Datenbank das Ziel hat, eine
einfach erweiterbare Anwendung zu erstellen, deren Kern nicht verändert
werden muss, wenn zusätzliche Typen hinzuzufügen sind.

--
Viele Gruesse
Peter

Armin Zingler

unread,
Jul 17, 2011, 2:25:09 PM7/17/11
to

Klar. Du scheinst aber davon auszugehen, dass Reflection - und nur dafür bräuchte
man den Klassennamen in der Datenbank - verwendet werden _muss_. Diese Annahme
ist aber nicht so ganz richtig, denn Wolf schrieb ja schon:

"Irgendwie gefällt mir aber nicht, dass ich hierfür Reflection einsetzen
muss, da es nicht wirklich elegant und vergleichsweise langsam ist."

Er hat ja selbst die beiden Alternativen, mit und ohne Reflection, aufgezeigt.

--
Armin

Jochen Kalmbach [MS MVP]

unread,
Jul 18, 2011, 3:51:31 AM7/18/11
to
Hall Wolf!

Verwende doch den XmlSerializer um das ganze zu speichern... der nimmt
Dir alles ab...

Greetings
Jochen

Wolf Wiegand

unread,
Jul 18, 2011, 2:14:31 PM7/18/11
to
Hallo,

Am 16.07.2011 22:18, schrieb Armin Zingler:

> Mich würde bei dem Beispiel eher interessieren, warum der Datentyp in der Datenbank
> steht. Gibt es denn nicht unterschiedliche Tabellen für die unterschiedlichen Datentypen,
> d.h. eine Auto- und eine LKW-Tabelle (zusätzlich zur gemeinsamen Fahrzeugtabelle)?

Nein, eine Tabelle für alle Fahrzeugtypen.

> Ich meine, wenn Autos und Lkw in derselben Tabelle stehen obwohl sie zwar gemeinsame
> aber auch jeweils eigene Felder haben, dann sollte man zuerst das Datenbank-Design
> angehen (Normalisierung).

In der Tabelle für die Fahrzeugtypen stehen nur die gemeinsamen
Eigenschaften (z.B. Kennzeichen). Alle weiteren Eigenschaften (je nach
Fahrzeugart unterschiedlich) werden in einer weiteren Tabelle

FahrzeugId | EigenschaftsId | Wert
------------------------------------
xxxx | nnnn | foobar

gespeichert. Jeder Fahrzeugtyp definiert einmal, welche Eigenschaften er
hat, das Speichern erfolgt für alle Eigenschaften über den selben Weg.
Zur Zeit sind die vorhandenen Eigenschaften noch pro abgeleiteter Klasse
fest in der jeweiligen Klasse definiert, aber so habe ich die Option,
die Eigenschaften pro Fahrzeugtyp z.B. in einer Konfigurationsdatei
abzulegen.

> Meine Wahl, da ich Reflection nur dann verwende, wenn es gar nicht anders geht,
> wäre also die "select case"-Variante. Einen zusätzlichen Typ musst du sowieso
> im ganzen Projekt berücksichtigen, also überall, wo du nicht mit IFahrzeug
> arbeitest sondern mit dem konkreten Typ.

Ja, für das Frontend der Anwendung ist mir das bewusst. Es wäre nur
schön, wenn man zumindest in der Geschäftslogik auf solche
Unterscheidungen verzichten könnte.


Schönen Gruß,

Wolf

Wolf Wiegand

unread,
Jul 18, 2011, 2:15:48 PM7/18/11
to
Hallo,

Am 18.07.2011 09:51, schrieb Jochen Kalmbach [MS MVP]:

> Verwende doch den XmlSerializer um das ganze zu speichern... der nimmt
> Dir alles ab...

Danke für das Stichwort, werde ich mir mal anschauen.


Schönen Gruß,

Wolf

0 new messages