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
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?
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
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.
Hallo,
das Stichwort dafür ist Reflection, s.a. C# Reflection Examples (bei dir also mittels PropertyInfo
).
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
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).
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
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.
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
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 🙂
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
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
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.
NuGet Packages im Code auslesen
lock Alternative für async/await
Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.
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!
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code