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