Retrieving property names for reflection, with Intellisense and Compile-Time validation

Ok That way, I have code that maps specific winForm controls to certain object properties to do certain things for controls when certain things happen to data. All is well and good, works great. Not a problem. The problem is that to add elements to the mapping, I call a function that looks like this:

this.AddMapping(this.myControl,myObject,"myObjectPropertyName"); 

The problem I am facing is that at compile time it is very difficult to determine the difference between the line above and below:

 this.AddMapping(this.myControl,myObject,"myObjectPropretyName"); 

Since the last parameter is a string, there is no compilation time check or anything similar that would ensure that the string itself actually matches the valid property name for this object. In addition, things like Refactor and "Find All References" skip this link, which leads to fun when the property name changes. So I'm wondering if there is any way to change the function, so what I pass in is still a string representing the property name in some way, but with checking the compile time of the actual value. Someone said that I can do this with expression trees, but I read them and don't seem to see the connection. I would like to do something like:

 this.AddMapping(this.myControl,myObject,myObject.myObjectPropertyName); 

or even

 this.AddMapping(this.myControl,myObject.myObjectPropertyName); 

will be sweet!

Any ideas?

+13
reflection c # mapping compile-time intellisense
Apr 27 '09 at 20:43
source share
5 answers

in 3.5, an expression is one way to specify member names as code; could you:

 public void AddMapping<TObj,TValue>(Control myControl, TObj myObject, Expression<Func<TObj, TValue>> mapping) {...} 

and then parse the expression tree to get the value. A bit inefficient, but not so bad.

Here is a sample code:

  public void AddMapping<TSource, TValue>( Control control, TSource source, Expression<Func<TSource, TValue>> mapping) { if (mapping.Body.NodeType != ExpressionType.MemberAccess) { throw new InvalidOperationException(); } MemberExpression me = (MemberExpression)mapping.Body; if (me.Expression != mapping.Parameters[0]) { throw new InvalidOperationException(); } string name = me.Member.Name; // TODO: do something with "control", "source" and "name", // maybe also using "me.Member" } 

called with:

  AddMapping(myControl, foo, f => f.Bar); 
+14
Apr 27 '09 at 20:48
source share

To make it easier to work with the expression-based llama method, I wrote it as an extension method.

  public static string GetPropertyName<T>(this object o, Expression<Func<T>> property) { var propertyInfo = (property.Body as MemberExpression).Member as PropertyInfo; if (propertyInfo == null) throw new ArgumentException("The lambda expression 'property' should point to a valid Property"); var propertyName = propertyInfo.Name; return propertyName; } 

Name it

  class testclass { public string s { get; set; } public string s2 { get; set; } public int i { get; set; } } [TestMethod] public void TestMethod2() { testclass x = new testclass(); string nameOfPropertyS = this.GetPropertyName(() => xs); Assert.AreEqual("s", nameOfPropertyS); string nameOfPropertyI = x.GetPropertyName(() => xi); Assert.AreEqual("i", nameOfPropertyI); } 

Well, using as an extension method is really for convenience, since you can actually call a method for one class for the properties of the anther class. I am sure this can be improved.

+3
Oct 26
source share

Consider using lambdas or even System.Linq.Expressions for this, with one of:

 extern void AddMapping<T,U>(Control control, T target, Func<T,U> mapping); extern void AddMapping<T,U>(Control control, T target, Expression<Func<T,U>> mapping); 

Then call him

 this.AddMapping(this.myControl, myObject, (x => x.PropertyName)); 

Use the Expression argument if you need to split the abstract syntax tree at run time, do reflective things, for example, get the property name as a string; alternatively, let the delegate do the work of finding the data you need.

+2
Apr 27 '09 at 20:53
source share

You really should not pass string literals as property names. Instead, you should use YourClass.PROPERTY_NAME_FOO .

You must declare these lines as const in your class.

 public const String PROPERTY_NAME_FOO = "any string, it really doesn't matter"; public const String PROPERTY_NAME_BAR = "some other name, it really doesn't matter"; 

Or you don't need to worry about Strings at all, just property names:

 public const int PROPERTY_NAME_FOO = 0; public const int PROPERTY_NAME_BAR = 1; //values don't matter, as long as they are unique 

This will stop lines that do not refer to a valid property from accessing function calls.

Intelisense will be able to show you property names from your class as suggestions for automatic completion.

+1
Apr 27 '09 at 20:47
source share
+1
09 Sep '09 at 17:18
source share



All Articles