Passing strongly typed property name as argument

后端 未结 3 1183
春和景丽
春和景丽 2020-12-30 17:37

I have a collection of IEnumerable that is being passed to an extension method that populates a DropDownList. I would also like to pa

相关标签:
3条回答
  • 2020-12-30 17:57

    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);
    
    0 讨论(0)
  • 2020-12-30 17:58

    With what you were trying, even if you did get it to compile/run, it would still be wrong because the Value & Text fields would've been set to a value in the list instead of the property name (ie, DataValueField = "TX"; DataTextField = "Texas"; instead of DataValueField = "stateCode"; DataTextField = "stateName"; like you really want).

    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 讨论(0)
  • 2020-12-30 18:07

    If you're only trying to use property chains, you could change the parameter to Expression<Func<T, string>> and then extract the property names involved - you'll need to dissect the Expression<TDelegate> you get... you'd expect that the Body will be a MemberExpression representing a property access. If you've got more than one (school.address.FirstLine) then the target expression of one member access will be another one, etc.

    From that, you can build up a string to use in the DataValueField (and the DataTextField). Of course, the caller can still screw you over:

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

    ... but you can detect it and throw an exception, and you're still refactor-proof for good callers.

    0 讨论(0)
提交回复
热议问题