Laden...

Zugriff auf Klasse mittels String-Variable

Erstellt von Stoffel000 vor 2 Jahren Letzter Beitrag vor 2 Jahren 696 Views
S
Stoffel000 Themenstarter:in
11 Beiträge seit 2021
vor 2 Jahren
Zugriff auf Klasse mittels String-Variable

Hallo liebes Forum,
in diesem Forum habe ich schon oft Hilfe gefunden. Doch leider diesmal nicht. Ich bin ziemlich neu in der Programmierung und habe momentan ein Problem an dem ich mir die Zähne ausbeiße.


List<Tuple<string, string>> list = new List<Tuple<string,string>>();
TextBox.Text = MeineKlasse.[hier sollte der 2. wert der liste stehen];

Die Klasse selbst und auch die List ist kein Problem. Das funktioniert soweit. Nun meine Frage: Ist es möglich aus dem String der List auf ein Objekt der Klasse zuzugreifen?

Dies ist ein vereinfachtes Beispiel. Die Klasse selbst hat über 400 Objekte und die List hat 6 Strings. Ein direkter Zugriff wäre zwar möglich aber ein sehr hoher Programmieraufwand.

Ich hoffe ich habe nichts vergessen. Falls doch, bitte einfach nachfragen.

Danke im Voraus.

LG Stoffel000

309 Beiträge seit 2020
vor 2 Jahren

Die Klasse selbst und auch die List ist kein Problem. Das funktioniert soweit. Nun meine Frage: Ist es möglich aus dem String der List auf ein Objekt der Klasse zuzugreifen?

Das hört sich verdächtig nach falscher Planung an, zeig doch mal deine Klasse.
Du willst in Abhängigkeit eines Strings der Liste auf ein bestimmtes Feld der Klasse zugreifen?

C
55 Beiträge seit 2020
vor 2 Jahren

Hallo,

Hast du dir mal die Klasse Dictionary<Tkey, TValue> angeschaut, damit dürftest du eher ans Ziel zu kommen, weil du dann dein Tuple nicht mehr in einer Liste wrappen müsstest.

Hier die Doku zu Dictionary:
Dictionary<TKey,TValue> Klasse (System.Collections.Generic)

Grüße

S
Stoffel000 Themenstarter:in
11 Beiträge seit 2021
vor 2 Jahren

Hallo JimStark,

hier ein kleiner Ausschnitt meiner Klasse:


 public class ClassDatenblatt_DC
    {
        public int Id { get; set; }
        public string Zeitstempel { get; set; }
        public string Ersteller { get; set; }
        public Nullable<int> Status { get; set; }
        public string Datum { get; set; }
        public string Artnr { get; set; }
        public string Berechnungsnummer { get; set; }
        public Nullable<int> Version { get; set; }
        public Nullable<int> Aenderung { get; set; }
        public string Stromrichter { get; set; }
....
}

und hier ein Ausschnitt der List:


 wandlerlistDc = new List<Tuple<string, string, string, string, string, string>>
            {
                new Tuple<string, string, string, string, string, string>("abnummer", "", "", "", "", ""),
                new Tuple<string, string, string, string, string, string>("pos", "", "", "", "", ""),
                new Tuple<string, string, string, string, string, string>("status", "status", "", "", "", ""),
                new Tuple<string, string, string, string, string, string>("erst_dat", "datum", "", "", "", ""),
                new Tuple<string, string, string, string, string, string>("a1", "berechnungsnummer", "", "", "", ""),
                new Tuple<string, string, string, string, string, string>("v1", "version", "", "", "", ""),
....
}

Wenn aus meiner List der 5. Eintrag an der Reihe ist, soll a1 als Label angezeigt werden und die Berechnungsnummer als Wert der Klasse geladen werden (würde dem 7. Eintrag der Klasse entsprechen.)
Der Zugriff auf den Wert der Klasse funktioniert mit:


internal Klassenbibliothek.ClassDatenblatt_DC neuerDatensatzBearbeiten = new Klassenbibliothek.ClassDatenblatt_DC(); //erstellen einer Ableitung der Klasse
TextBox.Text = neuerDatensatzBearbeiten.berechnungsnummer;

Da ich aber über den String der List zugreifen möchte (um mir sehr viel Arbeit zu ersparen) müsste das in etwa so aussehen:


internal Klassenbibliothek.ClassDatenblatt_DC neuerDatensatzBearbeiten = new Klassenbibliothek.ClassDatenblatt_DC(); //erstellen einer Ableitung der Klasse
TextBox.Text = neuerDatensatzBearbeiten.[StringDerListeFürDenZugriff];

Wie gesagt ich bin nicht wirklich fit in der Programmierung. Sollte ich einen komplett falschen Ansatz gewählt haben, dies bitte auch sagen und evtl. einen Vorschlag machen wie ich es besser machen könnte.

Danke.

S
Stoffel000 Themenstarter:in
11 Beiträge seit 2021
vor 2 Jahren

Hallo Th69,

ich habe mir soeben den verlinkten Beitrag angesehen. Leider verstehe ich das nicht ganz. Kannst du mir ein Beispiel geben?

Danke

Grüße Stofffel000

4.931 Beiträge seit 2008
vor 2 Jahren

Da wirst du dich selber ein bißchen einlesen müssen.
Die Hauptmethode für dich ist Type.GetProperty sowie PropertyInfo.GetValue (dort gibt es auch jeweils Beispiele).

PS: Die Strings für die Eigenschaftsnamen müssen aber exakt übereinstimmen (d.h. auch Groß-/Kleinschreibung).

S
Stoffel000 Themenstarter:in
11 Beiträge seit 2021
vor 2 Jahren

Danke für die Hilfe. Da werde ich dann mal lesen und googlen um das zu verstehen. Die Eigenschaftsnamen setzte ich mit ToLower() alle auf Kleinbuchstaben. Somit sind sie dann gleich.

Danke, danke, danke. Hast mir sehr geholfen.

Grüße

4.931 Beiträge seit 2008
vor 2 Jahren

Wenn die Eigenschaft Status heißt, so mußt du auch "Status" bei GetProperty angeben (und nicht "status")!
Eigenschaftsnamen in C# sollten laut C# Coding Conventions auch genau so benannt sein (d.h. mit einem großgeschriebenen Anfangsbuchstaben), dies solltest du also nicht ändern.

309 Beiträge seit 2020
vor 2 Jahren

Würde es gar nicht so kompiliziert machen:


enum Field
{
 abnummer,
 pos,
...
}

wandlerlistDc = new List<Tuple<Field, string, string, string, string, string>>
            {
                new Tuple<string, string, string, string, string, string>(Field.abnummer, "", "", "", "", ""),
...
....



und in der Klasse:


public string GetOrderInfo(Field field)
{
   switch(field)
      {
      case field.abummer:
        return this.Artnr;
...

Dann könntest du es da auch richtig formatieren, ist vielleicht mehr Schreibarbeit, fände ich aber einfacher

16.807 Beiträge seit 2008
vor 2 Jahren

Für sowas erstellt man eigentlich Metadata-Informationen/Klassen, denn der bisherige Ansatz und über Reflection skaliert nicht.
Die Änderung an einem String, an der Klasse und Co würde dazu führen, dass es knallt, weil Du es auch nicht validieren kannst.
Report-Komponenten machen das quasi in der Designer-Time. Neuere Reports gehen den Weg von Source Code Generators, der hier wahrscheinlich auch verwendet werden kann.

Der Aufbau (von der Klasse und auch den Tuples) ist zudem ein Zeichen bzw ein Hinweis, dass was an der Struktur / Architektur womöglich grundlegend nicht stimmen könnte.
Was Du mit "die Klasse hat über 400 Objekte" erschließt sich mir inbesondere aufgrund von "Objekt" nicht. Riecht aber auf alle Fälle auch eher fischig.

Reflection ist ein vergleichsweise komplexes Thema, das Du sicherlich nicht in den 10 Minuten zwischen Antwort und Post durchgelesen oder gar verstanden haben kannst.
Das wird Dich eher paar Tage kosten; lohnt sich aber generell sowieso 🙂

C
9 Beiträge seit 2012
vor 2 Jahren

Mit Reflection könntest Du es so lösen (ist ungetestet, evtl noch Fehler...)
Würde mit Groß/Kleinschreibungsvariationen umgehen können.


public static class CallByNameExtensions {
    public static object GetValue(this object instance, string propertyName) {
        try {
            return instance.GetType().InvokeMember(propertyName, BindingFlags.IgnoreCase|BindingFlags.GetProperty|BindingFlags.Instance|BindingFlags.Public, null, instance, null);
        } catch {
            return null;
        }
    }
}

Aufruf:


var blatt = new ClassDatenblatt_DC();
blatt.Id = "42";
blatt.BerechnungsNummer = "0815/4711";

var id = blatt.GetValue("id");  //id enthält 42
var bNummer = blatt.GetValue("berechnungsnummer"); //bNummer enthält 0815/4711

2.078 Beiträge seit 2012
vor 2 Jahren

Bei einem Catch, das einfach nur irgendeinen Default zurückgibt, bekomme ich Bauchschmerzen.

Gerade beim Thema Reflection sind die Namen fest definiert, da ist nichts dynamisch. (Naja, außer man lädt/generiert zur Laufzeit, aber das ist hier nicht der Fall)
Ist ein Name falsch, dann ist er nun mal falsch und definitiv ein Fehler zur Entwicklungszeit. So ein Fehler sollte nicht geschluckt werden!
Ich persönlich arbeite daher möglichst mit nameof (Docs), dann passt der Compiler für mich auf.

Viel eher könnte man eine "TryGetValue"-Methode daraus machen und dann entsprechend reagieren, wenn der Wert nicht gefunden wird. Dann aber bitte prüfen, ob's die Property gibt und nicht mit Exceptions für den Kontrollfluss arbeiten.
Oder man lässt die Exception fliegen (kapselt sie ggf. in einer Exception mit sprechendem Namen und mehr Infos) und erfährt dann auch auf Anhieb, was das Problem war.
Konkrete Exceptions an der konkreten Position sind sehr viel leichter zu behandeln, als ein Fehler wie "irgendwo ist irgendwann ein null entstanden".

Außerdem habe ich die Erfahrung gemacht, dass solche allgemeinen ExtensionMethods früher oder später zu Chaos führen.
Bedenke, dass sie für alles gelten (object) und Du sie daher auch überall aufrufen kannst, was einerseits nervig werden und andererseits zu ungünstigen Abhängigkeiten führen kann.
Abgesehen davon ist das Ding auch nichts anderes als eine ganz normale statische Methode mit ein bisschen Compiler-Zucker und den gleichen Nachteilen, die jede statische Methode hat. Bei diesem Beispiel ist das kein Problem, sobald aber etwas spezifischere Logik dazu kommt, wird es zu einem Problem, das Du nicht mehr so einfach weg bekommst.

Das heißt, wenn es auf eine solche Reflection-Lösung hinauslaufen sollte:
Einfach eine normale private Methode in die Klasse, in der sie gebraucht wird.
Und ein sinnvolles Fehlerhandling indem z.B. geprüft wird, ob es die Property auch wirklich gibt und ein Setter da ist.

16.807 Beiträge seit 2008
vor 2 Jahren

Ich kann da Palladin007 nur beipflichten: so ein Vorgehen hat mehrere Design Fehler bzw. Design Risiken.
Reflection an für sich ist schon ein gewisser Overhead und Risiko; das muss man nicht künstlich durch stringifcation und objectification noch verschlimmern.

Der Grund warum es überhaupt Source Code Generators gibt sind eben die Risiken von Reflections (und noch 2-3..72 andere Dinge wie Performance).
Nehmt also den Weg des .NET Ökosystems an und verzichtet auf Reflections, wo es nur geht - und nutzt moderne Mittel.

Dann aber bitte prüfen, ob's die Property gibt und nicht mit Exceptions für den Kontrollfluss arbeiten.

Der eigentliche Grund dieser Antwort: das ist ein enorm richtiger und wichtiger Satz, den man unbedingt betonen muss!