Passing a strongly typed property name as an argument

I have an IEnumerable<School> collection that is passed to the extension method that populates the DropDownList . I would also like to pass DataValueField and DataTextField as an argument, but I wanted them to be strongly typed.

Basically, I don't want to pass a string for the DataValueField and DataTextField , which are error prone.

 public static void populateDropDownList<T>(this DropDownList source, IEnumerable<T> dataSource, Func<T, string> dataValueField, Func<T, string> dataTextField) { source.DataValueField = dataValueField; //<-- this is wrong source.DataTextField = dataTextField; //<-- this is wrong source.DataSource = dataSource; source.DataBind(); } 

Called like that ...

 myDropDownList.populateDropDownList(states, school => school.stateCode, school => school.stateName); 

My question is, how do I pass the DataValueField and DataTextField tags strictly entered as an argument to populateDropDownList?

+3
source share
3 answers

Based on John's answer and this one , it gave me an idea. I passed DataValueField and DataTextField as Expression<Func<TObject, TProperty>> to my extension method. I created a method that takes this expression and returns MemberInfo for this property. Then all I need to call is .Name , and I have my string .

Oh, and I changed the name of the extension method to populate , it was ugly.

 public static void populate<TObject, TProperty>( this DropDownList source, IEnumerable<TObject> dataSource, Expression<Func<TObject, TProperty>> dataValueField, Expression<Func<TObject, TProperty>> dataTextField) { source.DataValueField = getMemberInfo(dataValueField).Name; source.DataTextField = getMemberInfo(dataTextField).Name; source.DataSource = dataSource; source.DataBind(); } private static MemberInfo getMemberInfo<TObject, TProperty>(Expression<Func<TObject, TProperty>> expression) { var member = expression.Body as MemberExpression; if(member != null) { return member.Member; } throw new ArgumentException("Member does not exist."); } 

Called like that ...

 myDropDownList.populate(states, school => school.stateCode, school => school.stateName); 
+4
source

If you are trying to use property chains, you can change the parameter to Expression<Func<T, string>> , and then extract the desired property names - you will need to Expression<TDelegate> you will get ... you expect Body be MemberExpression , representing access to properties. If you have more than one ( school.address.FirstLine ), then the target expression of one member access will be different, etc.

From this, you can create a string for use in a DataValueField (and a DataTextField ). Of course, the caller may still blame you:

 myDropDownList.populateDropDownList(states, school => school.stateCode.GetHashCode().ToString(), school => school.stateName); 

... but you can detect it and throw an exception, and you still keep refactoring for good subscribers.

+4
source

Using what you tried, even if you ran it to compile / run, it would still be wrong, because the Value and Text fields would be set as the value in the list instead of the property name (i.e. DataValueField = "TX"; DataTextField = "Texas"; instead of DataValueField = "stateCode"; DataTextField = "stateName"; how you really want it).

 public static void populateDropDownList<T>(this DropDownList source, IEnumerable<T> dataSource, Func<string> dataValueField, Func<string> dataTextField) { source.DataValueField = dataValueField(); source.DataTextField = dataTextField(); source.DataSource = dataSource; source.DataBind(); } myDropDownList.populateDropDownList(states, "stateCode", "stateName"); 
0
source

Source: https://habr.com/ru/post/1485596/


All Articles