Methoden über String aufrufen (Konsole)

  • VB.NET
  • .NET (FX) 1.0–2.0

Es gibt 21 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Methoden über String aufrufen (Konsole)

    Hey Ho,
    wollte mal für mich selbst eine Konsolenanwendung schreiben die Eingaben auswertet und die jeweilige Methode aufruft.
    Möchte gern wissen ob man das Code technisch optimieren / automatisieren kann ? Also wenn ich "test" eingeben die Methode "_test()" aufgerufen wird.
    Vielleicht mit einem 2D Array die die Methoden und Schlüsselwörter enthält ?

    Im Moment benutze ich Select Case dafür:

    VB.NET-Quellcode

    1. Private Sub CommandMapper(Line As String)
    2. With Line.ToLower
    3. Select Case True
    4. Case .StartsWith("exit") : _exit(Line)
    5. Case .StartsWith("test") : _test(Line)
    6. Case .StartsWith("radio") : _radio(Line)
    7. 'Case .StartsWith("usw") : _usw(Line)
    8. End Select
    9. End With
    10. End Sub
    11. Private Sub _exit(para As String)
    12. Console.WriteLine("Sayonara~")
    13. System.Threading.Thread.Sleep(2000)
    14. Environment.Exit(0)
    15. End Sub
    16. Private Sub _test(para As String)
    17. MsgBox(para)
    18. End Sub
    19. Private Sub _radio(para As String)
    20. Dim f As New fRadio
    21. f.Show()
    22. End Sub
    Dictionary mit string als key und delegate als value...müssen halt alle die gleiche signatur haben.
    und dann über linq extensions wie .find (ne sry, kein .Find() sondern .First(); .Find() gibts ja nur in List) oder dergleichen das dictionary durchsuchen und bei fund entsprechende methode aufrufen

    lg radinator
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell

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

    Lieber wäre mit komplett ohne Array etc. Also das ich nicht jede Methode irgendwo listen muss sondern halt eben direkt aufrufen kann.
    Habe eben CallByName() gefunden.
    Allerdings bekomme ich das noch nicht in einer Konsole zum Laufen.

    Und ich habe auch etwas über Reflection gelesen, aber da muss ich mich noch bissl schlau machen.

    Trotzdem vielen Danke schon mal für deinen Vorschlag. :)
    Von Reflection und Callbyname würde ich abraten. Das sind Techniken, die OOP-Prinzipien umgehen - umständlich und anfälig - sollte man nur ganz notfalls verwenden, und sich keinesfalls angewöhnen.

    Apropos Callbyname - das ist doch eine Methode aus dem Anfänger-Namespace, den man als allererstes abklemmen sollte, weil man sonst ständig Gefahr läuft, vb6-Code zu coden:
    Visual Studio - Empfohlene Einstellungen

    Ansonsten geht das ganz wunneprächtig mit einem Dictionary, und das wäre auch sehr lehrreich bezüglich wie OOP und .Net tickt - aber mehr dazu erst, wenn du dich von den Deppen-Einstellungen befreit hast, die MS allen vb.net-Proggern erstmal aufdrückt.
    Drücke ich mich iwie unklar aus?

    ThuCommix schrieb:

    Wieso würdest du von Reflection abraten?

    ErfinderDesRades schrieb:

    Das sind Techniken, die OOP-Prinzipien umgehen - umständlich und anfälig


    Pantsuu schrieb:

    meinst du die MsgBox ?

    ErfinderDesRades schrieb:

    Das blaue da, das ist ein Link, da ist imo ausschweifend erklärt, was ich meine.
    Msgbox ist auch im Deppen-Namespace, aber ist noch das geringste Problem.

    ErfinderDesRades schrieb:

    2) Sich Helfen lassen
    Als nächstes sollte man einen Teil (den hässlichsten) des so verunstalteten Codes einfach auf VBP posten, mit Bitte um Hilfe.Weil wo man als "Bisher-Depp" überhaupt keinen Ansatz für hat, das korrigiert jmd, der Datentypen "kann" in weniger als einer Minute.Dazu zwei oder drei Erklärungen, und dann findet man schnell hinein und kann die anderen hundert Fehler selbst berichtigen.Auch erhält man dabei wertvolle prinzipielle Vorschläge, etwa für Zahlen-Eingaben NumericUpdowns zu nehmen statt Textboxen - an solchen Punkten fängt die Anwendung an, sich wirklich zu wandeln, und in gleichzeitig mehrfacher Hinsicht sich zu verbessern (Benutzerführung, Wartbarkeit, Stabilität,...)


    Ich kenne den Thread und programmiere auch durchgehend mit OptionStrict On in meinen Projekten.
    Hier erfrage ich dich offiziell was dich genau an meinem CodeStil stört.
    @Mokki
    Das ist mir bewusst.. und das war nicht Grund seiner Aussage :/ vllt isser heut bissl gestresst.

    Der code oben ist nur als kurzen Beispiel dahin geachrieben, mir gehts eig nur ums mapping der Eingabe und den subs ~.~

    --
    sry wegen rechtschreibfehler etc bin nun unterwegs.

    Pantsuu schrieb:

    Hier erfrage ich dich offiziell was dich genau an meinem CodeStil stört.
    Habich gesagt, an deim Stil störe mich was?

    Ich rate nur, den Deppennamespace abzuklemmen, und das begründe ich ja auch.
    An deim Stil habich bislang nix auszusetzen, trotzdem rate ich, den Microsoft.VisualBasic-Namespace abzuklemmen.
    Eben damit du nicht an so vb6-Katastrophen wie CallbyName() gerätst.

    Warum interessiert dich CallbyName und Reflection mehr als Dictionary(Of String, Action(Of String))?
    CallbyName ist 'ne Katastrophe, Reflection ist eine Ausnahmetechnik, aber mit Dictionary(Of Tkey, TValue) umgehen zu können - das halte ich für einen Progger für sehr wichtig.

    mapping der Eingabe und den subs
    Weil Mapping - das ist genau was ein Dictionary macht, und zwar sehr fein macht es das.

    ErfinderDesRades schrieb:

    Warum interessiert dich CallbyName und Reflection mehr als Dictionary(Of String, Action(Of String))?
    CallbyName ist 'ne Katastrophe, Reflection ist eine Ausnahmetechnik, aber mit Dictionary(Of Tkey, TValue) umgehen zu können - das halte ich für einen Progger für sehr wichtig.


    Hatte dafür bis jetzt keine Verwendung gehabt aber hab mich daran erinnert wie ich früher bei den Controls auf winform per schleife durchgegangen bin und nach Name geprüft habe. Wie du siehst habe ich ja oben mit Select Case gearbeitet.
    Auf CallByName und Reflection bin ich nur gestoßen weil ich dachte es gibt evtl eine andere Möglichkeit eben ohne jede einzeln aufzulisten, da umso mehr Befehle desto länger wird auch der CommandMapper mit eigentlich immer fast identischen Aufrufe . Einfach Neugierde. ;) Praktisch denke ich auch nicht das über dem Dictionary "kürzer" geht als mit meiner Select Case Methode X/

    Außer man kann die Dictionary mit ner Schleife von einem StringArray der Befehle füllen und den Name der Methode davon übernehmen. ^^
    Nee, haste schon recht: viel kürzer als Select Case wirds mittm Dictionary auch nicht - weil Dictionary hat keine listige Methode, mit der mans beim Initialisieren gleich befüllen kann.

    Auch wäre Vermeidung von Code-Redundanz tatsächlich ein Argument für Reflection - damit kann man die zulässigen Befehle direkt aus der Klasse ablesen, und muss sie nicht extra als String-Key oder Case-Bedingung zusätzlich nochmal aufführen.

    Also Reflection-Stichworte sind GetType(Typename), damit kriegst du ein Type-Objekt, und mit Type.GetMethod() kannst du MethodInfos aller Methoden dieses Typs abrufen, und man kann son MethodInfo auch ausführen.
    Schau dir die Verhältnisse am besten im ObjectBrowser an - kennst du Objectbrowser? sonst guggemol VisualStudio richtig nutzen (Google ist nicht deine Mami)
    Und auf MSDN finden sich vlt. auch CodeSamples.

    Wie gesagt: Alles bisserl umständlich, und eher am Rande von OOP.
    Weil damit kannst du Methoden aufrufen, die eigentlich gekapselt sein sollten, und ausserdem kann der Compiler nicht die Richtigkeit der Aufrufe überprüfen - weder Methoden-Name noch Korrektheit der Argumente.
    Typsicherheit ist dabei also ebenfalls suspendiert.
    @TE: Ich hatte auch mal mir so ne kleine Application für Konsole geschrieben, die mir alle IP adressen von 0.0.0.0 bis 255.255.255.255 aufschreibt. Wollte das ganze halt auch steuerbar machen, also mit Befehlen wie etwa "exit" oder dergleichen. Ich hab das damals auch mit einem schlichten Select Case gelöst ;D Doch wie EDR bereits erwähnt hat, ist das mit einem Dictionary viel eleganter und OOP technisch besser gelöst.:

    ErfinderDesRades schrieb:

    Ansonsten geht das ganz wunneprächtig mit einem Dictionary


    ErfinderDesRades schrieb:

    viel kürzer als Select Case wirds mittm Dictionary auch nicht

    Jo ;D wird sogar nur noch länger, man braucht ja schließlich auch eine Methode, mit der man die Zurodnung von Funktionsaufruf und Schlüssel herstellt
    Wied eher kürzer, denn man braucht zwar ein bisschen mehr platz wegen Zuordnung von Funktionsaufruf und Schlüssel, spart sich aber das (vor allem in VB) lange Select case mit einem einfachen

    VB.NET-Quellcode

    1. Dictionary(Of String, Action(Of String)).First(Function(x) x.Key = input).Value.Invoke(s)
    2. 'Respektive:
    3. Action a = Dictionary(Of String, Action(Of String)).First(Function(x) x.Key = input).Value
    4. a(input)

    Im Fall von C#:

    C#-Quellcode

    1. Dictionary<string, Action<string>>.First(x => x.Key == input).Value(s);
    2. //Respektive:
    3. Action<string> a = Dictionary<string, Action<string>>.First(x => x.Key == input).Value;
    4. a(s)


    Hab das ganze mal in C# und VB gelöst:
    Code

    C#-Code

    C#-Quellcode

    1. using System;
    2. using System.Linq;
    3. using System.Collections.Generic;
    4. namespace CSCallFunctionByInput
    5. {
    6. class Program
    7. {
    8. private static Dictionary<string, Action<string>> actionList = new Dictionary<string, Action<string>>();
    9. static void Main(string[] args)
    10. {
    11. string s = "";
    12. init();
    13. do
    14. {
    15. s = Console.ReadLine();
    16. Action<string> a1;
    17. Action<string> a2;
    18. actionList.First((x) =>
    19. {
    20. return x.Key == s;
    21. }).Value(s);
    22. a1 = actionList.First((x) =>
    23. {
    24. return x.Key == s;
    25. }).Value;
    26. actionList.First(x => x.Key == s).Value(s);
    27. a2 = actionList.First(x => x.Key == s).Value;
    28. a1(s);
    29. a2(s);
    30. } while (true);
    31. }
    32. static void init()
    33. {
    34. actionList.Add("sub1", new Action<string>(sub1));
    35. actionList.Add("sub2", new Action<string>(sub2));
    36. actionList.Add("sub3", new Action<string>(sub3));
    37. actionList.Add("sub4", new Action<string>(sub4));
    38. }
    39. private static void sub1(string input)
    40. {
    41. Console.WriteLine("Ich bin Sub1 und das war mein Input: " + input);
    42. }
    43. private static void sub2(string input)
    44. {
    45. Console.WriteLine("Ich bin Sub2 und das war mein Input: " + input);
    46. }
    47. private static void sub3(string input)
    48. {
    49. Console.WriteLine("Ich bin Sub3 und das war mein Input: " + input);
    50. }
    51. private static void sub4(string input)
    52. {
    53. Console.WriteLine("Ich bin Sub4 und das war mein Input: " + input);
    54. }
    55. }
    56. }


    VB.NET-Code

    VB.NET-Quellcode

    1. Imports System
    2. Imports System.Linq
    3. Imports System.Collections.Generic
    4. Module Module1
    5. Private actionList As Dictionary(Of String, Action(Of String)) = New Dictionary(Of String, Action(Of String))()
    6. Sub Main()
    7. Dim s As String = ""
    8. Do
    9. s = Console.ReadLine()
    10. Dim a1 As Action(Of String)
    11. Dim a2 As Action(Of String)
    12. actionList.First(Function(x)
    13. Return x.Key = s
    14. End Function).Value.Invoke(s)
    15. a1 = actionList.First(Function(x)
    16. Return x.Key = s
    17. End Function).Value
    18. actionList.First(Function(x) x.Key = s).Value.Invoke(s)
    19. a2 = actionList.First(Function(x) x.Key = s).Value
    20. a1(s)
    21. a2(s)
    22. Loop While True
    23. End Sub
    24. Private Sub init()
    25. actionList.Add("sub1", New Action(Of String)(AddressOf sub1))
    26. actionList.Add("sub2", New Action(Of String)(AddressOf sub2))
    27. actionList.Add("sub3", New Action(Of String)(AddressOf sub3))
    28. actionList.Add("sub4", New Action(Of String)(AddressOf sub4))
    29. End Sub
    30. Private Sub sub1(input As String)
    31. End Sub
    32. Private Sub sub2(input As String)
    33. End Sub
    34. Private Sub sub3(input As String)
    35. End Sub
    36. Private Sub sub4(input As String)
    37. End Sub
    38. End Module




    Doof ist halt bei der Methode nur, dass eine Exception fliegt, falls man eine Eingabe macht, die im Dictionary ned hinterlegt ist. Also wenn ich "sub4" eingebe, dann geht alles wunderbar. Sobald aber w.w.i "Sub1" (sowohl C# als auch VB.Net sind in dem Fall Case-Sensitive oder "sub5"(gibt es in dem Bsp ned) eingebe, dann komm eine InvalidOperationException daher:
    Spoiler anzeigen

    System.InvalidOperationException wurde nicht behandelt.
    HResult=-2146233079
    Message=Die Sequenz enthält kein übereinstimmendes Element.
    Source=System.Core
    StackTrace:
    bei System.Linq.Enumerable.First[TSource](IEnumerable`1 source, Func`2 predicate)
    bei VBCallFunctionByInput.Module1.Main() in c:\users\Radinator\documents\visual studio 2015\Projects\VBCallFunctionByInput\VBCallFunctionByInput\Module1.vb:Zeile 15.
    bei System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
    bei System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
    bei Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
    bei System.Threading.ThreadHelper.ThreadStart_Context(Object state)
    bei System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
    bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
    bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
    bei System.Threading.ThreadHelper.ThreadStart()
    InnerException:


    //Edit: Bin auch dämlich *eyerole* die Exception kann man ja auch mit einer if-Abfrage abfangen. Einfach prüfen, ob die Auflistung die Eingabe als Key hat, wenn ja, dann Action-Variable erstellen und zuweisen, sonst nix machen (oder halt Fehlermeldung bringen...was man halt will)

    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell

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

    Also ich hab jetzt mal herausgefunden, dass man Code im eig. Code kompilieren u ausführen kann.
    Meine Idee wäre jetzt das du den Konsoleninput, damit kompilierst u ausführst.
    Es gibt aber hierbei dann ein großes Sicherheitsproblem da man ja jeden Code einfach eingeben u. ausführen kann.
    ​Smartnotr - ein intelligentes Notizprogramm
    zum Thread

    @Mokki wärst du auch so freundlich dein wissen mit uns zu teilen?

    @Te: wie bereits EDR sagte, und man darf davon ausgehen, dass er sich damit auskennt und dir keinen krampf erzählt, halte dich an 1.) einfach zu verwaltende und 2.) oop technisch einfache lösungen, wiiie:
    Dictionary!!

    Hab dir eh fast ne Musterlösung gegeben, brauchst nur code kopieren, überflüssige zuweisungseinträge rauswerfen und in der init methode deine gewollten methoden hinzufügen. Fertig.

    lg Radinator
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    @Radinator : Ich hab mich vor nem Jahr damit näher beschäftig....
    Hab jetzt mal auf Englisch und in c# nach c# execute a string as code gesucht. Man findet einfach mehr.
    Bin dann hierauf gestoßen: stackoverflow.com/questions/15…-execute-a-string-as-code
    Hab jetzt die Antwort nach VB übersetzt: stackoverflow.com/a/1511396

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. Dim t As Type = Me.GetType()
    3. Dim method As MethodInfo = t.GetMethod("showMessage")
    4. method.Invoke(Me, Nothing)
    5. End Sub
    6. Sub showMessage()
    7. MessageBox.Show("Hallo ich bin eine Messagebox")
    8. End Sub

    Edit: Des beste ist das dadurch kein Code ausgeführt werden kann der nicht im Programm integriert ist.
    ​Smartnotr - ein intelligentes Notizprogramm
    zum Thread

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

    @ThuCommix:
    Falls ich da iwo falsch liege, korrigiere mich, aber was willst du da mit Interfaces? Der TE will ja anhand von nem bestimmten Input eine funktion/sub/methode aufrufen. Wie gefragt: was bringt dir da ein interface?
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell