After answering three more questions today on how to ‘dynamically’ access a property or control on a form I thought I’d post my Reflection helpers again. I’ve put these into most articles I’ve published, but this way they are easily searchable and pointable for future reference.
Reflection is .Net’s mechanism to iterate and access members of any type. With it you can dynamically walk through an object at runtime and parse an object to retrieve or assign a list of values. You can also use it read or set a value of a member.
Since .Net is strongly typed Reflection is frequently required when you don’t know what specific type you’re dealing with at compile time. For example, if you dynamically generate controls or objects at runtime, or you use metabased programming where some of the data is dynamically created based on data stored in a database, XML or other store you often create things on the fly. Reflection can then be used against these ‘dynamic’ runtime generated objects which the compiler knows nothing about.
The most common question I see is something along the lines of this:
I need to dynamically assign a value to a TextBox at runtime, but I don’t know the name of the TextBox at compile time:
public const BindingFlags MemberAccess =
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Static | BindingFlags.Instance | BindingFlags.IgnoreCase ;
String MyTextBoxName = "txtCompany";
String ValueToSet = "NewValue";
// *** Get a reference to the TextBox
TextBox MyTextBox = (TextBox) this.GetType().GetField(MyTextBox,BindingFlags).GetValue(this,MyTextBoxName);
MyTextBox.GetType().GetProperty("Text",BindingFlags).SetValue(MyTextBox,ValueToSet,null);
Two things happen here: First we retrieve a reference to the TextBox and then we can assign the Text property of that new reference. The this. reference here references the form and txtCompany is a field member of that form. Note that you need to specify whether you’re dealing with a Field or Property explicitly using either GetField or GetProperty. All values are returned of type object so you will need to cast to the proper type (TextBox for example) when returning values.
You can spelunk around the various MemberInfo classes to see what else you can do to members – methods can be invoked dynamically for example to call MyTextBox.BindData(this,"Text") (a custom method) you’d use:
MyTextBox.GetType().InvokeMember("BindData",
MemberAccess | BindingFlags.InvokeMethod,
null,MyTextBox,
new object[] { this,"Text" });
So as you can see you can provide dynamic execution of code which provides EVALUATE() like functionality.
To make things easier I created a set of wrappers for this sort of thing that provides access to Set/Get properties and fields and call methods and to provide the ability to automatically drill into multiple levels. With the wrappers the above code can be reduced to:
wwUtils.SetPropertyEx(this,MyTextBox + ".Text",ValueToSet);
and
string Result = (string) wwUtils.CallMethod(MyTextBox,"BindData",this,"Text");
The Ex versions of the methods drill into child members (.Text here) so you don’t have to get a reference to each object individually, which saves a lot of code if you have complex object hierarchies. However, for performance reasons you may still want to get a reference to any intermediate objects.
Ok, here’s the code for the various Reflection helpers:
using System.Reflection
…
#region Reflection Helper Code
///
/// Binding Flags constant to be reused for all Reflection access methods.
///
public const BindingFlags MemberAccess =
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Static | BindingFlags.Instance | BindingFlags.IgnoreCase ;
///
/// Retrieve a dynamic 'non-typelib' property
///
/// Object to make the call on
/// Property to retrieve
///
public static object GetProperty(object Object,string Property)
{
return Object.GetType().GetProperty(Property,wwUtils.MemberAccess).GetValue(Object,null);
}
///
/// Retrieve a dynamic 'non-typelib' field
///
/// Object to retreve Field from
/// name of the field to retrieve
///
public static object GetField(object Object,string Property)
{
return Object.GetType().GetField(Property,wwUtils.MemberAccess).GetValue(Object);
}
///
/// Returns a property or field value using a base object and sub members including . syntax.
/// For example, you can access: this.oCustomer.oData.Company with (this,"oCustomer.oData.Company")
///
/// Parent object to 'start' parsing from.
/// The property to retrieve. Example: 'oBus.oData.Company'
///
public static object GetPropertyEx(object Parent, string Property)
{
MemberInfo Member = null;
Type Type = Parent.GetType();
int lnAt = Property.IndexOf(".");
if ( lnAt < 0)
{
if (Property == "this" || Property == "me")
return Parent;
// *** Get the member
Member = Type.GetMember(Property,wwUtils.MemberAccess)[0];
if (Member.MemberType == MemberTypes.Property )
return ((PropertyInfo) Member).GetValue(Parent,null);
else
return ((FieldInfo) Member).GetValue(Parent);
}
// *** Walk the . syntax - split into current object (Main) and further parsed objects (Subs)
string Main = Property.Substring(0,lnAt);
string Subs = Property.Substring(lnAt+1);
// *** Retrieve the current property
Member = Type.GetMember(Main,wwUtils.MemberAccess)[0];
object Sub;
if (Member.MemberType == MemberTypes.Property )
{
// *** Get its value
Sub = ((PropertyInfo) Member).GetValue(Parent,null);
}
else
{
Sub = ( (FieldInfo) Member).GetValue(Parent);
}
// *** Recurse further into the sub-properties (Subs)
return wwUtils.GetPropertyEx(Sub,Subs);
}
///
/// Sets the property on an object.
///
/// Object to set property on
/// Name of the property to set
/// value to set it to
public static void SetProperty(object Object,string Property,object Value)
{
Object.GetType().GetProperty(Property,wwUtils.MemberAccess).SetValue(Object,Value,null);
}
///
/// Sets the field on an object.
///
/// Object to set property on
/// Name of the field to set
/// value to set it to
public static void SetField(object Object,string Property,object Value)
{
Object.GetType().GetField(Property,wwUtils.MemberAccess).SetValue(Object,Value);
}
///
/// Sets the value of a field or property via Reflection. This method alws
/// for using '.' syntax to specify objects multiple levels down.
///
/// wwUtils.SetPropertyEx(this,"Invoice.LineItemsCount",10)
///
/// which would be equivalent of:
///
/// this.Invoice.LineItemsCount = 10;
///
///
/// Object to set the property on.
///
///
/// Property to set. Can be an object hierarchy with . syntax.
///
///
/// Value to set the property to
///
public static object SetPropertyEx(object Parent, string Property,object Value)
{
Type Type = Parent.GetType();
MemberInfo Member = null;
// *** no more .s - we got our final object
int lnAt = Property.IndexOf(".");
if ( lnAt < 0)
{
Member = Type.GetMember(Property,wwUtils.MemberAccess)[0];
if ( Member.MemberType == MemberTypes.Property )
{
((PropertyInfo) Member).SetValue(Parent,Value,null);
return null;
}
else
{
((FieldInfo) Member).SetValue(Parent,Value);
return null;
}
}
// *** Walk the . syntax
string Main = Property.Substring(0,lnAt);
string Subs = Property.Substring(lnAt+1);
Member = Type.GetMember(Main,wwUtils.MemberAccess)[0];
object Sub;
if (Member.MemberType == MemberTypes.Property)
Sub = ((PropertyInfo) Member).GetValue(Parent,null);
else
Sub = ((FieldInfo) Member).GetValue(Parent);
// *** Recurse until we get the lowest ref
SetPropertyEx(Sub,Subs,Value);
return null;
}
///
/// Wrapper method to call a 'dynamic' (non-typelib) method
/// on a COM object
///
///
/// 1st - Method name, 2nd - 1st parameter, 3rd - 2nd parm etc.
///
public static object CallMethod(object Object,string Method, params object[] Params)
{
return Object.GetType().InvokeMember(Method,wwUtils.MemberAccess | BindingFlags.InvokeMethod,null,Object,Params);
}
Note that Reflection doesn't provide for dynamic code block execution - it can only execute specific members one at a time. However, you can use the .Net CodeDom assemblies to compile code on the fly and run it dynamically. I wrote about this a while back in this article.