Entwickler-Ecke
Basistechnologien - Reflection -> Zugriff auf Methode innerhalb einer Klasse
furunkulus - Do 22.12.11 15:05
Titel: Reflection -> Zugriff auf Methode innerhalb einer Klasse
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
furunkulus - Do 22.12.11 15: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 - Do 22.12.11 16: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 [
http://www.sharpcrafters.com/] ein paar passende Attribute basteln. Ansonsten habe ich leider, außer das ich Bennenen kann was man tun müsste, keine konkreten Erfahrungen.
Yogu - Do 22.12.11 16: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 [
http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject.aspx#Examples] genommen und etwas angepasst.
C#-Quelltext
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 - Di 27.12.11 14: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 - Di 27.12.11 15: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]
furunkulus - Di 27.12.11 17: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
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!