将强类型属性名称作为参数传递

3
我有一个集合 IEnumerable<School>,它被传递给一个扩展方法,该方法填充了一个 DropDownList。我还想将 DataValueFieldDataTextField 作为参数传递,但我希望它们是强类型的。
基本上,我不想为 DataValueFieldDataTextField 参数传递一个字符串,这样容易出错。
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();
}

以下是调用方式...

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

我的问题是,如何将 DataValueFieldDataTextField 作为参数强类型传递给 populateDropDownList 方法以填充下拉列表?

我不明白那些字段是字符串类型。 - jfin3204
3个回答

5

基于Jon的回答和这篇文章,给了我一个想法。我将DataValueFieldDataTextField作为Expression<Func<TObject, TProperty>>传递给我的扩展方法。我创建了一个接受该表达式并返回该属性的MemberInfo的方法。然后,我只需要调用.Name,就可以得到我的string

哦,我把扩展方法的名称改成了populate,因为它很丑陋。

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.");
}

这样调用...

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

只是想让你知道,在惯用的C#中,你应该使用PascalCase来命名方法为Populate - Oliver

4
如果您只是想使用属性链,您可以将参数更改为Expression<Func<T, string>>,然后提取涉及的属性名称 - 您需要分解获取到的Expression<TDelegate> ...你会期望Body是一个表示属性访问的MemberExpression。如果有多个(school.address.FirstLine),则一个成员访问的目标表达式将是另一个成员访问的目标表达式,以此类推。

从中,您可以构建一个字符串用于DataValueField(和DataTextField)。当然,调用者仍然可能会出错:

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

……但你可以检测它并抛出异常,对于好的调用者来说,你仍然是重构安全的。


0
根据你的尝试,即使你成功编译/运行了它,它仍然是错误的,因为Value和Text字段将被设置为列表中的值,而不是属性名称(例如,DataValueField =“TX”;DataTextField =“Texas”;而不是像你真正想要的那样DataValueField =“stateCode”;DataTextField =“stateName”;)。
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");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接