Tuesday, October 2, 2007

Reflection makes partial classes visible again

ASP.Net 1.x didn't use partial classes to implement pages and user controls. This made things a little cluttered. However, it had the advantage of the type being accessible for things like casting the Page object to your own specific page class. You can't do this in ASP.Net 2.x because partial classes are not available until runtime when the two partial classes are joined to make a complete class. This means we can't cast. You might be thinking, why would I want to do that anyway. In most cases you don't have a big need. However, when you want to pass information between a user control a master page or even the page itself it can be difficult. Let's assume you have a user control as defined below. public partial class MyControl : System.Web.UI.UserControl { public string MyProperty { set { myLabel.Text = value; } } } In ASP.Net 1.x (assuming MyControl was defined as a non-partial class because 1.x didn't support partial classes) you do something like the following from the parent Page of this control. The following assumes that the parent page has an instance of the MyControl on the page called myControl. public class MyPage .... ((MyControl)this.myControl).MyProperty = "abcdefg"; .... In ASP.Net 1.x the compiler would know how to find MyControl class and would compile ok. In ASP.Net 2.0 the compiler would NOT know how to find MyControl class and would FAIL to compile. Again, the reason is the class is a partial class and does not exist yet. How do we fix this you ask? Option 1. Convert your Web Site project to Web Application Project under ASP.Net 2.0. Option 2. Convert only this class to a non-partial class. I have not tried this, and don't know if this will work. Option 3. Use reflection to get around the issue. This works well since both reflection and partial classes will be doing their magic at runtime, not compile time. /// <summary> /// Set the value of a property on any class /// </summary> public static void SetProperty(System.Object obj, System.String propertyName, System.Object propertyValue) { if (obj == null) { throw new System.ArgumentException("No target specified"); } if (propertyName == null) { throw new System.ArgumentException("No name specified"); } PropertyInfo pi = obj.GetType().GetProperty(propertyName); if (pi == null) { throw new Exception("Object does not have a property named: " + propertyName); } pi.SetValue(obj, propertyValue, null); } Be warned, reflection can open up the chance for errors that the compiler can no longer check for. For example, don't change MyProperty, to MyProperty2 without changing your string in the SetProperty method above. Now we can do something like:
....
SetProperty(myControl, "MyProperty", "abcdefg");
....
Also, try not to do something like this in a loop as there is a slight performance hit for using reflection.

No comments: