Laden...

Unterkl. soll eigene Operationen anbieten, die einheitlich über die Oberkl. aufgerufen werden können

Erstellt von inflames2k vor 13 Jahren Letzter Beitrag vor 13 Jahren 3.271 Views
inflames2k Themenstarter:in
2.298 Beiträge seit 2010
vor 13 Jahren
Unterkl. soll eigene Operationen anbieten, die einheitlich über die Oberkl. aufgerufen werden können

Hallo,

ich habe eine Klasse, nennen wir sie mal "Operation" welche ein enum beinhalten soll. - Das enum soll hier jedoch dynamisch sein.

Die Klasse Operation sieht also wie folgt aus:


public class Operation
{
    public ??WelcherDatentyp?? OperationType { get; set; }
}

Nun habe ich Beispielsweise folgende 2 enums:


public enum MyFirstOperationEnum
{
    Unknown = 0,
    OperationXY = 1,
    OperationYZ = 2,
    OperationZA = 4
}

public enum MySecondOperationEnum
{
    Unknwon = 0,
    Ping = 1,
    RemoteConnect = 2,
    Discover = 4,
    ShutDown = 8,
    Restart = 16
}

Der einzige Weg der mir auf die schnelle einfällt, wäre OperationType der Klasse Operation als Object zu definieren. - Allerdings wäre mir das zu typ unsicher.

Also, welchen Datentyp kann ich angeben um den jeweiligen enum angeben zu können, außer den Typ Object?

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

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

5.742 Beiträge seit 2007
vor 13 Jahren

Hallo inflames2k,

ein Enum ist ja an sich nichts anderes als ein int/byte/short (standardmäßig ersteres). Theoretisch könntest du also einfach int verwenden.

Allerdings bieten sich hier Klassen evtl. eher an als Enums - ein "dynamisches Enum" macht IMHO eher wenig Sinn.

inflames2k Themenstarter:in
2.298 Beiträge seit 2010
vor 13 Jahren

Es ist so, dass ich aktuell an einem Projekt sitze, in dem mehrere Klassen von einer Basisklasse erben.

Basisklassenmethoden:


public virtual Run();
public virtual ExecuteOperation(Operation operation);

Childklassenmethoden:


public override Run();
public override ExecuteOperation(Operation operation)
{
    ChildOperationType operationType = (ChildOperationType)operation.OperationType;
    switch(operationType)
    {
           case ChildOperationType.Ping: this.Ping();
           break;
           case XY: XY();
           break;
    }
}

Nun ist es wie gesagt so, dass jede ChildClass andere Operationen hat die nichts miteinander zu tun haben. - Also wird wohl nur der Typ int bleiben.

Inwiefern Operationstypen hier Klassen werden könnten habe ich keine Ahnung. - Die Operationen sind jedoch Dinge die zum Objekt direkt gehören, somit schließe ich schon einmal aus Klassen mit den Operationen zu erstellen.

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

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

C
2.121 Beiträge seit 2010
vor 13 Jahren

Wenns nichts miteinander zu tun hat, warum dann in die Basisklasse stecken?
Du weißt anhand der Basisklasse ja sowieso nicht was du zuweisen sollst. Von daher kannst du die enums ja auch in die abgeleitete Klasse packen.

1.130 Beiträge seit 2007
vor 13 Jahren

Was hälst du von MethodInfo-objekten/Delegaten?

Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!

inflames2k Themenstarter:in
2.298 Beiträge seit 2010
vor 13 Jahren

Wenns nichts miteinander zu tun hat, warum dann in die Basisklasse stecken?
Du weißt anhand der Basisklasse ja sowieso nicht was du zuweisen sollst. Von daher kannst du die enums ja auch in die abgeleitete Klasse packen.

Fast, die Basisklasse daher, weil die "ExecuteOperation" in einer anderen Basisklasse ist und die Childklassen diese methode erben.

Da ich direkt über die Basisklasse ausführe wird ein eigener enum direkt übergeben nicht funktionieren.

Was hälst du von MethodInfo-objekten/Delegaten?

Kannst du das näher erläutern (falls nicht nach dem Post hinfällig)? - Auf obig geschriebenes an chilic kann ich mir ersteinmal nichts per Delegate vorstellen.

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

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

C
2.121 Beiträge seit 2010
vor 13 Jahren

Dann übergibs doch wirklich als object. Du hast die Methode ja wahrscheinlich in jeder Klasse abgeleitet, dann kannst du da jeweils prüfen ob der richtige Typ übergeben wird.
if (parameter is MyEnum) ...
Fakt ist ja dass du je nach Ableitung einen anderen Typ in die Methode übergeben willst und somit einen Basistyp brauchst. Selbst wenn du den findest, kannst du trotzdem noch "falsche abgeleitete" Objekte in die Methode übergeben. Wirklich ganz sicher kann das meiner Meinung nicht werden, denn die Methoden haben ja eigentlich eine andere Signatur, wenn man genau hinschaut.

Muss diese Methode überhaupt abgeleitet sein? Spricht was dagegen dass du die Methode ohne Ableitung in den Unterklassen implementierst, dann mit dem richtigen Enum? Du kannst sie dann nicht von der Basisklasse aus ansprechen, aber nachdem du doch je nach Unterklasse verschiedene Werte übergibst, wirst du das sowieso nicht nutzen können?

5.742 Beiträge seit 2007
vor 13 Jahren

Childklassenmethoden:

Ihh! 😉

Eine solche Methode macht nicht wirklich Sinn - du kannst sie ja auch gar nicht aufrufen, ohne zu wissen, welchen Typ das Ziel hat.
Mit sauberer OO hat das wenig zu tun!

Evtl. hilft es, wenn du etwas genauer beschreibst, wie du zu diesem Aufbau kamst bzw. was dein Ziel ist.

E
31 Beiträge seit 2009
vor 13 Jahren

Du kannst doch aus der Basisklasse eine generische Klasse machen wobei der enum type der generische type ist. Instanzieren kannst Du die abgeleiteten Klassen mit ihren jeweiligen type.


class Basis<TEnum>
{
public virtual ExecuteOperation(TEnum operation);

}
class Derived1 : Basis<Enum1>
class Derived2 : Basis<Enum2>

Gruß

1.130 Beiträge seit 2007
vor 13 Jahren

Eigendlich fallen mir je nach deiner Situation eine ganze Reihe von Möglichkeiten ein:
a.)Wenn die Klasseninstanz ihre Operationen selbst bekannt gibt:


class Base
{
    public virtual IList<Action> GetOperations()
    {
        List<Action> result=new List<Action>();
        result.Add(new Action(this.Operation1));
        result.Add(new Action(this.Operation2));
        return result;
    }

    void Operation1()
    {
    }

    void Operation2()
    {
    }
}

class Derived:Base
{
     
    public override IList<Action> GetOperations()
    {
        IList<Action> result=base.GetOperations();
        result.Add(this.Operation3);
        return result;
    }

    void Operation3()
    {
    }
}

Das hat den Vorteil, dass man die Aktionen direkt aufrufen kann (es sind schließlich Delegaten)
Die Namen der jeweiligen Funktion kann man auch ganz einfach rauskriegen:
Delegate.Method.Name

b.)


MethodInfo[] operations=typeof(Somthing).GetMethods().Where((m)=>m.Name.StartsWith("Operation")).ToArray();

kombiniert mit MethodInfo.Invoke
Das ist allerdings bescheuert zu debuggen und noch unsauberer als variante a. Man könnte die Methoden auch mit einem Attribut markieren. Außerdem ist diese Variante von natur aus langsam. Mit etwas trixerei lässt sie sich aber deutlich beschleunigen.
c.)
Dictionary<string,Action>
Vorteil: am felxibelsten, evl. sogar immernoch schneller als die switch-variante

Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!

inflames2k Themenstarter:in
2.298 Beiträge seit 2010
vor 13 Jahren

Childklassenmethoden:
Ihh! 😉

Eine solche Methode macht nicht wirklich Sinn - du kannst sie ja auch gar nicht aufrufen, ohne zu wissen, welchen Typ das Ziel hat.
Mit sauberer OO hat das wenig zu tun!

Evtl. hilft es, wenn du etwas genauer beschreibst, wie du zu diesem Aufbau kamst bzw. was dein Ziel ist.

Klar kann ich die Methode aufrufen? Meine Idee war halt das enum auf irgend eine Art zu casten. - (Hier wäre wie oben schon angesprochen wahrscheinlich nur ein int angebracht).

Grund des Aufbaus ist folgender, ich möchte eine Anwendung schreiben mit der Gewisse Komponenten überwacht werden können und zusätzliche Operationen zu diesen ausgeführt werden können.

Die Basisklasse wäre dann Observator. - Geerbte Klassen könnten dann alles mögliche sein, angefangen bei Rechnern bis was weis ich. Nun ist es ja Beispielsweise so, dass Rechner auch Freigaben haben können und eine Operation wäre die Freigaben zu öffnen.

Wäre die geerbte Klasse nun NT-Dienst könnte der als Operation haben Dienst starten.

Um die Visualisierung jedoch so einfach wie nur möglich zu halten war mein Grundgedanken hier nun eine Liste von Instanzen der Basisklasse zu verwalten. - Nun ist Observator.ExecuteOperation(...) nicht gleich Observator.ExecuteOperation(...) da eben die Operationen anders sind.

Der erste Weg von Floste sieht sehr komfortabel aus. Wobei ich hier eine Kombination aus a und c wählen würde / werde.

Ich würde es auch etwas umstellen, da nach außen maximal die Operationsbezeichnungen bekannt sein sollten.

Mir schwebt da nun folgendes vor:


public class Base
{
     public Base()
     {
           _operations = new Dictionary<String, Action>();
           _operations.Add("Operation1", new Action(this.Operation1));
     }

     private Dictionary<String, Action> _operations;

     private void Operation1()
     {

     }

     public List<String> GetOperations()
     {
         List<String> operations = new List<String>();
         foreach(String key in _operations.Keys)
         {
              operations.Add(key);
         }

         return operations;
     }

     public void ExecuteOperation(String operation)
     {
           Action action = _operations[operation];
     }

     public virtual void Initialize()
     {

     }

     protected void AddOperation(String name, Action operation)
     {
          _actions.Add(name, operation);
     }
}

public class Derived : Base
{
       public override void Initialize()
       {
             this.AddOperation("Shutdown", new Action(this.ShutDown));
             this.AddOperation("Discover", new Action(this.Discover));
             ...
       }

       private void ShutDown()
       {

       }

       private void Discover()
       {

       }
}

Den Methoden Namen nehme ich an der Stelle nicht als Key für das Dictionary, auch wenn es naheliegend wäre.

Gibt es gegen den Aufbau Einwände?

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

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

656 Beiträge seit 2008
vor 13 Jahren

Wie Umfangreich sind die Operationen, auf die sich der Enum auswirkt? Sind die Operationen selber zwischen verschiedenen Klassen möglicherweise wiederverwendbar?

Vielleicht bietet sich hier ein Pattern ala Strategy an, wo du statt dem Enum einfach eine Basisklasse/Interface bekommst, und statt dem switch/case nur eine DoStuff()-Methode aufrufst.

inflames2k Themenstarter:in
2.298 Beiträge seit 2010
vor 13 Jahren

Einige Operationen sind in anderen Klassen verwendbar (allerdings nicht in allen) andere wiederum nur in einer.

Vielleicht bietet sich hier ein Pattern ala Strategy an, wo du statt dem Enum einfach eine Basisklasse/Interface bekommst, und statt dem switch/case nur eine DoStuff()-Methode aufrufst.

Du meinst also ein Interface bzw. eine Basisklasse mit genau dieser einen Methode?

Hier sehe ich das Problem, dass die Klasse auf gewisse Fields und Properties der aufrufenden Klasse zugreifen muss. - Reflection schließe ich hier aus.

Werd mal sehen, momentan favorisiere ich Floste's Variante auf meine Bedürfnisse angepasst,

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

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

S
902 Beiträge seit 2007
vor 13 Jahren

Hallo,

in welcher FW-Version arbeitest du?
Wenn du die 4 verwendest, solltest du dir mal dynamic anschauen.

Du meinst also ein Interface bzw. eine Basisklasse mit genau dieser einen Methode?

Hier sehe ich das Problem, dass die Klasse auf gewisse Fields und Properties der aufrufenden Klasse zugreifen muss. - Reflection schließe ich hier aus.

da kannst du doch diese Infos als Parameter an deine Strategy (welche auch als Methodenobjekt refaktorisiert werden kann) übergeben!

mfg
serial

inflames2k Themenstarter:in
2.298 Beiträge seit 2010
vor 13 Jahren

Hallo,
in welcher FW-Version arbeitest du?
Wenn du die 4 verwendest, solltest du dir mal dynamic anschauen.

Version 2. 😃

da kannst du doch diese Infos als Parameter an deine Strategy (welche auch als Methodenobjekt refaktorisiert werden kann) übergeben!

Ich hab seine Variante folgendermaßen verstanden:


interface IStrategy
{
     void DoStuff();
}

public class DiscoverStrategy : IStrategy
{
      public void DoStuff()
      {
           // do the things to do
      }
}

public class ShutdownStrategy : IStrategy
{
     public void DoStuff()
     {
          // do the things to do
     }
}

public class Base
{
      private BaseConfig _config;
      public BaseConfig Config
      {
           get { return _config; }
           set { _config = value; }
      }

      private BaseInfo _info;
      public BaseInfo Info
      {
          get { return _info; }
          set { _info = value; }
      }

      // ... some more properties and other way fields

      public virtual void Run()
      {
      }

      public void ExecuteOperation(IStrategy operation)
      {
          operation.DoStuff();
      }
}

Nun habe ich zwar meine vielen schönen Properties und Fields, kann diese aber nicht passend an die Strategy übergeben. Je nach auszuführender Operation übergeben, sondern könnte sie allenfalls alle übergeben.

Das wäre allerdings aus meiner Sicht großer Mist, da ich an eine Methode auch nur die Dinge übergeben muss, die ich brauche.

da kannst du doch diese Infos als Parameter an deine Strategy (welche auch als Methodenobjekt refaktorisiert werden kann) übergeben!

Äh wie jetzt? Dann würde oben beschriebenes wieder keinen Sinn machen. - Die einzelnen Klassen haben unterschiedliche Properties und Fields - lediglich die der Basisklasse sind die gleichen.

Ich kann jetzt schlecht dem Interface der Strategy sagen: "So jetzt kriegst du mal X Dinge von mir." - Da Antwortet mir das Interface auch mit ner Exception oder nem Compiler Fehler ala "Du bist doof!".

Jeder Strategy in der direkten Implementation die Informationen übergeben wäre auch wieder unschön. - Weil hier müsste ich nun wieder casten und das möchte ich vermeiden.

Ziel für mich ist es, dass ganze so dynamisch wie möglich und problemlos erweiterbar zu halten.

Das StrategyPattern gibt mir hier zwar genau diese Möglichkeit aber mit Abstrichen, wie eben die Übergabe der Parameter, die an der Stelle die Dynamic wieder heraus nimmt.

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

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

S
902 Beiträge seit 2007
vor 13 Jahren

HAllo,

du kannst das Strategyinterface generisch machen, und als Parameter ein Parameterobjekt übergeben, welches durch den generischen Typ angeboten wird.

mfg
serial

inflames2k Themenstarter:in
2.298 Beiträge seit 2010
vor 13 Jahren

Kam mir auch in den Sinn, doch muss ja auch hier die Anzahl der Parameter bekannt sein.

Nehmen wir an, wir möchten die Erreichbarkeit eines Hosts ermitteln, brauchen wir was? Genau, nur seine IP, diese als Typargument reicht also aus.

Also wäre für den Fall:


interface IStrategy<T>
{
}

ausreichend.

Nehmen wir nun einen NT-Dienst der per Socket Verbindungen entgegen nimmt. - Hier bräuchten wir Port und IP Adresse. Wo wir bei 2 Parametern wären. - Ich kann mich auch irren, aber die Typparameter eines generischen Interfaces kann man auch nicht dynamisch erweitern.

Somit müsste das Interface nun so aussehen:


interface IStrategy<T, U>
{
}

Die Aufzählung könnte ich nun ewig weiter führen. - Lasse mich aber auch gern eines besseren belehren.

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

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

S
417 Beiträge seit 2008
vor 13 Jahren

Hallo,

du verwendest am Besten eine Klasse, welche die benötigten Parameter enthält, dann hast du immer einen generischen Parameter, denn du bei Bedarf erweitern kannst.

Beispiel:


public class NtServiceData
{
	public string IP { get; set; }
	public int Port { get; set; }
}

public class NtServiceStrategy : IStrategy<NtServiceData>
{
	// ...
}
inflames2k Themenstarter:in
2.298 Beiträge seit 2010
vor 13 Jahren

Das war jetzt nur ein Beispiel 8)

Für die beiden Fälle würde das Config - Objekt alle mal ausreichen. Aber gibt halt auch Dinge die mit einfließen können die mit der Config nichts zu tun haben.

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

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

S
902 Beiträge seit 2007
vor 13 Jahren

HAllo,

so wie es Sarc sagt, meine ich es auch!
Du hast ein Parameterobjekt, wieviele Member dieses hat, ist ja egal.

Für den Host eben nur die IP, für etwas anderes, eben mehr!
Dieser Typ des Parameterobjektes ist dann der Typparameter der generischen Strategyklasse.

mfg
serial

inflames2k Themenstarter:in
2.298 Beiträge seit 2010
vor 13 Jahren

Aber auch da komme ich doch wieder um casts nicht herum? - Wie auch immer, eine ganze Fülle an Lösungsvorschlägen habe ich ja nun, über die ich schlafen kann.

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

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

S
902 Beiträge seit 2007
vor 13 Jahren

Aber auch da komme ich doch wieder um casts nicht herum? - Wie auch immer, eine ganze Fülle an Lösungsvorschlägen habe ich ja nun, über die ich schlafen kann.

manchmal kommt man einfach nicht um wenigsten einen cast herum, wobei man da wieder generische factories oder microkernel in erwägung ziehen kann.

mfg
serial

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo inflames2k,

bisher haben wir über verschiedene Operationen gesprochen. Jetzt reden wir auch noch über unterschiedliche Parameter für die Operationen. Es ist offensichtlich, dass sich das mit einer streng typisierten Sprache nicht verträgt. Wenn du komplett dynamisch vorgehen willst, sind nicht mal Generics flexibel genug, denn das ist ein Compiletimefeature. Wenn du volle Flexibilität zur Laufzeit haben willst, dann brauchst du Object oder Object [] (ob es mit dynamic gehen würde, weiß ich nicht, vielleicht). Dann hast du aber natürlich keine Compilezeitprüfungen mehr und musst in den Unterklassen auf die konkreten Typen casten. Wenn du Compilezeitprüfungen haben willst und nicht casten willst, dann ist natürlich auch die Flexibilität geringer. Vollständig dynamisch geht es dann nicht mehr. Du hast da einen Zielkonflikt, der nie vollständig aufgelöst werden kann ... egal wielange wir noch drüber reden.

herbivore