Search code examples
c#lambdastrong-typing

Passing strongly typed property name as argument


I have a collection of IEnumerable<School> that is being passed to an extension method that populates a DropDownList. I would also like to pass the 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 arguments, it's 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 so...

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

My question is, how can I pass the DataValueField and DataTextField strongly typed as an argument to populateDropDownList?


Solution

  • Based off Jon's answer and this post, it gave me an idea. I passed the DataValueField and DataTextField as Expression<Func<TObject, TProperty>> to my extension method. I created a method that accepts that expression and returns the MemberInfo for that property. Then all I have to call is .Name and I've got my string.

    Oh, and I changed the extension method name 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 so...

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