Laden...

Übergabe von Listen-Objekt-Eigenschaften per Name an Methode

Erstellt von NOFX vor 5 Jahren Letzter Beitrag vor 5 Jahren 1.174 Views
N
NOFX Themenstarter:in
42 Beiträge seit 2015
vor 5 Jahren
Übergabe von Listen-Objekt-Eigenschaften per Name an Methode

Hi, wahrscheinlich tue ich mich gerade einfach viel zu schwer für dieses einfache Problem:

Ich möchte Daten aus Listen verarbeiten, die in verschiedenen Listeneinträgen abgelegt sind. Die bisherige Lösung ist aktuell, dass ich per LinQ und Select zuerst einmal die einzelnen Eigenschaften in eine Liste packe und die an meine Methode übergebe. Also so in etwa:


class Class A
{
    public string Name { get; set;}
    ...
}


List<ClassA> listA = new List<ClassA>() { new ClassA() {....},...};

List<string> names = listA.Select(ca => ca.Name);
var tmp = DoSomething(names);

Mein Wunsch wäre jetzt das Ganze universeller und ohne die temporären Listen machen zu können also in etwa so:


List<ClassA> listA = new List<ClassA>() { new ClassA() {....},...};

var tmp = DoSomething( listA, "Name");

Wenn ich Reflections nutzen wollen würde wäre das machbar und man findet einige Lösungen dazu aber das muss doch auch eleganter und performanter machbar sein.

2.080 Beiträge seit 2012
vor 5 Jahren
List<string> names = listA.Select(ca => ca.Name);

Das wird so nicht funktionieren, die Select-Methode gibt ein IEnumerable zurück 😛
Da wird also auch nichts kopiert oder eine neue Liste erstellt. Hast Du hier ein ToList vergessen? Das brauchst Du nicht, wenn Du direkt IEnumerable verwendest.

Warum willst Du das denn in die Methode rein ziehen? Sind vor dem Select noch andere LINQ-Aufrufe (z.B. Where) nötig, die sich immer wiederholen?
Wenn nicht, würde ich das so lassen, Du baust dir sonst auf lange Sicht nur eine hässlichere Variante von LINQ nach.

Aber wenn Du das aber unbedingt haben willst:

var tmp = DoSomething(listA, a => a.Name);

object DoSomething<TSource, TValue>(IEnumerable<TSource> source, Func<TSource, TValue> getValue)
{
    var data = source.Select(getValue);

    // ...
}

Mir wäre es so lieber, damit bin ich flexibler:

var tmp = DoSomething(listA.Select(a => a.Name));

object DoSomething<T>(IEnumerable<T> source)
{
    // ...
}

PS:
Der Code ist direkt im Browser getippt, verzeiht eventuelle Fehler.

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.

T
2.224 Beiträge seit 2008
vor 5 Jahren

Meine erste Frage wäre, was ist der Zweck des Ganzen?
Willst du dir Tipparbeit ersparen oder soll hier ein spezifischer Zweck abgedeckt werden?
Mit Reflections sollte man arbeiten, wenn man weiß was man macht.

Mir ist auch nicht klar, in wie weit der gewünschter Ansatz den Code lesbar machen soll.
Damit würdest du die Funktion deines Codes mehr verschleiern als es nützt.
Deine Daten in einer Klasse zu kapseln ist absolut im Sinne der OOP.
Dies zu verwerfen würde die Lesbarkeitverschlechtern, da niemand außer dir verstehen kann warum dort der Code umgesetzt ist wie er aktuell ist.
Wenn du den Code in 1-2 Jahren oder gar Monaten mal wieder verstehen willst, machst du es dir damit extra schwer.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

N
NOFX Themenstarter:in
42 Beiträge seit 2015
vor 5 Jahren
List<string> names = listA.Select(ca => ca.Name);  

Das wird so nicht funktionieren, die Select-Methode gibt ein IEnumerable zurück 😛
Da wird also auch nichts kopiert oder eine neue Liste erstellt. Hast Du hier ein ToList vergessen? Das brauchst Du nicht, wenn Du direkt IEnumerable verwendest.

Ja, das habe ich vergessen. Aber direkt IEnumerable zu nutzen macht Sinn.

Warum willst Du das denn in die Methode rein ziehen? Sind vor dem Select noch andere LINQ-Aufrufe (z.B. Where) nötig, die sich immer wiederholen?
Wenn nicht, würde ich das so lassen, Du baust dir sonst auf lange Sicht nur eine hässlichere Variante von LINQ nach.

Aber wenn Du das aber unbedingt haben willst:

var tmp = DoSomething(listA, a => a.Name);  
  
object DoSomething<TSource, TValue>(IEnumerable<TSource> source, Func<TSource, TValue> getValue)  
{  
    var data = source.Select(getValue);  
  
    // ...  
}  

Danke! Genau sowas habe ich gesucht, Func zu nutzen statt über die PorpertyInfo zu gehen.

Mir wäre es so lieber, damit bin ich flexibler:

var tmp = DoSomething(listA.Select(a => a.Name));  
  
object DoSomething<T>(IEnumerable<T> source)  
{  
    // ...  
}  

Meine erste Frage wäre, was ist der Zweck des Ganzen?
Willst du dir Tipparbeit ersparen oder soll hier ein spezifischer Zweck abgedeckt werden?
Mit Reflections sollte man arbeiten, wenn man weiß was man macht.

Mir ist auch nicht klar, in wie weit der gewünschter Ansatz den Code lesbar machen soll.
Damit würdest du die Funktion deines Codes mehr verschleiern als es nützt.
Deine Daten in einer Klasse zu kapseln ist absolut im Sinne der OOP.
Dies zu verwerfen würde die Lesbarkeitverschlechtern, da niemand außer dir verstehen kann warum dort der Code umgesetzt ist wie er aktuell ist.
Wenn du den Code in 1-2 Jahren oder gar Monaten mal wieder verstehen willst, machst du es dir damit extra schwer.

Es sind viele Properties (ca. 50) die jeweils in zwei Listen des gleichen Typs vorhanden sind und auf die die Methode angewandt wird.

Real sieht das dann eher so aus:


List<ClassA> valuesForward0 = listAForward.Select(ca => ca.Property0).ToList();
List<ClassA> valuesBackward0 = listABackward.Select(ca => ca.Property0).ToList();

result0 = DoSomething(valuesForward0, valuesBackward0, defaultValue0);

Das 5 zig mal ist nicht unbedingt übersichtlich und jedesmal die temporären Listen zu erstellen ist imho auch nicht sehr übersichtlich. Da ist die Variante doch deutlich angenehmer:


result0 = DoSomething(listAForward, listABackward, ca => ca.Property0, defaultValue0);

2.080 Beiträge seit 2012
vor 5 Jahren

Nun, da - und da kann man sicher hervorragend drüber streiten - würde ich doch Reflection in Betracht ziehen, zumindest wenn es immer alle Properties der Klasse sind.

Dann würde ich aber eine zweite Methode schreiben, die alle Properties der Klasse durch läuft und einzeln die erste Methode aufruft. Je nachdem, worum es da geht, hat das den Vorteil, dass Du keine Properties vergessen kannst.

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.

3.003 Beiträge seit 2006
vor 5 Jahren

Reflection:

  • Vorteil: neue und entfernte Properties schlagen direkt auf das Ergebnis durch
  • Nachteil: wenn einzelne Properties ausgelassen werden oder sonstige Anpassungen erfolgen sollen, muss der Reflection-Helper angepasst werden. Viele von uns haben so eine Klasse schonmal nach ein paar Jahren gesehn, das blickt niemand mehr.

Einzelaufruf pro Property:

  • Vorteil: scheller, sicherer Zugriff auf die Properties, die man braucht. Welche Properties man braucht, sieht man direkt im Quellcode.
  • Nachteil: führt man neue Properties ein, wird nicht direkt DoSomething für sie aufgerufen. Zusätzlicher Aufwand durch zusätzliche Zeile Code + zusätzlichen Testcase für den Unittest. Werden Properties entfernt, baut er nicht mehr (ob das ein Nachteil ist, lass ich mal dahingestellt).

Kurz gesagt: entscheide dich zwischen Compiletime- oder Runtime-Fehlern. Ich müsste da nicht allzu lange nachdenken.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

N
NOFX Themenstarter:in
42 Beiträge seit 2015
vor 5 Jahren

Es werden nicht immer alle Properties der Klasse genutzt und teilweise die gleiche Methode später noch mal aufgerufen, daher wird das per Reflections wohl nicht so schön.

Ein weitere Vorteil ist für mich speziell, da es sich fast durchgängig um double-Werte handelt, dass eine teilweise notwendige Skalierung direkt mit im Aufruf ersichtlich wird (so wie es aktuell beim erzeugen der temporären Listen auch ist). Via Reflections müsste diese dann ja zusätzlich übergeben werden.

Auf jeden Fall allen schon mal einen herzlichen Dank für die schnellen Antworten und konstruktive Diskussiopn! 👍