Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Reflection to provide EVALUATE functionality


:P
On this page:

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.


The Voices of Reason


 

Rick Strahl's WebLog
April 11, 2004

# .Net Reflection and Performance

Reflection often gets a bad rap for being slow. True it's much slower than direct access, but it's important to look at performance in the proper perspective. For many operations Reflection and 'evaluative' access to properties, fields and methods provides flexibility that wouldn't otherwise be there and the speed hit you take, especially in non-iterative situations is only minmal.

Carlos Gutierrez
April 29, 2004

# re: Reflection to provide EVALUATE functionality

And a more Faster developtment.

Rick Strahl's WebLog
July 11, 2004

# Debugging a VFP COM object in place

A little trick on how you can debug VFP COM objects in a real application scenario by using the Visual FoxPro IDE Automation model to drive your code for debugging.

Jigar
March 27, 2006

# re: Reflection to provide EVALUATE functionality

What needs to be changed to use this class to modify indexed properties such as

string _property = "gaugeContainer1.CircularGauges[\"Default\"].Pointers[\"[Default\"].Value"

Thanks

Hugh McGregor
September 06, 2006

# re: Reflection to provide EVALUATE functionality

Okay, that is a very neat introduction into what refelction can do. How do you have to treat it when you do NOT know the field data type? As in this case:
System.Reflection.FieldInfo[] fi = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);

You just cannot do a cast. Any hints?

Rick Strahl
September 06, 2006

# re: Reflection to provide EVALUATE functionality

Jigar, doing index values is a little more complicated. I'll post the solution to that another time.

Hugh, you don't need to know the field datatype - it returns as object and you can continue to use Reflection on that object to read additional data. Assignments via Reflection work without strong typing so you can assign object and Reflection will figure out what the appropriate type to assign is.

If you don't know the type you don't know the type. Know what I mean <g>...

Rick Strahl's Web Log
October 15, 2006

# Strong Typing vs. Loose Typing - Rick Strahl's Web Log

Having worked extensively with FoxPro and .NET which are loosely and strongly typed respectively I can tell you that I have s strong preference for strong typing. However, some people seem to think that strong typing is too much work and a hassle.

# Ejecutar Contenido de Una Variable en VB.Net - microsoft.public.es.dotnet.aspnet | Grupos de Google


David Wendelken
August 03, 2007

# re: Reflection to provide EVALUATE functionality

Rick,

Without the wwUtils class, this example is unusable. Where do we find it?

David Wertman
August 10, 2007

# re: Reflection to provide EVALUATE functionality

Copy the copy Rick posted as "Ok, here's the code for the helper classes" and created a new class called wwutils.cs, paste it in, and you should be good to go.

db042188
September 05, 2007

# re: Reflection to provide EVALUATE functionality

I'm experimenting with the SetValue method. The data source is xml, the target is a pretty well known MS WCF binding protocol class.

My command looks like...

bindingObject.GetType().GetProperty(nodeIter.Current.LocalName).SetValue
(bindingObject, nodeIter.Current.Value, null);

which is great for targets of type string but not much of anything else. So I'm temporarily going with code that looks like...

Type type = bindingObject.GetType().GetProperty(nodeIter.Current.LocalName).PropertyType;
switch (type.ToString())
{
...
case "System.Int64":
bindingObject.GetType().GetProperty(nodeIter.Current.LocalName).SetValue
(bindingObject, Convert.ToInt64(nodeIter.Current.Value), null);
break;
default:
bindingObject.GetType().GetProperty(nodeIter.Current.LocalName).SetValue
(bindingObject, nodeIter.Current.Value, null);
break;
}


...but would love to see a way where one SetValue handles all c# data types. Do you have such an example?

Suba
June 04, 2008

# re: Reflection to provide EVALUATE functionality

Try this using Typeconvertor.....its working for me............no need to write switch case for all the .NET datatypes...............

TypeConverter converter = TypeDescriptor.GetConverter(pType);
objValue = converter.ConvertFrom(myValue);
controlProperty.SetValue(cont, objValue, null);

controlproperty is the propertyinfo...............

Linda Harmes
August 26, 2008

# re: Reflection to provide EVALUATE functionality

Rick,

I tried this out and think I put everything in correctly but cannot get the line that gets the reference to the textbox to work correctly. I am using the Mere Mortals .Net framework but I don't see how that could be causing the problem. oCoverage is the reference to the business entity object. Here's what I tried:

            oCoverageType.GetAllData();
            int CvgTypeCount = oCoverageType.GetRowCount();
            for (int i = 0; i < CvgTypeCount; i = i + 1)
            {
                oCoverageType.DataRow = oCoverageType.DataSet.Tables[0].Rows[i];

                string CvgTypeTextBoxName = "this.txtCoverage" + i.ToString();
                TextBox CvgTypeTextBox = (TextBox)this.GetType().GetField(CvgTypeTextBox, BindingFlags).GetValue(CvgTypeTextBoxName);
                CvgTypeTextBox.GetType().GetProperty("Text", BindingFlags).SetValue(CvgTypeTextBox, oCoverageType.Entity.CoverageType, null);
            }


When I try to compile this, I get the error: 'System.Reflection.BindingFlags' is a 'type' but is used like a 'variable'.

I then thought that I needed to change the word BindingFlags to MemberAccess but that resulted in the error: Argument '1': cannot convert from 'System.Windows.Forms.TextBox' to 'string'

I must be missing something. Did I miss something?

Thanks.

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024