| Autor |
Beitrag |
furunkulus
Hält's aus hier
Beiträge: 4
|
Verfasst: Do 22.12.11 14:05
Hallo liebe C#Sharp Gemeinde,
ich kann mittels Reflection wunderbar auf Objekte einer Klasse zugreifen. Leider bekomme ich es aber nicht hin, dass ich auf z. B. Variablen innerhalb einer Methode zugreifen kann. Mein Beispiel:
Klasse:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
| public class cDummy { public static int nClassVar = 100; public int nInstanzVar = 200; public string sInstanzString = "ABC"; public bool TestFunc(int nZahl, out string sZeichen) { sZeichen = ""; string s1, s2, s3; if (nZahl > 100) return false; else { s1 = "x"; s2 = "y"; s3 = "z"; sZeichen = s1 + s2 + s3; } return true; } } |
Zugriff auf Klassenvariable funktioniert:
C#-Quelltext 1: 2: 3: 4:
| StackTrace stack = new StackTrace(); Type T = stack.GetFrame(1).GetMethod().DeclaringType; FieldInfo fiField = T.GetField("sInstanzString", BindingFlags.Instance | BindingFlags.NonPublic); fiField.GetValue... |
Aber wie bekomme ich die Felder s1, s2 und s3 innerhalb von TestFunc ??
Danke,
LG Michi Moderiert von Th69: Topic aus WinForms verschoben am Do 22.12.2011 um 14:48
|
|
Yogu
      
Beiträge: 2598
Erhaltene Danke: 156
Ubuntu 13.04, Win 7
C# (VS 2013)
|
Verfasst: Do 22.12.11 14:25
furunkulus hat folgendes geschrieben : | | Aber wie bekomme ich die Felder s1, s2 und s3 innerhalb von TestFunc ?? |
Gar nicht. Die Namen dieser Variablen (es sind keine Felder!) sind in der Exe-Datei gar nicht mehr vorhanden. Außerdem kann da auch ordentlich optimiert werden, sodass sie am Ende gar nicht mehr in der Form existieren, in der du sie verwendet hast.
Wegen was willst du denn auf die lokalen Variablen zugreifen? Vielleicht findet sich eine andere Lösung.
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Do 22.12.11 14:34
Über die MethodInfo (stack.GetFrame(1).GetMethod()) kannst du dir per GetMethodBody().LocalVariables die lokalen Variablen ranholen. Der IL Code enthält aber keine Namen mehr für lokale Variablen so das du irgendwie anders rausfinden mußt welcher Eintrag der LocalVariables Liste sich auf welche Variable in deinem Sourcecode bezog. Den aktuellen Wert der lokalen Variablen müßtest du dir dann noch über irgendwelche anderen Tricks aus dem Speicher klauben. Reflection wird da AFAIK nicht weiterhelfen.
Versuchst du einen Debugger zu schreiben? Dann wäre wohl eher die Debug Interfaces zum debuggen von .Net Code interessant. Dann bewegen wir uns aber in der unamnaged Welt und du müßtest deinen Debugger in z.b. klassischem C++ implementieren.
|
|
furunkulus 
Hält's aus hier
Beiträge: 4
|
Verfasst: Do 22.12.11 14:49
Hallo, also erstmal vielen Dank für die superschnellen Antworten, das macht Spaß!
Mit Reflection komme ich hier also nicht weiter. Ich erkläre mal das Problem, vielleicht hat jemand eine andere Idee die zur Lösung beitragen könnte.
Wir stellen eine mit Gupta (Unify) Team Developer entwickelte Software auf C# um. Für diese Applikation sind gut 1000 Sql Statements in einem File hinterlegt und diese sehen für TeamDeveloper so (oder ähnlich) aus:
"SELECT a, b, c FROM tabelle INTO :sA, :sB, :sC WHERE idx = :nIdx"
Binds und Into Variablen werden direkt im StatementString mitgegeben und bei Prepare bzw. Fetch ausgewertet oder befüllt.
Damit wir dieses Statements nicht überarbeiten müssen, möchte ich in C# etwas ähnliches anbieten. Das funktioniert auch mittels Reflection, allerdings eben nicht wenn das Statement innerhalb einer Methode ausgeführt wird und dabei lokale Variablen verwendet werden.
Also entweder es werden alle verwendeten Variablen als Klassenvariablen definiert, oder..... ??? jemand eine andere Idee ??.
Danke,
LG Michi
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Do 22.12.11 15:03
| Zitat: | Also entweder es werden alle verwendeten Variablen als Klassenvariablen definiert, oder..... ??? jemand eine andere Idee ??.
|
Zum Beispiel einen Post-Compiler schreiben der während des Kompilierens anhand der SqlStatement Parameter erstellt die Werte lokaler Variablen diesen Parametern zuweist und dann in den generierten IL Code injected. Vielleicht kann man da mit fertigen Frameworks wie PostSharp ein paar passende Attribute basteln. Ansonsten habe ich leider, außer das ich Bennenen kann was man tun müsste, keine konkreten Erfahrungen.
|
|
Yogu
      
Beiträge: 2598
Erhaltene Danke: 156
Ubuntu 13.04, Win 7
C# (VS 2013)
|
Verfasst: Do 22.12.11 15:24
Wenn ich das richtig verstanden habe, dürften Dynamics interessant für dich sein. Objekte dieses Typs können beliebige Eigenschaften haben.
Zunächst musst du dir eine Klasse designen, die ein Dictionay zum Speichern der Weret enthält und einen dynamischen Zugriff auf diese bietet. Die Klasse habe ich aus dem Abschnitt Examples der DynamicObject-Seite des MSDN genommen und etwas angepasst.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34:
| public class DynamicDictionary : DynamicObject { Dictionary<string, object> dictionary = new Dictionary<string, object>();
public Dictionary<string, object> Dictionary { get { return dictionary; } }
public override bool TryGetMember( GetMemberBinder binder, out object result) { string name = binder.Name.ToLower();
return dictionary.TryGetValue(name, out result); }
public override bool TrySetMember( SetMemberBinder binder, object value) { dictionary[binder.Name.ToLower()] = value;
return true; } } |
Dann könnte die Query-Methode in etwa so aussehen:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7:
| void executeQuery(dynamic data, string sql) { int id = data.Dictionary["nIdx"]; data.Dictionary["sA"] = "wert1"; data.Dictionary["sB"] = 123; data.Dictionary["sC"] = 23.5; return result; } |
Und der Aufruf denkbar einfach:
C#-Quelltext 1: 2: 3: 4:
| dynamic data = new DynamicDictionary(); data.nIdx = 123; executeQuery(data, "SELECT a, b, c FROM tabelle INTO :sA, :sB, :sC WHERE idx = :nIdx"); MessageBox.Show(data.sA); |
Typsicherheit ist damit natürlich nicht mehr gegeben, aber das wäre sie bei direkten Zugriff auf die Variablen auch nicht wirklich, da der SQL-String ja erst zur Laufzeit aufgerufen wird.
|
|
furunkulus 
Hält's aus hier
Beiträge: 4
|
Verfasst: Di 27.12.11 13:01
Vielen Dank für die Hilfe! Ich möchte aber eigentlich nicht mit einem Dictionary arbeiten, denn dann muss ich (oder der betreffende Programmierer) nach dem Read jeweils die Intos extra abrufen. Das sollte eben in meiner Read Methode automatisch passieren.
Wir haben uns jetzt mal darauf geeinigt, dass die INTO und BIND Felder als Klassenobjekte definiert sind und ich somit über Reflection darauf zugreifen kann. Leider habe ich jetzt aber bei der FieldInfo SetValue Methode noch folgendes Problem:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| static Type[] TParentClass = new Type[3]; StackTrace stack = new StackTrace(); TParentClass[nSession] = stack.GetFrame(1).GetMethod().DeclaringType;
private bool SetIntoValue(string sInto, object objValue) { FieldInfo fiField = TParentClass[nSession].GetField(sInto, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); if (fiField == null) { MessageBox.Show("INTO-ERROR: " + sInto + " not found!\r\nPlease declare as Class Member." + "\r\n\r\nClass: " + TParentClass[nSession].Name + "\r\n\r\n" + sOrgStmt, "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return false; } fiField.SetValue(TParentClass[nSession], objValue); return true; } |
Wenn ich statt
fiField.SetValue(TParentClass[nSession], objValue);
fiField.SetValue(cAPP, objValue); // cAPP ist die Instanz der Klasse in der das Statement ausgeführt wurde
verwende, dann funktioniert es.
Ich kann die Klasse aber nicht fix hinterlegen, denn das Statement kann ja von überall ausgeführt worden sein.
Meine Frage also:
Wie mache ich aus dem Type TParentClass[nSession] das Objekt der aufrufenden Klasse?
Also z. B. (object)TParentClass[nSession] oder TParentClass[nSession].GUID.xxx oder so ähnlich?
Sorry, aber jetzt steigt mein Hirn aus.
Liebe Grüße,
Furunkulus
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Di 27.12.11 14:44
| Zitat: | | Wie mache ich aus dem Type TParentClass[nSession] das Objekt der aufrufenden Klasse? |
So rum gar nicht. Und die Stacktrace/Stackframe Klassen liefern dir auch keine Unterstützung dafür. Vielleicht kannst du den impliziten this Pointer der als Parameter in die Methoden geht irgendwie vom Stack abgreifen.
[Rant]Sorry aber was du das tust wiederspricht gefühlt allem wofür C# steht. Wenn du ein coding by convention-System möchtest solltest du eine dies unterstützende Sprache/System wählen. Was du da gerade schraubst, also C# so umzubauen das es so funktioniert wie das bisherige Gupta System, wird im Extremfall nur zu einem führen nämlich das man sowohl Gupta als auch C# verstehen muss um deine Software zu begreifen. Wenn ihr schon eine Migration wagt solltet ihr auch die entsprechenden Konzepte wagen die von Programmierern verstanden werden die C# programmieren. Hast du übrigens mal versucht deinen Code mit Stackframe/Stacktrace auf irgendein anderes System zu deployen? Das muss doch jetzt schon wahrscheinlich zwingend ein Debug Build sein damit das funktioniert oder? Ich würde mich nicht wundern wenn du bald auch noch zwingend die pdb Files brauchst wenn du den Weg weiter gehst. Kurzfristig mag sich das was du versuchst nett anhören mittelfristig erwarte ich aber deutlich mehr Problem als es jetzt Aufwand bedeutet einmal richtig die Software ins jetzt zu heben.[/Rant]
|
|
Th69
      

Beiträge: 4805
Erhaltene Danke: 1061
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Di 27.12.11 15:06
Hallo furunkulus,
über den StackTrace erhältst du nur die Typen, nicht aber Instanzen (Objekte).
Der einzige Weg ist, daß die Methode selber das Objekt (d.h. sich selbst, also this) mit übergibt,
s.a. stackoverflow.com/qu...-calling-object-in-c sowie stackoverflow.com/qu...flection-diagnostics
Ansonsten kann ich Ralf Jansen bzgl. seines rant nur zustimmen!
Laß Gupta Gupta sein und programmiere entsprechend den C# und .NET Vorgehensweisen.
|
|
furunkulus 
Hält's aus hier
Beiträge: 4
|
Verfasst: Di 27.12.11 16:37
OK Leute, vielen Dank!
Ich werde das Objekt mit this übergeben - es geht nur mehr um die vielen gespeicherten SQL Statements. Ansonsten halte ich mich schon an C# - macht auch deutlich mehr Spass.
Vielen Dank für euere echt guten Ratschläge.
LG Furunkulus
|
|
|