===== Strom zobrazující strukturu grafickým prvků našeho okna =====
Třída **ReflectionModule** zobrazuje typové informace ve stromu.
Hlavní okno zavolá konstruktor s parametrem TreeView a třemi dalšími tabulky pro zobrazení detailů.
reflectionModule = new ReflectionModule (structureTree, propGrid, reflectionGrid, typeGrid);
Později z hlavního okna zavoláme funkci **display**, jako parametr předáme odkaz na hlavní okno
reflectionModule.display (this);
Funkce //display// zavolá metodu **addStructure**
* první parametr **target** určuje místo ve stromu kam bude přidána nová položka
* druhý parametr **obj** je odkaz na zobrazovaný objekt
* vytvoříme nový uzel stromu **node**
* do **node.Tag** si uložíme odlaz na objekt
* jméno typu objektu získáme pomocí **obj.GetType().ToString()**
* pokud je objekt odvozen ze třídy **Control**
* přetypujeme objekt na Control, **Control c = obj as Control**
* vnořené grafické prvky získáme pomocí **c.Controls**
* rekurzivně zavoláme //addStructure// pro zobrazení vnořených prvků
* funkce addAttributes popíšeme později, přidá do stromu informace o atributech
* **target.Add (node)** přidá prvek do stromu
private void addStructure (TreeNodeCollection target, object obj)
{
TreeNode node = new TreeNode ();
node.Tag = obj;
string typ = obj.GetType().ToString();
if (obj is Control)
{
Control c = obj as Control;
node.Text = c.Name + " : " + typ;
foreach (Control item in c.Controls)
addStructure (node.Nodes, item);
}
else
{
node.Text = obj.ToString () + " : " + typ;
}
addAttributes (node.Nodes, obj);
target.Add (node);
}
===== Tabulka vlasností jednoho objektu =====
Funkce zavolaná po změně právě atkivního prvku ve stromu
* vyzvedne odkaz na objekt **object obj = e.Node.Tag**
* získá popis typu daného objektu **Type typ = obj.GetType ()**
* na záložce //Properties// v tabulce **propGrid** typu **PropertyGrid** zobrazí vlastnosti objektu
* na záložce //Type Info// zobrazí typové informace
* na záložce //Reflection// se v tabulce **reflectionGrid** typu **DataGridView** pokusíme sami zobrazit vlastnosti objektu
* **typ.GetProperties ()** nám poskytne pole s prvky typu **PropertyInfo** obsahující popisy jednotlivých vlastností
* ke každé vlastnosti, jejíř popis máme v proměnná **prop**, můžeme získat jméno **prop.Name**
* pokud je vlastnost možné číst ( if (**prop.CanRead**) )
* hodnotu uloženou jako obecný typ **object** přečteme pomocí **prop.GetValue (obj, null)**
* **prop** je popis vlastnosti
* **obj** je objekt ze kterého vlastnost čteme
* vytvoříme pole objektů představující jednu řádku tabulky: **object [] line = new object [] {name, value}; **
* řádku přidáme do takulky: **reflectionGrid.Rows.Add (line);**
public void reflectionTree_AfterSelect (object sender, TreeViewEventArgs e)
{
object obj = e.Node.Tag;
Type typ = obj.GetType ();
propGrid.SelectedObject = obj;
typeGrid.SelectedObject = obj.GetType ().GetTypeInfo();
foreach (PropertyInfo prop in typ.GetProperties ())
{
string name = prop.Name;
object value = null;
if (prop.CanRead)
{
value = prop.GetValue (obj, null); /* second parameter required from .Net 4.5 */
}
object [] line = new object [] {name, value};
reflectionGrid.Rows.Add (line);
}
}
===== Atributy =====
Hlavnímu oknu jsem přidal několik //atributů// **Description**, které chci pomocí typových informací najít a zobrazit.
Reálným použití je například označení metod, které se objeví v kontextovém menu
[[https://gitlab.fjfi.cvut.cz/culikzde/pw-sharpdevelop/-/tree/master/Attr|příklad na gitlab]]
[Description ("Main Window")]
public partial class TypeInfo : Form
{
[Description ("Compilation")]
private CodeModule codeModule;
[Description ("Reflection")]
private ReflectionModule reflectionModule;
[Description ("Open file")]
private void open_Click (...) { }
[Description ("Save file")]
private void save_Click (...) { }
}
===== Zobrazení atributů ve stromu =====
V zadaném objektu vyhledáme atribut (nebo několik atributů) typu **DescriptionAttribute**, \\
které jsme zadali pomocí **[Description ("...")]** před deklarací třídy \\
a zobrazíme je jako //modré// položky ve stromu
* **obj** - objekt ve kterém hledáme atributy
* **type** - popis typu zadaného objektu
* **type.GetCustomAttributes ()** pole s atributy **Description**
private void addAttributes (TreeNodeCollection target, object obj)
{
Type type = obj.GetType ();
foreach (DescriptionAttribute attr in type.GetCustomAttributes ())
{
TreeNode node = new TreeNode ();
node.Tag = attr;
node.Text = attr.Description;
node.ForeColor = Color.CornflowerBlue;
target.Add (node);
}
Nebo můžeme zobrazit všechny atributy zadaného objektu
foreach (Attribute attr in type.GetCustomAttributes ())
{
TreeNode node = new TreeNode ();
node.Tag = attr;
node.Text = attr.ToString ();
node.ForeColor = Color.CornflowerBlue;
target.Add (node);
}
Zobrazíme atributy, které se váží k jednotlivým metodám.
* **type.GetRuntimeMethods ()** poskytne pole popisů jednotlivých metod v daném typu
* popis jedné metody **meth** je typu **MethodInfo**
* seznam **Description** attributů získáme pomocí **meth.GetCustomAttributes ()**
* zobrazíme je jako //oranžové// prvky stromu
foreach (MethodInfo meth in type.GetRuntimeMethods ())
{
foreach (DescriptionAttribute attr in meth.GetCustomAttributes ())
{
TreeNode node = new TreeNode ();
node.Tag = attr;
node.Text = meth.Name + " : " + attr.Description;
node.ForeColor = Color.Orange;
target.Add (node);
}
}
Podobně zobrazíme atributy jednotlivých proměnných.
* **type.GetRuntimeFields ()** poskytne pole popisů jednotlivých proměnných
* popis jedné proměnné **field** je typu **FieldInfo**
* seznam **Description** attributů získáme pomocí **field.GetCustomAttributes ()**
* zobrazíme je jako //zelené// prvky stromu
foreach (FieldInfo field in type.GetRuntimeFields ())
{
var list = field.GetCustomAttributes ();
foreach (DescriptionAttribute attr in list)
{
TreeNode node = new TreeNode ();
node.Tag = attr;
node.Text = field.Name + " : " + attr.Description;
node.ForeColor = Color.Lime;
target.Add (node);
}
}
}
===== Obrázek našeho programu =====
Obrázek našeho programu se stromem, který zobrazuje strukturu hlavního okna.
V dolní části stromu jsou barevně zobrazeny **Description** //attributy// našeho okna.
Na záložce //Properties// naleznete vlastnosti zobrazené pomocí //PropertyGrid//.
Na záložce //Reflection// jsou vlastnosti zobrazené naším programem.
{{typeinfo.png}}
===== Třída ReflectionModule =====
public class ReflectionModule
{
private TreeView reflectionTree;
private PropertyGrid propGrid;
private DataGridView reflectionGrid;
private PropertyGrid typeGrid;
public ReflectionModule (TreeView reflectionTree0,
PropertyGrid propGrid0,
DataGridView reflectionGrid0,
PropertyGrid typeGrid0)
{
reflectionTree = reflectionTree0;
propGrid = propGrid0;
reflectionGrid = reflectionGrid0;
typeGrid = typeGrid0;
reflectionTree.AfterSelect += reflectionTree_AfterSelect;
}
public void display (object obj)
{
addStructure (reflectionTree.Nodes, obj);
}
private void addStructure (TreeNodeCollection target, object obj)
{
TreeNode node = new TreeNode ();
node.Tag = obj;
string typ = obj.GetType().ToString();
if (obj is Control)
{
Control c = obj as Control;
node.Text = c.Name + " : " + typ;
foreach (Control item in c.Controls)
addStructure (node.Nodes, item);
}
else
{
node.Text = obj.ToString () + " : " + typ;
}
addAttributes (node.Nodes, obj);
target.Add (node);
}
private void addAttributes (TreeNodeCollection target, object obj)
{
Type type = obj.GetType ();
/*
foreach (Attribute attr in type.GetCustomAttributes ())
{
TreeNode node = new TreeNode ();
node.Tag = attr;
node.Text = attr.ToString ();
node.ForeColor = Color.CornflowerBlue;
target.Add (node);
}
*/
foreach (DescriptionAttribute attr in type.GetCustomAttributes ())
{
TreeNode node = new TreeNode ();
node.Tag = attr;
node.Text = attr.Description;
node.ForeColor = Color.CornflowerBlue;
target.Add (node);
}
foreach (MethodInfo meth in type.GetRuntimeMethods ())
{
foreach (DescriptionAttribute attr in meth.GetCustomAttributes ())
{
TreeNode node = new TreeNode ();
node.Tag = attr;
node.Text = meth.Name + " : " + attr.Description;
node.ForeColor = Color.Orange;
target.Add (node);
}
}
foreach (FieldInfo field in type.GetRuntimeFields ())
{
var list = field.GetCustomAttributes ();
foreach (DescriptionAttribute attr in list)
{
TreeNode node = new TreeNode ();
node.Tag = attr;
node.Text = field.Name + " : " + attr.Description;
node.ForeColor = Color.Lime;
target.Add (node);
}
}
}
public void reflectionTree_AfterSelect (object sender, TreeViewEventArgs e)
{
object obj = e.Node.Tag;
Type typ = obj.GetType ();
propGrid.SelectedObject = obj;
typeGrid.SelectedObject = obj.GetType ().GetTypeInfo();
foreach (PropertyInfo prop in typ.GetProperties ())
{
string name = prop.Name;
object value = null;
if (prop.CanRead)
{
value = prop.GetValue (obj, null); /* second parameter required from .Net 4.5 */
}
object [] line = new object [] {name, value};
reflectionGrid.Rows.Add (line);
}
}
} // end of class
===== Hlavní okno =====
[Description ("Main Window")]
public partial class TypeInfo : Form
{
[Description ("Compilation")]
private CodeModule codeModule;
[Description ("Reflection")]
private ReflectionModule reflectionModule;
public TypeInfo ()
{
InitializeComponent ();
codeModule = new CodeModule (codeTree, propGrid);
reflectionModule = new ReflectionModule (structureTree, propGrid, reflectionGrid, typeGrid);
viewReflection_Click (null, null);
}
[Description ("Open file")]
private void open_Click (object sender, EventArgs e)
{
if (openDialog.ShowDialog () == DialogResult.OK)
{
editor.Lines = File.ReadAllLines (openDialog.FileName);
}
}
[Description ("Save file")]
private void save_Click (object sender, EventArgs e)
{
if (saveDialog.ShowDialog () == DialogResult.OK)
{
File.WriteAllLines (saveDialog.FileName, editor.Lines);
}
}
private void quit_Click(object sender , EventArgs e)
{
Close ();
}
/* Reflection */
void viewReflection_Click (object sender, EventArgs e)
{
leftTabs.SelectedTab = structureTab;
rightTabs.SelectedTab = reflectionTab;
structureTree.Nodes.Clear ();
reflectionModule.display (this);
structureTree.SelectedNode = structureTree.Nodes[0];
structureTree.Nodes[0].Expand ();
}
/* Compilation */
void compile_Click (object sender, EventArgs e)
{
leftTabs.SelectedTab = codeTab;
centralTabs.SelectedTab = editorTab;
info.Clear ();
codeModule.compile (editor, info);
}
} // end of class
===== Kompilace zdrojového textu ======
Na http://stackoverflow.com jsem našel příklad jak zkompilovat zdrojový text,
který máme uložený jen v proměnné typu **string**.
Jako zdrojový text použijeme text z editačního okénka a umístíme do jendoprvkového pole **code**.
Pomocí následnujícho kódu nám knihovny //platformy .NET// přeloží zdrojový text a výsledek uloží jen do paměti.
public void compile (TextBox editor, TextBox info)
{
string[] code = { editor.Text };
CompilerParameters CompilerParams = new CompilerParameters ();
CompilerParams.GenerateInMemory = true;
CompilerParams.TreatWarningsAsErrors = false;
CompilerParams.GenerateExecutable = false;
CompilerParams.CompilerOptions = "/optimize";
string[] references = { "System.dll", "System.Data.dll" };
CompilerParams.ReferencedAssemblies.AddRange (references);
CSharpCodeProvider provider = new CSharpCodeProvider ();
CompilerResults compile = provider.CompileAssemblyFromSource (CompilerParams, code);
Pokud během překladu dojde k chybám zobrazíme zprávy v dolním okénku **info**
if (compile.Errors.HasErrors)
{
foreach (CompilerError ce in compile.Errors)
{
info.AppendText (ce.ToString () + "\r\n");
}
}
Pokud se překlad povede zobrazí naše funkce **displayAssembly** informace o přeložené programu ve stromu.
V editoru je přednastaven následující zdrojový text
namespace CodeFromFile
{
public class CodeFromFile
{
static public int Add(int a ,int b)
{
return a+b;
}
}
}
Pokusíme zavolat funkci **Add** s parametry **5** a **10**
* do proměnné **module** uložíme první modul z přeloženého //assembly//
* do **mt** popis přeložené třídy **CodeFromFile**
* do **meth** popis metody **Add**
* funkci //Add// zavoláme pomocí ** meth.Invoke (null, new object[] { 5, 10 }) **
* funkce //Add// je statická, tak instanci objektu zadáme jako **null**
* pole parametrů bude ** new object[] { 5, 10 }**
* o výsledku přepokládáme, že bude celé číslo
* výsledek zobrazíme v dolní části okna
if (compile.Errors.HasErrors)
{ ... }
else
{
displayAssembly (compile.CompiledAssembly);
Module module = compile.CompiledAssembly.GetModules ()[0];
Type mt = null;
MethodInfo meth = null;
if (module != null)
mt = module.GetType ("CodeFromFile.CodeFromFile");
if (mt != null)
meth = mt.GetMethod ("Add");
if (meth != null)
{
int result = (int)meth.Invoke (null, new object[] { 5, 10 });
info.AppendText ("result = " + result);
}
}
}
Funkce **displayAssembly**, **displayModule**, **displayType**, **displayMember**, ... zobrazí ve stromu informace o přeloženém kódu
public class CodeModule
{
private TreeView codeTree;
private PropertyGrid propGrid;
public CodeModule (TreeView codeTree0, PropertyGrid propGrid0)
{
codeTree = codeTree0;
propGrid = propGrid0;
codeTree.NodeMouseClick += codeTree_NodeMouseClick;
}
public void displayAssembly (Assembly a)
{
codeTree.Nodes.Clear ();
foreach (Module m in a.GetModules ())
displayModule (codeTree.Nodes, m);
}
private void displayModule (TreeNodeCollection target, Module m)
{
TreeNode node = new TreeNode();
node.Text = "module " + m.Name;
node.Tag = m;
foreach (Type t in m.GetTypes ())
displayType (node, t);
target.Add (node);
}
private void displayType (TreeNode target, Type t)
{
TreeNode node = new TreeNode();
node.Text = "type " + t.Name;
node.Tag = t;
foreach (MemberInfo m in t.GetMembers ())
displayMember (node, m);
target.Nodes.Add (node);
}
private void displayMember (TreeNode target, MemberInfo m)
{
TreeNode node = new TreeNode();
node.Text = m.Name;
node.Tag = m;
if (m is MethodBase)
{
MethodBase b = m as MethodBase;
foreach (ParameterInfo p in b.GetParameters ())
displayParameter (node, p);
}
if (m is MethodInfo)
{
displayResult (node, m as MethodInfo);
}
target.Nodes.Add (node);
}
private void displayParameter (TreeNode target, ParameterInfo p)
{
TreeNode node = new TreeNode();
node.Text = p.Name + " : " + p.ParameterType.ToString ();
node.Tag = p;
target.Nodes.Add (node);
}
private void displayResult (TreeNode target, MethodInfo m)
{
TreeNode node = new TreeNode();
node.Text = "result " + m.ReturnType.ToString ();
target.Nodes.Add (node);
}
private void codeTree_NodeMouseClick (object sender, TreeNodeMouseClickEventArgs e)
{
propGrid.SelectedObject = e.Node.Tag;
}
/* code from stackoverflow.com */
public void compile (TextBox editor, TextBox info)
{
string[] code = { editor.Text };
CompilerParameters CompilerParams = new CompilerParameters ();
CompilerParams.GenerateInMemory = true;
CompilerParams.TreatWarningsAsErrors = false;
CompilerParams.GenerateExecutable = false;
CompilerParams.CompilerOptions = "/optimize";
string[] references = { "System.dll", "System.Data.dll" };
CompilerParams.ReferencedAssemblies.AddRange (references);
CSharpCodeProvider provider = new CSharpCodeProvider ();
CompilerResults compile = provider.CompileAssemblyFromSource (CompilerParams, code);
if (compile.Errors.HasErrors)
{
string text = "Compile error: ";
foreach (CompilerError ce in compile.Errors)
{
info.AppendText (ce.ToString () + "\r\n");
text += "\r\n" + ce.ToString ();
}
// throw new Exception (text);
}
else
{
displayAssembly (compile.CompiledAssembly);
Module module = compile.CompiledAssembly.GetModules ()[0];
Type mt = null;
MethodInfo meth = null;
if (module != null)
mt = module.GetType ("CodeFromFile.CodeFromFile");
if (mt != null)
meth = mt.GetMethod ("Add");
if (meth != null)
{
int result = (int)meth.Invoke (null, new object[] { 5, 10 });
info.AppendText ("result = " + result);
}
}
}
} // end of class
{{typeinfo_compile.png}}
[[https://gitlab.fjfi.cvut.cz/culikzde/pw/-/tree/master/TypeInfo|gitlab]]
[[http://kmlinux.fjfi.cvut.cz/~culikzde/pw/TypeInfo2020.zip|zip]]