DLL verwaltet ihre Erweiterungen

  • VB.NET
  • .NET 9

Es gibt 15 Antworten in diesem Thema. Der letzte Beitrag () ist von siycah.

    DLL verwaltet ihre Erweiterungen

    Neu

    Hallo,

    Ist es möglich, dass ich eine DLL so gestalten kann, dass z.B. eine extern hinzugefügte Methode automatisch von der DLL mitgenutzt wird?
    Angenommen man hat eine Methode Foo(x as Base) und eine Methode Bar(x as A) in der DLL definiert, wobei A von Base abgeleitet ist. Kann ich Foo so basteln, dass sie eine Abhängigkeit zu allen abgeleiteten Klassen hat?
    Also extern leite ich dann Base ab als B und erstelle die Methode Baz(x as B). Und am Ende kann Foo(B_Instanz) Baz nutzen?

    Ich weiß nicht ob das Richtung DependencyInjection geht. Das Vorhaben wirkt auf mich aber sehr "injizierend"

    Viele Grüße

    Neu

    Ich blicke leider Dein Wunschkonzept nicht. Ob jetzt DLL oder nicht, ist ja wahrscheinlich erstmal wurscht.
    was ich aus Deinen Infos extrahiere:

    VB.NET-Quellcode

    1. Sub Foo(x As Base)
    2. Baz(x)
    3. End Sub
    4. Sub Bar(x As A)
    5. End Sub
    6. Sub Baz(x As B)
    7. End Sub
    8. Class Base
    9. End Class
    10. Class A: Inherits Base
    11. End Class
    12. Class B: Inherits Base
    13. End Class


    Nach dem Prinzip kann Foo zwar das von außen hinzugefügte x nutzen, aber erstmal nur die Base-Komponenten verwenden/aufrufen, da Foo nur den Typ Base kennt, nicht den abgeleiteten Typ A/B. Auch kann Foo das Base-Objekt an Baz weiterleiten. Und Baz kann (wie auch Foo, obwohl das nicht gewünscht zu sein schein) x per DirectCast in ein A/B zurückcasten. Ein Objekt in eine Variable der Basisklasse zu packen, nennt sich Boxing. Das Umwandeln in den abgeleiteten Typ Unboxing. Das ist ein gängiges Prinzip.

    Nur weiß ich nicht, ob damit Deine Frage beantwortet wurde.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Neu

    Wenn du in deiner Lib einen Base-Typ hast und in der implementierenden Anwendung einen spezialisierten Typ mitgibst, wird der spezialisierte Typ verwendet.
    Dafür läuft im Endeffekt die VTable durch.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems

    Selbstständiger Softwareentwickler & IT-Techniker.

    Neu

    Das Prinzip des Boxing ist mir bekannt, damit versuche ich zu arbeiten damit ich mit einer Methode alle abgeleiteten Typen erschlagen kann.
    Der Punkt in meiner Fragestellung ist, ein Teil des Codes wird per DLL (*) bereitgestellt (Base, A, Foo, Bar).
    Extern wird der Typ B und die Methode Baz eingeführt. Da die DLL fertig ist (*) kann Foo nicht mehr angepasst werden. Foo kann Baz oder B nicht auf diesem direkten Weg kennen.

    (*) Ich schreibe zwar die DLL sei fertig, die DLL will ich jedoch erstellen, sie ist also nicht tatsächlch fertig. In einem hypothetischen zukünftigen Szenario, in dem die DLL genutzt wird wäre sie dann aber natürlich fertig.
    In diesem Szenario entspricht die DLL nicht dem open-closed-principle. Eine Erweiterung kann nur durch Modifikation erfolgen, wenn die DLL naiv erstellt wurde.
    Die Frage ist gibt es eine Möglichkeit die DLL so zu bauen, dass sie erweiterbar wird in Bezug auf den Nutzen von Foo?

    Neu

    Haudruferzappeltnoch schrieb:

    Das Prinzip des Boxing ist mir bekannt


    Von Boxing rede ich nicht. Boxing ist, wenn du ein Typ zum abstrakten Typ object wandelst und danach die Informationen inferierst. Ich rede von ganz normaler Vererbung.

    Haudruferzappeltnoch schrieb:

    Der Punkt in meiner Fragestellung ist, ein Teil des Codes wird per DLL (*) bereitgestellt (Base, A, Foo, Bar).


    Habe ich verstanden und dir genau so auch mitgeteilt, dass das funktioniert.

    In deiner Lib hast du IMyBase.
    Dann hast du MyBaseUser, was eine Funktion Foo hat:

    C#-Quellcode

    1. // MyLib
    2. interface IMyBase {
    3. void DoFoo();
    4. }
    5. class MyBaseSpecialiser: IMyBase {
    6. public void DoFoo() => Console.WriteLine(nameof(MyBaseSpecialiser));
    7. }
    8. class MyBaseUser {
    9. public void Foo(IMyBase bar) => bar.DoFoo();
    10. }


    C#-Quellcode

    1. // MyApp
    2. class MyBaseSpecialiser2: IMyBase {
    3. public void DoFoo() => Console.WriteLine("I'm in the application!");
    4. }
    5. class LibCaller {
    6. internal void Baz() {
    7. var user = new MyBaseUser();
    8. user.DoFoo(new MyBaseSpecialiser2());
    9. }
    10. }


    Output ist dann: I'm in the application!

    Und die Lib kennt davon nichts.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems

    Selbstständiger Softwareentwickler & IT-Techniker.

    Neu

    Haudruferzappeltnoch schrieb:

    Das war auch nicht an dich gerichtet.

    Entschuldige, ging aus deiner Nachricht nicht so hervor.

    Haudruferzappeltnoch schrieb:

    Deinen Ansatz verstehe ich zwar noch nicht, aber ich werde mich daran orientieren und dann wird das im Selbstversuch denke ich klarer.

    Jo, das klingt nach einem guten Plan.
    Ist eigentlich ganz einfach.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems

    Selbstständiger Softwareentwickler & IT-Techniker.

    Neu

    @siycah
    Leider krieg ich es damit nicht hin, bei mir unterscheiden sich die Signaturen.
    Was ich ja eigentlich will ist, dass Foo(other as A) sich anders verhält als Foo(other as B) beide aber über eine Basisklasse zugäglich machen.
    Die Basisklasse kann dann aber nur Foo(other as Base) verlangen.
    In A kann ich Foo(other as Base) implementieren und wenn other vom Typ A ist auf Foo(other as A) umleiten. Aber was ist wenn other vom Typ B ist? Da müsste Foo(other as Base) quasi auf B.Foo(other as A) umleiten, diese Methode kann ich extern ja nachträglich erstellen. Aber B ist der DLL nicht bekannt.

    Kann ja sein dass das auswegslos ist.

    C#-Quellcode

    1. // naive DLL
    2. public abstract class Base
    3. {
    4. public abstract int Foo(Base other);
    5. }
    6. public class A : Base
    7. {
    8. public override int Foo(Base other)
    9. {
    10. if (other is A)
    11. return 1;
    12. else
    13. return 0;
    14. }
    15. }
    16. //----------------------------------------------------------------------------------
    17. public class B : Base
    18. {
    19. public override int Foo(Base other)
    20. {
    21. if (other is B)
    22. return -1;
    23. else if (other is A)
    24. return 2;
    25. else
    26. return 0;
    27. }
    28. }
    29. // A1.Foo(A2) = 1 , B1.Foo(B2) = -1 , B1.Foo(A1) = 2 , A1.Foo(B1) = 0 <- das sollte aber auch 2 werden.


    @fichz
    Das mit dem PlugIn sieht auch nicht ganz uninteressant aus, aber da muss ich villeicht noch ein bisschen drüber grübeln, ob mir das weiterhelfen kann.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Neu

    Hallo,
    wenn du willst, dass sich die Funktionsweise unterscheidet, solltest du vielleicht doch auf Interfaces setzen. So wie @siycah bereits vorgeschlagen hatte. Seit C# 8 können in Interfaces auch Standardschnittstellenmethoden definiert werden.

    C#-Quellcode

    1. internal interface IBaseImplementation
    2. {
    3. int Foo() => 0;
    4. }
    5. internal class ClassA : IBaseImplementation
    6. {
    7. public int Foo()
    8. {
    9. return 1;
    10. }
    11. }
    12. internal class ClassB : IBaseImplementation
    13. {
    14. public int Foo()
    15. {
    16. return -1;
    17. }
    18. }
    19. internal class ClassBase : IBaseImplementation
    20. {
    21. void Other() { }
    22. }


    Jede Instanz der Klasse kann dann Foo aufrufen und je nach Implementierung anders arbeiten.

    C#-Quellcode

    1. IBaseImplementation b1 = new ClassBase();
    2. Execute(b1);
    3. IBaseImplementation b2 = new ClassA();
    4. Execute(b2);
    5. IBaseImplementation b3 = new ClassB();
    6. Execute(b3);
    7. static void Execute(IBaseImplementation b) => Console.WriteLine(b.Foo());


    Du musst dann so nicht wild mit Pattern-Matching prüfen, welche Funktionsweise du jetzt ausführen willst.

    Und einhergehend hilft auch das Facade pattern

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ISliceUrPanties“ ()

    Neu

    Das mit dem Boxing/Unboxing war falsch bei mir abgespeichert. Willst Du uns was zu Deinem spezifischen Ziel verraten? Welches Problem willst Du konkret damit lösen?
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Neu

    @ISliceUrPanties
    Es ist wichtig dass die Klassen untereinander interagieren. Du hast jetzt auch kein Argument in den Signaturen. Eine Ausgabe 2 kommt bei dir gar nicht vor. In dem Fall kann ich das nachvollziehen, das würde mit Vererbung aber auch funktionieren. Ich kann das auch über Interfaces machen, darauf kommt es mir am Ende auch nicht an.

    @VaporiZed
    Wenn ich mit mathematischen Objekte Operationen durchführe, dann sind das meist sehr explizite Gesetzmäßigkeiten abhängig von der Natur des Objekts.
    Bestimme den Schnittpunkt von Gerade und Kreis oder Gerade und Gerade. Will an dieser Stelle quasi Schnittpunkte bestimmen können unabhängig vom Objekt.
    Oder Ich kann Matrix mit Matrix multiplizieren oder Matrix und Skalar oder Skalar und Skalar. An der Stelle soll eine allgemeine Multiplikation aufrufbar sein.
    Einige solcher Operationen sind einfach und kann man alles schön vorbereiten in einer DLL, aber nun kommt eines Tages ein neues Objekt, eine neue Methode oder einfach jemand der komplexere Dinge bewerkstelligen möchte.
    Dann soll die DLL Methode des generellen Schneidens oder des generellen Multiplizieren auch die neue Implementation umsetzen können, die es in der DLL selbst gar nicht gibt.

    Und da passiert immer etwas von der Art "Operand1 Operator Operand2". Ich habe das vorher missverständlich erklärt, weswegen wir nur zu "Operand Operator" gekommen sind (Wie zum Beispiel die Fakultätsfunktions, wenn es nicht von anderen Operanden abhängt verstehe ich, ist das nachträgliche Ergänzen einfach).

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Neu

    A1.Foo(B1) = 0 <- das sollte aber auch 2 werden.

    Nein, A.Foo gibt 0 zurück wenn es sich um keine Instanz von A handelt. Falls B1 keine Instanz von A ist stimmt die Ausgabe.

    DI Beispiel

    C#-Quellcode

    1. namespace App
    2. {
    3. using System;
    4. using DLL;
    5. // DLL kann keine Multiplikation, daher wird der Typ in der App implementiert.
    6. internal class Multiplication : AbstractCalculation
    7. {
    8. public override int CalculateValue(params int[] values)
    9. {
    10. var result = values[0];
    11. for (var i = 1; i < values.Length; ++i)
    12. result *= values[i];
    13. return result;
    14. }
    15. }
    16. class Program
    17. {
    18. static void Main(string[] args)
    19. {
    20. var division = new Division();
    21. var multiplication = new Multiplication();
    22. Console.WriteLine($"100/10 = {StaticCalculation.GetResult(division, 100, 10)}");
    23. Console.WriteLine($"10*10 = {StaticCalculation.GetResult(multiplication, 10, 10)}");
    24. var calcMultiplication = new Calculation<Multiplication>(multiplication);
    25. Console.WriteLine($"10*10 = {calcMultiplication.GetResult(10, 10)}");
    26. Console.ReadLine();
    27. }
    28. }
    29. }
    30. namespace DLL
    31. {
    32. public static class StaticCalculation
    33. {
    34. public static int GetResult(AbstractCalculation abstractCalculation, params int[] values)
    35. => abstractCalculation.CalculateValue(values);
    36. }
    37. public class Calculation<T>(T abstractCalculation) where T : AbstractCalculation
    38. {
    39. public int GetResult(params int[] values)
    40. => abstractCalculation.CalculateValue(values);
    41. }
    42. public abstract class AbstractCalculation
    43. {
    44. public abstract int CalculateValue(params int[] values);
    45. }
    46. public class Division : AbstractCalculation
    47. {
    48. public override int CalculateValue(params int[] values)
    49. {
    50. var result = values[0];
    51. for (var i = 1; i < values.Length; ++i)
    52. result /= values[i];
    53. return result;
    54. }
    55. }
    56. }

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Fakiz“ ()

    Neu

    Haudruferzappeltnoch schrieb:

    Leider krieg ich es damit nicht hin, bei mir unterscheiden sich die Signaturen.

    Das ist ja auch der Sinn dahinter. Du kannst nicht bei unterschiedlichen Objekten und Methoden gleiche Signaturen haben.

    Haudruferzappeltnoch schrieb:

    Was ich ja eigentlich will ist, dass Foo(other as A) sich anders verhält als Foo(other as B) beide aber über eine Basisklasse zugäglich machen.

    Genau das kannst du damit ja abbilden. Du musst dein Programm einfach generischer gestalten; die Spezialisierung darf dich nicht interessieren. Du willst einfach nur, dass eine bestimmte Signatur/ein bestimmter Vertrag eingehalten wird.

    C#-Quellcode

    1. using System;
    2. namespace OtherApp {
    3. class MyDerivedB: Test.MyBase {
    4. public override void Foo() => Console.WriteLine("MyDerivedB.Foo");
    5. }
    6. }
    7. namespace Test {
    8. class MyBase {
    9. public virtual void Foo() => Console.WriteLine("MyBase.Foo");
    10. }
    11. class MyDerivedA: MyBase {
    12. public override void Foo() => Console.WriteLine("MyDerivedA.Foo");
    13. }
    14. class Program {
    15. static void ExecFoo(MyBase obj) {
    16. obj.Foo();
    17. Console.WriteLine($"obj is {obj.GetType().Name}");
    18. }
    19. static void Main(string[] args) {
    20. MyBase obj = new MyDerivedA();
    21. ExecFoo(obj);
    22. ExecFoo(new OtherApp.MyDerivedB());
    23. }
    24. }
    25. }


    Output:

    Quellcode

    1. MyDerivedA.Foo
    2. obj is MyDerivedA
    3. MyDerivedB.Foo
    4. obj is MyDerivedB


    Haudruferzappeltnoch schrieb:

    In A kann ich Foo(other as Base) implementieren und wenn other vom Typ A ist auf Foo(other as A) umleiten. Aber was ist wenn other vom Typ B ist? Da müsste Foo(other as Base) quasi auf B.Foo(other as A) umleiten, diese Methode kann ich extern ja nachträglich erstellen. Aber B ist der DLL nicht bekannt.


    Du denkst zu sehr mit Spezialisierungen. Versuch etwas abstrakter zu denken.

    Alles was du wissen musst, ist dass irgendwo dein Objekt von deinem Interface/deiner abstrakten Klasse erbt und die Funktionalität erweitert. Dann musst du nur noch den Basistypen verlangen und dann regelt die Runtime alles Andere für dich.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems

    Selbstständiger Softwareentwickler & IT-Techniker.

    Neu

    Ich glaub jetzt hab ichs, meine Base.Foo war abstract nicht virtual. Aber ich muss im Base Case generell auf other.Foo umleiten, dann wird immer die neue Implementation genommen, wenn eine Ableitung kommt die der DLL nicht bekannt ist, richtig?
    Dazu kam, dass ich eigentlich in vb code und dort in Interfaces keine Vordefinition gemacht werden kann, aber in c# geht das einfach. Warum ist das so?

    Das führt im Fall der Matrixmultiplikation dazu dass seitenvertauscht gerechnet würde, aber das ist verkraftbar.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Neu

    Haudruferzappeltnoch schrieb:

    Dazu kam, dass ich eigentlich in vb code und dort in Interfaces keine Vordefinition gemacht werden kann, aber in c# geht das einfach. Warum ist das so?


    VB wird seit Jahren von Microsoft "vernachlässigt", weil VB's Syntax so schon viel zu komplex ist, aufgrund historischer Entscheidungen.
    Das bedeutet, dass es einfach viel mehr Ressourcen seitens Microsoft in Anspruch nimmt, die Syntax sinnvoll zu erweitern - wobei C# im Gegensatz die Sprache ist, auf die Microsoft setzt.
    Das Framework ist in C# implementiert, weite Teile des Betriebssystems sind/waren in C# umgesetzt und dementsprechend hat Microsoft intern natürlich genug Ressourcen, die sich drum kümmern können.

    Abgesehen davon, ist die C-artige Syntax relativ einfach erweiterbar, mit komplexen Funktionen.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems

    Selbstständiger Softwareentwickler & IT-Techniker.