Laden...

Variablenabhängige Verknüpfung

Erstellt von cprogrammer vor 9 Monaten Letzter Beitrag vor 8 Monaten 869 Views
C
cprogrammer Themenstarter:in
73 Beiträge seit 2023
vor 9 Monaten
Variablenabhängige Verknüpfung

Hallo, folgende Problem:

Ich habe eine Anzahl x von string Variablen (könnte z.B. eine Liste sein), z.B.

string a,b,c,d, usw.

Nun sollen diese variablen abhängig von ihrem Inhalt miteinander weiterverarbeitet werden, d.h. die Anzahl der zu verarbeitenden Variablen variiert je nach deren Ihnalt.

Beispiel:

wenn z.B. b = "None" enthält, dann soll nur mit a,c,d weitergearbeitet werden.

wenn z.B. keine Variable "None" enthält, dann soll mit a,b,c,d weitergearbeitet werden.

wenn z.B. a und c "None" enthalten, dann soll nur mit b,d weitergearbeitet werden.

usw.

Ich möchte eine Verschachtelung von if-statements vermeiden, weil sich die Anzahl der variablen ändern kann und dann

immer alles wieder angepasst werden muss, theoretisch können es 20 bis 30 variablen werden.

Gibt es irgendeine schlaue Möglichkeit, dies flexibel bzgl. der Anzahl der Variablen zu realisieren ?

Weiteres Beispiel:

a && b && c && d

wenn b wegfällt, dann soll es heißen: a && c && d

wenn a und b wegfällt, dann soll es heißen: c && d

die Abfrage passt sich also dynamisch und in Abhängigkeit der zu verarbeitenden Variablen an.

2.071 Beiträge seit 2012
vor 9 Monaten

Erstelle eine Klasse, die einen Wert beschreibt und Funktionen anbietet, die das Verhalten beschreiben.
Wie genau das aussiehen soll, weißt nur Du.

Wenn die Werte sich deutlich unterscheiden, erstelle so viele Klassen, wie Du brauchst und ein Interface mit den Funktionen, das Du dann in jeder Klasse implementierst.

Danach eine List<IMyInterface>, jeder Eintrag ist eine Instanz der jeweiligen Klasse und die kannst Du dann abarbeiten.

T
50 Beiträge seit 2010
vor 9 Monaten

An sich lässt sich das ganz einfach für Strings lösen. Man baut sich eine Liste mit den Strings auf und filtert die nicht benötigten Elemente mit der Where-Methode aus System.Linq aus. Die verbleibenden Elemente können dann verarbeitet werden.

var a = "Wert 1";
var b = "Wert 2";
var list = new List<string> { a, b }; 
var filtered = list.Where(x => x != "Wert 1");	// ggf. mit string.Equals arbeiten, wenn Groß-/Kleinschreibung ignoriert werden soll

foreach (var value in filtered)
{
    // Verarbeiten
}

Wenn komplexere Strukturen entstehen, macht es Sinn, diese in Klassen bzw. in Interfaces auszulagern. Hierbei werden dann die Klassen/Interfaces in der Liste gehalten und können über die Eigenschaften gefiltert werden. Funktioniert also analog.

C
cprogrammer Themenstarter:in
73 Beiträge seit 2023
vor 9 Monaten

Danke erstmal für die Hinweise.

Also wenn es lediglich um das sortieren von strings ginge, dann wäre das mit den bisherigen Hinweisen schon gelöst.

Mein Problem ist allerdings, dass ich letztlich boolsche Verknüpfungen durchfühgren muss, und zwar nur immer mit denjenigen boolschen variablen, die nicht ausselektiert wurden.

Ich denke aber es wird wohl nicht möglich sein aus z.B. einem String wie " a && c && e" zur Laufzeit eine boolsche verknüfungsoperation

zu generieren, nach dem Motto:

if (a && c && e)

{

}

Bei anderen Auswahl könnte es heißen:

if (b && f)

{

}

usw.

Wie mir ne Klasse dabei helfen soll ist offen gestanden noch unklar.

2.071 Beiträge seit 2012
vor 9 Monaten

(Fast) alles ist möglich 😉
Nur können wir ohne gute Erklärung nicht wissen, was dein Ziel ist.

So wie ich es verstanden habe:

var result = true;

foreach (var s in GetMyString())
{
    if (IgnoreString(s))
        continue;
        
    result = result && GetBoolFromString(s);
}

Console.WriteLine(result);

Wie mir ne Klasse dabei helfen soll ist offen gestanden noch unklar.

Wenn dein Vorhaben so ist, wie gerade beschrieben, brauchst Du nicht.

C
cprogrammer Themenstarter:in
73 Beiträge seit 2023
vor 9 Monaten

Du hast Recht, wahrscheinlich war es ein Fehler, Teile meines Lösungsansatzes zu beschreiben, anstelle der Aufgabenstellung selbst.

Vielleicht liege ich ja total falsch mit meinen strings (die habe ich gewählt, weil ich damit eine leichte Testausgabe erstellen kann).

Aufgabenstellung:

Der Anwender möchte bestimmte Kriterien überpfüft haben, bis zu 30 Stück.

Dazu bekommt er einen Auswahldialog mit combo-boxen wie folgt:

Kriterium1 (oben, unten, ignore)

Kriterium2 (links, rechts, ignore)

Kriterium3 (hoch, tief, ignore)

...

Kriterium30 (x, y, Ignore)

Die Kriterien zu prüfen ist nicht das Problem, das erledigen Memberfunktionen.

Ignore bedeutet: Nicht prüfen und (!!!) später nicht berücksichtigen.

Aber:

Am Ende soll geprüft werden, ob alle diejenigen Kriterien erfüllt sind, bei denen der Anwendet NICHT "Ignore" ausgewählt hat, das meine

ich mit bedingter Verknüpfung. Daher kann ich nicht einfach hergehen und am Ende z.B. mittels && - Verknüpfung prüfen, ob alle Kriterien erfüllt sind, das wäre dann nur in dem Falle richtig, wenn der Anwender bei keinem Kriterium "Ignore" angegeben hat.

Hoffe das ist verständlicher ...

2.071 Beiträge seit 2012
vor 9 Monaten

Ja, das klingt schon mehr nach meinem ursprünglichen Vorschlag.

Ich würde es so machen:

var myData = GetMyData();
var myCriteria = new List<ICriteria>();

myCriteria.Add(new Criteria1(1, 2)); // Fill with data from the ui
// Criterion 2 was ignored - do not add

var isValid = ValidateData(myData, myCriteria);

bool ValidateData(IMyData data, IEnumerable<ICriteria> criterias)
{
    foreach (var criteria in criterias)
    {
        if (!criteria.Validate(data))
            return false;
    }

    return true;
}

class Criteria1 : ICriteria
{
    public int Up { get; }
    public int Down { get; }

    public Criteria1(int up, int down)
    {
        Up = up;
        Down = down;
    }

    public bool Validate(IMyData data)
    {
        // validate data
    }
}

class Criteria2 : ICriteria
{
    public int Left { get; }
    public int Right { get; }

    public Criteria2(int left, int right)
    {
        Left = left;
        Right = right;
    }

    public bool Validate(IMyData data)
    {
        // validate data
    }
}

interface ICriteria
{
    bool Validate(IMyData data);
}

Die Liste kannst Du frei nach Laune aus der UI füllen und auch komplexere Prüfungen gehen nicht in einem ewigen if-else-Spaghetticode unter, sondern sind einzeln und übersichtlich in Klassen organisiert.

124 Beiträge seit 2023
vor 9 Monaten

Das riecht ein wenig nach Specification Pattern

Hat die Blume einen Knick, war der Schmetterling zu dick.

C
cprogrammer Themenstarter:in
73 Beiträge seit 2023
vor 9 Monaten

Hallo, ganz herzlichen Dank an Alle, aber besonders an Palladin007, deine Lösung liefert das gewünschte Ergebnis !

C
cprogrammer Themenstarter:in
73 Beiträge seit 2023
vor 9 Monaten

Es wird leider noch etwas komplizierter:

Die Lösung oben funktioniert nun für eine erste zu prüfenden Komponente (ich nenne es mal Komponente).

Die Kriterien werden mittels eines GUI über getters/setters vorgegeeben für diese erste Komponente und dann erfolgt die Prüfung wie oben angegeben für diese erste Komponente.

Nun soll es jedoch noch eine zweite , ggf. dritte etc. Komponenten geben, für welche jeweils unterschiedliche Prüfkriterien vorgebbar sein sollen, d.h. die getters/setters oben bleiben diesselben, werden nur ggf. unterschiedlich konfiguriert/eingestellt pro Komponente.

Dann erfolgt z.B. folgende Prüfung:

Krieterien1 für Komponente1 setzen → Prüfung: erfüllt Komponente1 diese Kriterien ja/nein

Krieterien2 für Komponente2 setzen → Prüfung: erfüllt Komponente2 diese Kriterien ja/nein

usw.

Zuletzt: Prüfung ob alle Komponenten die Kriterien erfüllen ja/nein.

Man müsste also jetzt:

  1. die getters/setters so gestelten, dass diese nur 1x codiert werden, aber für unterschiedliche Komponenten eine unterschiedliche Konfiguration ermöglichen.
  2. den vorhandenen Code so umbauen, dass er mehrere Komponenten unterstützt bzw. abbprüfbar macht.
  3. eine finale Prüfung aller Komponenten realisieren

Macht es hier Sinn weiter mit Klassen und Listen von Klassen zu arbeiten und kann man die getters/setters einer Klasse überhaupt als Klasse realisieren ?

124 Beiträge seit 2023
vor 9 Monaten

Es ist etwas konfus was du da schreibst ... du könntest ruhig etwas konkreter werden, dann könnte man auch konkreter Hilfestellung geben.

Worauf du dich mit getters/setters beziehst erschließt sich mir nicht (im Code von Palladin sehe ich nur Getter).

Hast du dir das Spezification Pattern mal angesehen? Das ist eigentlich für so etwas der richtige Ansatz.

Hat die Blume einen Knick, war der Schmetterling zu dick.

2.071 Beiträge seit 2012
vor 9 Monaten

Mir ist auch nicht klar, was Du uns sagen willst.

Eigentlich sollte so jede Form von Prüfung möglich sein.

Zur Not copy&pastest Du halt ein paar Klassen und passt sie nach abweichenden Anforderungen an, oder Du arbeitest mit Vererbung.

C
cprogrammer Themenstarter:in
73 Beiträge seit 2023
vor 9 Monaten

Mit getters/setters meine ich die Properties, die ich anderweitig schon erwähnt habe, also z.B.

[Display(GroupName = "xxx", Name = "xxx")]
public Datentyp Candlenumber
{
  get => _selection;
  set
  {
    _selection = value;
  }

}

Davon gibt es bis zu 30 Stück, die sich über das GUI zu prüfende Einstellungen für die Komponente holen.

Wenn ich nun mehrere Komponenten habe, die gemäß der 30 Properties unterschiedlichen Prüfungen unterzogen werden sollen, dann müsste ich ja für jede Komponente eigene Properties erstellen, d.h. bei 2 Komponenten werden es dann 2 x 30 properties.

Das würde ich gerne vermeiden und daher die Frage, ob man denselben code mehrfach nutzen kann.

Die VAriable _selection wird im Code oben wie folgt verwendet:

myCriteria.Add(new Criteria1(1, _selection)); // Fill with data from the ui for Komponente 1

Für Komponente 2 würde das dann so aussenen:

myCriteria.Add(new Criteria1(1, _selection_2)); // Fill with data from the ui for Komponente 2

Wobei _selection_2 dasselbe Kriterium abprüft wie _selection und ich müsste dafür dasselbe Property nochmal codieren:

[Display(GroupName = "xxx", Name = "xxx")]
public Datentyp Candlenumber2
{
  get => _selection_2;
  set
  {
    _selection_2 = value;
  }

}
2.071 Beiträge seit 2012
vor 9 Monaten

Vererbung.

Oder Du erstellst die Criterien unabhängig von deinen UI-Properties.
Der User stellt also XY ein und Du erstellst anhand der Einstellungen deine Kriterien-Objekte.

C
cprogrammer Themenstarter:in
73 Beiträge seit 2023
vor 9 Monaten

Der User sieht es doppelt, weil er das muss, er sieht:

Komponente1:

Kriterium 1

Kriterium 2

...

Komponente2:

Kriterium 1

Kriterium 2

...

Der Code hinter Kriterium 1,2 etc. ist aber derselbe, nämlich die oben erwähnten Properties.

2.071 Beiträge seit 2012
vor 9 Monaten

Wenn "Komponente1" und "Komponente2" die Gruppen sind, wie Du sie im DisplayAttribute angibst, dann geht es nicht.
Jede Property kann nur ein DisplayAttribute haben, also auch nur eine Gruppe, was bedeutet, dass Du für jede Gruppe die Properties copy&pasten musst.

Eine Möglichkeit gäbe es aber vielleicht noch:

Was Du da hast, klingt nach einem PropertyGrid.
Das kann ggf. auch mit einem CustomTypeDescriptor arbeiten:
https://itecnote.com/tecnote/c-propertygrid-with-custom-propertydescriptor/

Wie genau das dann umgesetzt wird, müsste ich mich aber auch erst wieder einarbeiten - ist ewig her, dass ich das mal genutzt habe.

C
cprogrammer Themenstarter:in
73 Beiträge seit 2023
vor 9 Monaten

So ist es (daher hatte ich überlegt, ob man nicht über ein solches Property im UI der 3rd party app einen Aufruf eines Forms in meiner dll ermöglichen könnte, dann hätte man vielleicht andere Möglichkeiten):

[Display(GroupName = "Komponente1", Name = "xxx")]
public Datentyp Candlenumber
{
  get => _selection;
  set
  {
    _selection = value;
  }

}

... weitere

[Display(GroupName = "Komponente2", Name = "xxx")]
public Datentyp Candlenumber
{
  get => _selection_2;
  set
  {
    _selection_2 = value;
  }

}

... weitere

2.071 Beiträge seit 2012
vor 9 Monaten

Du kannst jederzeit eine eigene Form bzw. ein eigenes Window anzeigen.
Aber das musst Du selber machen, einen Button in die bestehende View integrieren ist nicht so einfach.

C
cprogrammer Themenstarter:in
73 Beiträge seit 2023
vor 8 Monaten
Nachtrag

Hallo,

ist es denn möglich, um doppelte Codierung pro Komponente zu vermeiden die DisplayAttribute zwar pro Komponente im Code einzufügen, jedoch nur ein property oder eine memberfunktion zu schreiben, welcher man die Komponente übergibt und welche dann alle Komponenten bedient, also nach dem Motto:

[Display(GroupName = "Komponente1", Name = "xxx")]

public Datentyp Candlenumber(Komponente1);

[Display(GroupName = "Komponente2", Name = "xxx")]

public Datentyp Candlenumber(Komponente2);

........

public Datentyp Candlenumber (Komponente)
{
 
  if(Komponente1)
  {
      get => _selection1;
      set
      {
        _selection1 = value;
      }
  }
  
  if(Komponente2)
  {
      get => _selection2;
      set
      {
        _selection2 = value;
      }
  }
  
  
}

Funktioniert das so, oder gäbe es vielleicht eine noch pfiffigere Lösung mittels einer Klasse ?

Ziele wäre es, an möglichst wenigen Codestellen änderungen durchführen zu müssen, falls es solche gäbe.

2.071 Beiträge seit 2012
vor 8 Monaten

Nein das geht nicht.

Aber das solltest Du dir anschauen:

Zitat von Palladin007

Was Du da hast, klingt nach einem PropertyGrid.
Das kann ggf. auch mit einem CustomTypeDescriptor arbeiten:
https://itecnote.com/tecnote/c-propertygrid-with-custom-propertydescriptor/

C
cprogrammer Themenstarter:in
73 Beiträge seit 2023
vor 8 Monaten

Ich habe jetzt die Properties in einer Klassen gekapselt wie folgt:

public enum CANDLE : int
   {
       Current = 0,
       Last = 1,
       Previous = 2
   }

class GUI
   {
       private bool _formonoff = false;
       private CANDLE number = CANDLE.Current;

       [Display(GroupName = "Tick Script")]
       public bool ShowForm
       {
               get 
               {
                      if (number == CANDLE.Current) return _formonoff;
                      else
                      return false;
               }
               set 
               {
                      if (number == CANDLE.Current) _formonoff = value;
               
               }
           }
   

       public GUI(CANDLE Number)
       {
           number = Number;
       }


       }

[Display(GroupName = "Tick Script")]

Bleibt wirkungslos, d.h. die Gruppe "Tick Script" wird im Dialog meiner 3rd Party Application nicht angezeigt.

Packe ich denselben Code in die Hauptklasse meine DLL in den Bereich der Properties und lasse die Klasse GUI weg, dann funktioniert es.

Mein Ziel ist es ja diesen Code aus meiner Haupklasse zu verbannen und zu Kapseln, aber irgendwas passt nicht.

2.071 Beiträge seit 2012
vor 8 Monaten

Das PropertyGrid arbeitet mit Reflection, es sucht die Daten aber nicht selbständig.

Das Programm sucht sich das also irgendwie auf eigene Weise und da können wir dann nicht helfen.
Du kannst dir aber ILSpy oder DnSpy herunterladen und selber nachforschen, wie die Daten gesucht werden.
Sobald das Objekt dem PropertyGrid bekannt ist, sollte es auch die Properties gruppiert nach der Gruppe anzeigen.

C
cprogrammer Themenstarter:in
73 Beiträge seit 2023
vor 8 Monaten

Habe jetzt dotPeek installiert und die .exe der Applikation, welche meine dll nutz, geladen.

Der Code ist obfuscated.

Aber es scheint eine Attributes.dll zu geben, siehe Anhang.

Hättest du noch einen Tipp, wie ich da jetzt rangehe ?

Es scheint so, als ob die Diaplay-Anwensiung immer eine Funktion mit get/set erwartet, ohne sonstige Parameter. Versuche ich Parameter zu übergeben, geht es schon nicht mehr.

Werden diese Attribute eigentlich zur Laufzeit oder nur zur Compilierzeit verarbeitet ?

PS. Kannst du einen Dotfuscator für den eigenen Code empfehlen und taugt der in VS mitgelieferte etwas ?

2.071 Beiträge seit 2012
vor 8 Monaten

Unter Attributes sind vermutlich nur Attribute, da wirst Du keine Logik finden.

Hättest du noch einen Tipp, wie ich da jetzt rangehe ?

Beim Entwickler der Software nachfragen, wie die sich das gedacht haben.
Wenn die sich gar nichts gedacht haben bzw. das gar keine offizielle Schnittstelle ist, dann bleibt nur Probieren und Reverse Engeneering.