Laden...

Metainformationen für Klassen: Unterklassen nach ihrem FriendlyName fragen können

Erstellt von onkel_keks vor 11 Jahren Letzter Beitrag vor 11 Jahren 4.328 Views
O
onkel_keks Themenstarter:in
20 Beiträge seit 2013
vor 11 Jahren
Metainformationen für Klassen: Unterklassen nach ihrem FriendlyName fragen können

Hallo,

ich versuche mal mein Problem zu beschreiben: Ich habe eine abstrakte Klasse, sagen wir, Auto. Von dieser erben jetzt einige andere Klassen, z.B. Volvo_S45 oder BMW_X5, eben konkrete "Ausführungen" eines Autos.
Mein Ziel ist es, diesen Kindklassen einen Namen (als Eigenschaft der Klasse, nicht einer bestimmten Instanz) zu geben, idealerweise möchte ich also so etwas machen können:

Debug.Print(Volvo_S45.FriendlyName);
-> "Volvo S45, Baujahr 2008"

Dabei müsste jede Kindsklasse, die von Auto erbt, gezwungenermaßen so einen Namen definieren, sonst würde ich das einfach mit einer static-Variablen in den Kindsklassen machen.

Versucht hab ich bis jetzt, eine abstract static Variable in Auto zu erzeugen, aber das ist in C# nicht möglich(?); weiters habe ich lang und breit bei Google gesucht, was aber auch nicht wirklich zielführend war - es wurden z.B. Attribute erwähnt, aber ich kann in C# Klassen nicht zwingen, ein bestimmtes Attribut zu definieren.

Habt ihr vielleicht Ideen dazu? Das kann doch nicht unmögilch sein...

Danke,
onkel_keks

16.835 Beiträge seit 2008
vor 11 Jahren

Im Sinne des OOD ist dies absolut nicht.
Volvo_S45 sollte keine Klasse sein, sondern ein Objekt. Das sind Grundlagen des Modell-Designs.

Auto, Motorrad, LKW, Flugzeug stellen Klassen dar, die Du dann erweitern kannst. Es bietet sich hierbei eine abstrakte Basisklasse an. Sowas kann man auf verschiedene Wege (je nach absoluter Anforderung) modellieren.
Aber nicht so, dass ein Modell eine Klasse abbildet. Unterschiede werden mit Hilfe von Typen oder Eigenschaften modelliert.

F
10.010 Beiträge seit 2004
vor 11 Jahren

Abstract Static ist auch eine blöde Idee.

Aber ein Abstract Property mit einem einzelnen Getter ist doch sehr einfach zu erstellen.
Aber ansonsten muss ich Abt zustimmen.

O
onkel_keks Themenstarter:in
20 Beiträge seit 2013
vor 11 Jahren

Danke für eure Antworten,

@FZelle:
abstract property ist aktuell auch mein workaround. Das ist mir aber nicht flexibel genug, denn manchmal muss ich auf besagten Namen zugreifen, ohne eine Objektinstanz vor mir zu haben.

@Abt:
Ich bin nicht ganz sicher, ob ich das richtig verstehe...
Auto ist eine abstrakte Klasse. Volvo_S45 ist zwar - wie ich vielleicht mißverständlich geschrieben habe - eine Ausführung von Auto. Allerdings kann es viele Volvo_S45 geben, denen allen gemein ist, dass sie eben Volvo S45 heißen.

Heißt das, es fehlt noch eine Hierarchieebene? Sodass Volvo_S45 auch eine abstrakte Klasse ist und alle Volvos, die dann herumfahren, von Volvo_S45 erben sollen? Wie ist dann abgebildet, dass jedes Auto grundsätzlich einen Namen hat?

F
10.010 Beiträge seit 2004
vor 11 Jahren

denn manchmal muss ich auf besagten Namen zugreifen, ohne eine Objektinstanz vor mir zu haben.

Dann läuft bei dir was ganz gewaltig schief.

Wozu meinst du so etwas zu brauchen?

5.941 Beiträge seit 2005
vor 11 Jahren

Hallo

Nein, im Gegenteil, weniger Klassen schreiben, sondern Merkmale in Eigenschaften unterzubringen.

Ungefähr so:
Auto.Marke = "Volvo"
Auto.Typ = "S45"

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

16.835 Beiträge seit 2008
vor 11 Jahren

Eher


var hersteller = new Hersteller
{
  Name = "Volvo"
};

var auto = new Auto
{
   Name = "S45",
   Hersteller = hersteller 
};

O
onkel_keks Themenstarter:in
20 Beiträge seit 2013
vor 11 Jahren

@Peter Bucher:
Tut mir leid, ich sehe nicht wie mir das hilft. Mein Problem ist nicht, dass ich separat auf Hersteller und Modell zugreifen will.

@FZelle:
Da muss ich etwas ausholen.
Zunächst geht es nicht um Autos sondern um Sensoren (ich habe das Beispiel aufgrund der Anschaulichkeit gewählt). Zur Kommunikation mit diesen gibt es eine abstrakte Basisklasse, die gewisse Grundfunktionalitäten abbildet, und eben für jeden konkreten Sensor davon abgeleitete Klassen, die sensor- und protokollspezifische Dinge beinhalten.

Als Teil der abstrakten Sensor-Klasse gibt es eine Methode, die in einem anderen Teil des Programms hinterlegt, ob der jeweilige Sensor im System vorhanden ist, das funktioniert etwa so:


protected void SetSensorPresent(bool present)
{
   if (this.GetType() == typeof(SensorX1))
   {
      sensorstates.present["SensorX1"] = sensor_visible;
   }
   if (this.GetType() == typeof(SensorX2))
   {
      sensorstates.present["SensorX2"] = sensor_visible;
   }
   //...etc.
}

Für diesen Teil des Programms reicht eine abstract property völlig aus, dann würde der Code so aussehen:


protected void SetSensorPresent(bool present)
{
   sensorstates.present[this.FriendlyName] = sensor_visible;
}

Wunderbar.
Allerdings gibt es auch einen Programmteil, der XML-Konfigurationsfiles parst, in denen die Sensoren per Namen referenziert sind. Dieser Teil hat keine Instanzen der Sensoren, braucht er auch nicht, denn diese Funktion soll nicht kommunizieren, sondern XML-Daten auslesen. Alles, was dort benötigt wird, ist der Name des Sensors, der derselbe Name sein muss wie im obigen Code. Im Sinne der Kapselung dachte ich, ich hinterlege eben diesen Namen mit einer abstract static Variable in der abstrakten Sensorklasse. Was uns zu diesem Thread führt.

16.835 Beiträge seit 2008
vor 11 Jahren

Naja, es gibt eben eine (abstrakte) BasisSensor-Klasse, in der die Sichtbarkeit definiert ist
SensorX1 und SensorX2 erben halt von diesem. Das ist wie gesagt OOD Grundlage.

O
onkel_keks Themenstarter:in
20 Beiträge seit 2013
vor 11 Jahren

SensorX1 und SensorX2 erben halt von diesem. Das ist wie gesagt OOD Grundlage.

Richtig, SensorX1 und SensorX2 erben beide von der abstrakten Basisklasse BasisSensor.

es gibt ebene eine (abstrakte) BasisSensor-Klasse, in der die Sichtbarkeit definiert ist

Ja, aber wie ist sie zu definieren, damit die o.g. Anforderungen erfüllt werden?

EDIT:
Ich glaube ja mittlerweile, es ist keine so gute Idee, den Sensornamen in dieser Klasse unterzubringen. Offensichtlich ist das in C# nicht so trivial umzusetzen, und dafür, dass es eher eine Kleinigkeit ist, werde ich nicht das ganze Klassenkonzept auf den Kopf stellen. Falls jemand trotzdem noch Ideen hat, bin ich natürlich immer noch interessiert daran.

F
10.010 Beiträge seit 2004
vor 11 Jahren

Du willst also eigentlich nur die Funktionalität einer Factory in die jeweiligen Klassen auslagern!?!

O
onkel_keks Themenstarter:in
20 Beiträge seit 2013
vor 11 Jahren

Nein. Ich will doch nur den einen String unterbringen. Factories waren und sind in der Lösung nicht vorgesehen weil viel zu viel Aufwand für so eine Kleinigkeit. Es geht dabei ja nur darum, dass ich nicht zweimal irgendwo einen String hinterlegen muss und dabei noch dafür Sorge tragen muss, dass diese zwei Strings immer gleich sind.

Falls es interessiert, ich habe mir jetzt mit einer Konstanten, einer static Variable in den abgeleiteten Klassen und einer abstract property in der Basisklasse geholfen - nicht unbedingt wunderschön, aber es funktioniert und ich muss den String nur 1x, zentral und gekapselt, hinterlegen.
Somit:


public abstract class BaseSensor
{
   abstract protected String friendly_name
   {
      get;
   }
   //...
}

public class SensorX1 : BaseSensor
{
   private const String SENSOR_FRIENDLY_NAME = "Sensor X1";
   protected override string friendly_name
   {
      get { return SENSOR_FRIENDLY_NAME; }
   }
   public static String Friendly_name = SENSOR_FRIENDLY_NAME;
   //...
}

Und auf magische Weise funktioniert dann sowohl das (innerhalb von BaseSensor):


Sensor X1 sens = new SensorX1();
Debug.Print(sens.friendly_name);

als auch das:


Debug.Print(SensorX1.Friendly_name);

Danke für eure Posts! (Wenn noch jemand was ergänzen mag, es würde mich weiterhin interessieren.)

2.298 Beiträge seit 2010
vor 11 Jahren

Das macht so absolut keinen Sinn und ist nicht wirklich sauber.

Wozu musst du außerhalb einer Instanz auf den Namen zugreifen können? Hat das irgendeine spezielle Bewandnis?

Klassendesigntechnisch muss ich dazu nichts sagen. Nur eine Frage stellen: Was machst du, wenn es plötzlich 2 verschiedene Ausführungen von SensorX1 gibt? Erstellst du dann eine neue Klasse, obwohl sich die beiden nur in einigen Eigenschaften unterscheiden?

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

O
onkel_keks Themenstarter:in
20 Beiträge seit 2013
vor 11 Jahren

plötzlich 2 verschiedene Ausführungen von SensorX1

Wieso ist das relevant? Es kann weiterhin jede Instanz von SensorX1 ein eigenes Set an Eigenschaften/Variablen haben, die sich selbstverständlich von Instanz zu Instanz unterscheiden können. Nur friendly_name, genauso wie SensorX1.Friendly_name, sind eben immer SENSOR_FRIENDLY_NAME. Was übersehe ich dabei?

F
10.010 Beiträge seit 2004
vor 11 Jahren

Factories waren und sind in der Lösung nicht vorgesehen weil viel zu viel Aufwand für so eine Kleinigkeit.

Dann hast Du einen falschen Eindruck davon was Factories sind und wie die aussehen können/sollten.
Die meisten Factories die ich kenne bestehen aus einer einzelnen Funktion die per übergebenen Parameter(n) ein Objekt eines Interfacetyps zurückgeben.

Man kann evtl noch eine 2. Funktion für den Reversen Weg erstellen.

Im Gegensatz dazu finde ich deinen Ansatz als zu Aufwendig und fehlerbehaftet.

O
onkel_keks Themenstarter:in
20 Beiträge seit 2013
vor 11 Jahren

OK danke, das hilft mir auf jeden Fall schon weiter - dann muss ich wohl mehr zum Thema Factories unter C# lesen.

Kannst du mir noch sagen, was an meinem Ansatz _fehler_behaftet ist? (D.h. funktional falsch?)

5.658 Beiträge seit 2006
vor 11 Jahren

Wozu braucht man eine Eigenschaft sowohl als statisches als auch als nicht-statisches Property? Mir erschließt sich der der Sinn dahinter überhaupt noch nicht, so daß ich auch keine besser Alternative vorschlagen könnte.

Christian

Weeks of programming can save you hours of planning

O
onkel_keks Themenstarter:in
20 Beiträge seit 2013
vor 11 Jahren

Das habe ich in dem Beitrag um 13:45 schon beschrieben - grundsätzlich würde mir auch nur ein statisches Feld reichen, aber abstract static gibt es eben in C# nicht. Der Sinn dahinter ist, dass ich nicht immer, wenn ich auf den Sensornamen zugreifen will, eine Instanz des Sensors habe (siehe Sensorkommunikation (Instanz vorhanden) vs. XML auslesen (keine Instanz vorhanden, aber derselbe Name wird benötigt).

5.658 Beiträge seit 2006
vor 11 Jahren

grundsätzlich würde mir auch nur ein statisches Feld reichen, aber abstract static gibt es eben in C# nicht.

Das hat nichts mit C# zu tun, sondern mit Objektorientierter Programmierung. Du willst aber statische Eigenschaften vererben, und das ist nicht mit OOP vereinbar. Ich meine das Prinzip, nicht das fehlende abstract static. Wenn du es unbedingt so durchsetzen möchtest, dann darfst du aber nicht erwarten, daß du hier dazu ermutigt wirst.

XML auslesen (keine Instanz vorhanden, aber derselbe Name wird benötigt).

Du stellst das einfach so in den Raum, als wäre es eine absolut sichere Feststellung. Aber warum hast du keine Instanz und kannst sie auch nicht haben, wenn eine XML-Datei ausgelesen wird? Und wozu brauchst du den Namen einer Instanz wenn du keine Instanz hast. Was ist der Sinn dahinter?

Christian

Weeks of programming can save you hours of planning

O
onkel_keks Themenstarter:in
20 Beiträge seit 2013
vor 11 Jahren

Für mich ist es eine sichere Feststellung, dass es im Fall des XML auslesen keine Instanz gibt. Ich gebe dir und auch allen anderen Recht, wenn gesagt wird, dass es keine schöne Lösung ist, dass der Sensor eigentlich anders repräsentiert sein sollte (und zwar so, dass eben eine Instanz da ist).
Fakt ist aber, dass ich mit dem vorhandenen Code arbeiten muss. Ich werde bestimmt kein ein neues Klassenkonzept aus dem Hut zaubern, nur weil ich einen String zentral hinterlegen möchte und ich das - wie beschrieben - auch mit einem Hack hinbekomme.

Ich möchte auch nicht zu irgend etwas "ermutigt" werden, das ist nicht notwendig. Es geht mir um den Austausch von Ideen zu diesem Problem. Wie es nunmal in einem OOP-Forum zu erwarten ist, lautet die erste Antwort (und die nächsten 20), dass das Problem gar nicht das Richtige ist, und dass die Sache von Grund auf anderes angegangen werden müsste. Das habe ich zur Kenntnis genommen und es ist mir bewusst. Es steht eine Überarbeitung des gesamten Programms bevor, wo auf solche Überlegungen eingegangen wird. Heute reicht es mir, wenn ich eine funktionierende Lösung habe.

Diese habe ich mir mittlerweile konstruiert, was mich nun interessiert ist, ob dieser Ansatz tatsächlich fehlerhaft ist (im Sinne von: so funktioniert es nicht weil...) - denn das ist er meiner Meinung nach nicht, und ich wäre sehr dankbar wenn mir jemand einen konkreten Fehler aufzeigen könnte. Ich möchte auch nicht arrogant oder undankbar erscheinen; das Feedback ist mir wichtig und ich bin dankbar dafür. Ich bitte nur um Nachsicht, wenn es mich nicht besonders interessiert, zum 20. Mal zu lesen, dass das alles totaler Schwachsinn ist oder dass das alles falsch ist, ohne zu spezifizieren, was daran (funktional) falsch ist.

EDIT:

Und wozu brauchst du den Namen einer Instanz wenn du keine Instanz hast. Was ist der Sinn dahinter?

Ich brauche ja nicht den Namen der Instanz. Ich möchte quasi einen Alias für alle von BaseSensor abgeleiteten Klassen erzeugen, wobei sichergestellt sein soll, dass jede abgeleitete Klasse einen solchen Alias definiert. Im Prinzip ist die abstract property in meiner obigen Lösung gar nicht notwendig, sie soll nur sicherstellen, dass ein Alias definiert wird.

16.835 Beiträge seit 2008
vor 11 Jahren

Du brauchst kein Lenkrad, wenn Du kein Auto hast.

Beschäftige Dich mal mit der OOD, dann hast Du heute noch selbst die Lösung. Und ja, es kommt sehr oft in der Entwicklung vor, dass man Code verwerfen und neu machen muss, weil man die Sache falsch angegangen ist. Das ist das Spiel.
Aber wir drehen uns im Kreis.

Und Dein Edit wurde hier schon mehrmals beantwortet.

3.170 Beiträge seit 2006
vor 11 Jahren

Hallo,

jedenfalls schützt Dich Dein Workaround auch nicht. Denn es wird ja nur erzwungen, dass die protected Property überschrieben wird, nicht aber, dass eine Konstante oder ein statisches Feld angelegt wird.
Wenn jemand also nur die Property überschreibt, kompiliert der Code wunderbar, aber

Debug.Print(SensorX1.Friendly_name);

wirft dann ein Exception...

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

O
onkel_keks Themenstarter:in
20 Beiträge seit 2013
vor 11 Jahren

Danke, das stimmt natürlich, aber ich sehe keinen Weg, das anders hinzubekommen - denn die Definition einer Konstanten oder eben eines static member zu erzwingen geht ja eben nicht (oder?).

F
10.010 Beiträge seit 2004
vor 11 Jahren

Nein, geht auch nicht.

Aber deshalb gibt es ja Factories, die ausserhalb der Klassenhierarchie liegen und damit sehr wohl alle Klassentypen kennen können.
Wie das dadrin gelöst ist, Switch/Case/Dictionary/Reflection (und damit auch Attribute ) o.ä. ist dabei eher nebensächlich.

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo zusammen,

also ich finde den Wunsch von onkel_keks nicht so abwegig.

Der Startbeitrag mit den konkreten Automarken vermittelte zwar den Eindruck, als hätte der Threadstarter die Objektorientierung noch nicht so genau verstanden, aber aus meiner Sicht hat sich das spätestens aufgeklärt, als das konkrete Szenario enthüllt wurde, das nach meiner Meinung ok ist. Dass dann weiterhin unterstellt bzw. so getan wurde, dass der Threadstarter keine Ahnung hätte, hat mir nicht so gefallen.

Mein Eindruck war, dass sich eher einige der Helfer mit dem Verständnis schwerer getan haben als der Threadstarter.

Und es ist auch nicht so, dass die Objektorientierung als solche die Vererbung von Klassenvariablen verhindert. In Smalltalk - was ja eine der wenn nicht sogar die objektorientierteste Sprache überhaupt ist - werden Klassenvariablen als normale Variablen in sog. Metaklassen definiert. Die Metaklassen sind selbst wieder ganz normale Klassen und daher greifen da auch alle Prinzipien der Vererbung von Variablen. Klassenvariablen zu vererben ist in Smalltalk also ganz normal.

Es ist aber in der Tat richtig, dass man in C# eine Unterklasse nicht zwingen kann, bestimmte statische Member zu implementieren oder zu überschreiben.

Hallo onkel_keks,

fehlerbehaftet oder besser fehleranfällig ist übrigens nicht das gleiche wie fehlerhaft. Dein Lösung ist nicht per se fehlerhaft. Sie ist nur fehleranfällig in dem Sinne, dass man leicht (Kopier-)Fehler einbauen kann, wenn man diese Lösung für viele Unterklassen umsetzen will.

Im Titel hast du ja selbst etwas von Metainformationen geschrieben. Nun hilft es zwar nicht, per Reflection die Member der Klasse zu ermitteln, denn du willst ja nicht den Namen der Klasse, sondern einen FriendlyName, aber es gibt ja immer Metainformationen in Form von Attributen.

Zwar kann man auch nicht erzwingen, dass eine Unterklasse mit einem bestimmten Attribut ausgezeichnet ist, aber man würde immerhin zur Laufzeit feststellen, wenn das Attribut fehlt. Das Attribut kann man sowohl aus einer Instanz-Property als auch aus einer statischen Property heraus abfragen.

Allerdings stellt sich bei einer statischen Property das Problem, dass man den Typ der (Unter-)Klasse kennen muss und diesen nicht einfach per this.GetType () ermitteln kann. Insofern reicht es leider auch hier nicht, die statische Property einmal in der Oberklasse zu definieren.

Wenn man sich eine Methode schreibt, die den Typ der Klasse bekommt und den FriendlyName aus dem Attribut der entsprechenden Klasse ausliest, dann reicht es die Instanz-Property einmal in der Oberklasse als return GetFriendlyName (this.GetType ()); zu implementieren. Die statische Property würde man als return GetFriendlyName (typeof (Oberklasse)); implementieren. Diese Implementierung müsste man dann (leider) in jede Unterklasse kopieren und dabei "Oberklasse" durch "Unterklasse" ersetzen.

Oder man schreibt sich wie in Smalltalk eine Metaklasse. Dann würde aus der statischen Property eine Instanz-Property der Metaklasse, so dass man wieder mit einer Implementierung in der Ober-(Meta-)Klasse auskommen.

herbivore