Saturday, July 23, 2016

PEMSTATUS & More With Reflection In .NET

Many, many, *many* years ago, when I was a Visual FoxPro developer, we could make use of a VFP function called PEMSTATUS. One of the things that function could tell you was whether or not a particular class/object contained a particular Property or Event or Method (hence the “PEM”) and other information about that P/E/M.

I’ve repurposed some of that functionality in .NET. Many of you may remember a post I wrote about 7 years ago, Reflection in .NET. That blog post was about using Reflection to dynamically instantiate a class using a string containing the name of the class. Well, we can also use Reflection for determining if a class/object contains a P/E/M, and we can then use Reflection for getting the value of a property or executing a method … all dynamically, just with a string containing the name.

Let’s say that we add some static methods to that MyReflectionClass from that 7-year-old post.

First, how about a PemStatus() method that will just tell us if a P/E/M exists? Here it is, along with two supporting methods:

// PemStatus checks for existence of a Property, Event or Method
public static bool PemStatus(object o, string name, string PEM)
{
    switch (PEM.ToUpper())
    {
        case "P":
            PropertyInfo pi = o.GetType().GetProperty(name);
            if (pi == null)
            {
                // GetProperty will only work on "real" properties ... those with get/set
                // Consequently, we want to look at Fields if we don't get a hit with GetProperty
                FieldInfo fi = GetFieldInfo(o, name);
                if (fi == null)
                    return false;
            }
            return true;
        case "E":
            EventInfo ei = o.GetType().GetEvent(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
            if (ei == null)
            {
                // GetEvent doesn't always find the Event, so we'll check the Fields for it in that case
                FieldInfo fi = GetFieldInfo(o, name);
                return fi != null;
            }
            return ei != null;
        case "M":
            MethodInfo mi = GetMethodInfo(o, name);
            return mi != null;
        default:
            return false;
    }
}
private static FieldInfo GetFieldInfo(object o, string name)
{
    FieldInfo fi = null;
    if (o is System.Windows.Forms.Control && !(o is System.Windows.Forms.Form))
        fi = typeof(Control).GetField("Event" + name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
    if (fi == null)
        fi = o.GetType().GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
 
    // Base class fields are not always searched, so we need to look in each BaseType if fi is still null
    if (fi == null)
    {
        Type baseType = o.GetType().BaseType;
        while (baseType != null && fi == null)
        {
            fi = baseType.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
            baseType = baseType.BaseType;
        }
    }
 
    return fi;
}
private static MethodInfo GetMethodInfo(object o, string name)
{
    MethodInfo mi = null;
 
    mi = o.GetType().GetMethod(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static) as MethodInfo;
 
    // Base class methods are not always searched, so we need to look in each BaseType if mi is still null
    if (mi == null)
    {
        Type baseType = o.GetType().BaseType;
        while (baseType != null && mi == null)
        {
            mi = baseType.GetMethod(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
            baseType = baseType.BaseType;
        }
    }
 
    return mi;
}

Now, what is this useful for? How about this: you might want to simply test for the existence of a P/E/M before deciding to do something else in your logic.  Let’s think of something that kind of makes sense. Let’s say that you’ve got an instance of a class (whether or not it’s been dynamically instantiated doesn’t matter) … let’s call it MyTest. You want to see if it has a Property called “TestIt”. I use the term “Property” loosely here, because it could also be a field (public, protected or even private).

Now, say your requirements are such that if MyTest has a TestIt property, then you want to get the value of that property and then see if MyTest contains a Method with the name contained in the TestIt property … and if so, execute that Method!  It’s actually pretty easy to do, but we’ll need some more static methods added to MyReflectionClass. Here they are:

// The next 3 methods, do stuff with the P/E/M:
// You can get the Property's value, execute the Event's handler, or execute the Method
public static object GetPropertyValue(object o, string name)
{
    PropertyInfo pi = o.GetType().GetProperty(name);
    if (pi != null)
        return pi.GetAccessors()[0].Invoke(o, null);
 
    FieldInfo fi = GetFieldInfo(o, name);
    if (fi != null)
        return fi.GetValue(o);
 
    return null;
}
public static void ExecuteEventHandler(object o, string name, EventArgs args = null)
{
    object eventKey = null;
    FieldInfo fi = GetFieldInfo(o, name);
 
    if (fi != null)
        eventKey = fi.GetValue(o);
 
    if (eventKey != null)
    {
        // This is for Events on non-Control objects
        if (eventKey is EventHandler)
            ((EventHandler)eventKey)(o, args);
        else
        {
            // This is for Events on Controls (such as a Button Click)
            EventHandlerList eventHandlerList = null;
            PropertyInfo pi = o.GetType().GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
            if (pi != null)
                eventHandlerList = pi.GetValue(o, new object[] { }) as EventHandlerList;
 
            if (eventHandlerList != null)
            {
                var eventHandler = eventHandlerList[eventKey] as Delegate;
                Delegate[] invocationList = eventHandler.GetInvocationList();
                foreach (EventHandler item in invocationList)
                {
                    item(o, args);
                }
            }
        }
    }
}
public static object ExecuteMethod(object o, string name, object[] parms = null)
{
    object result = null;
 
    try
    {
        MethodInfo mi = GetMethodInfo(o, name);
        if (mi != null)
        {
            // Let's add checks for parameters while we're at it
            ParameterInfo[] pi = mi.GetParameters();
            if ((pi.Length == 0 && parms == null) || (parms != null && pi.Length == parms.Length))
                result = mi.Invoke(o, parms);
            else
                result = string.Format("Parameter mismatch in method '{0}'. Method expected {1} parameter{2}, but received {3}.",
                    name, pi.Length, pi.Length > 1 ? "s" : "", parms == null ? "none" : parms.Length.ToString());
        }
        else
            result = string.Format("Method '{0}' does not exist");
    }
    catch (Exception ex)
    {
        result = ex.Message;
    }
 
    return result;
}

Here is the class we’ll be instantiating and using in our testing:

public class TestForBlog
{
    protected string TestIt = "DoIt";

    protected void DoIt()
    {
        Console.WriteLine("Did It!!");
    }
}

The class *does* contain a field called TestIt. It defaults to containing the string “DoIt”. And there *is* a method called DoIt defined in this class!! Here’s how we implement this test:

// As mentioned, this could have been instantiated via Reflection, but that's not the point of this particular blog post. 
// Read the older post, for which I provided the link, to see how that would work ...
TestForBlog MyTest = new TestForBlog();
 
// First see if the Property "TestIt" exists:
if (MyReflectionClass.PemStatus(MyTest, "TestIt", "P"))
{
    // It does, let's grab the value, see if there's a method with that name and execute it if there is
    string MethodToTest = MyReflectionClass.GetPropertyValue(MyTest, "TestIt").ToString();
    if (MyReflectionClass.PemStatus(MyTest, MethodToTest, "M"))
        MyReflectionClass.ExecuteMethod(MyTest, MethodToTest);
}

That’s all there is to it. Hopefully you may find this useful!!

Happy coding!  =0)

No comments:

Post a Comment