【问题标题】:Passing strongly typed property name as argument将强类型属性名称作为参数传递
【发布时间】:2012-10-25 17:42:17
【问题描述】:

我有一组 IEnumerable<School> 正在传递给扩展 填充DropDownList 的方法。我也想通过 DataValueField 和 DataTextField 作为论据,但我希望它们成为 强类型。

基本上,我不想为DataValueField 和DataTextField 参数传递string,这很容易出错。

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);

我的问题是,如何将DataValueField 和DataTextField 强类型作为参数传递给populateDropDownList?

【问题讨论】:

  • 我不明白这些字段是字符串类型的。

标签: c# lambda strong-typing


【解决方案1】:

根据 Jon 的回答和 this 的帖子,它给了我一个想法。我将DataValueField 和DataTextField 作为Expression&lt;Func&lt;TObject, TProperty&gt;&gt; 传递给我的扩展方法。我创建了一个接受该表达式并返回该属性的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# 中,您会将方法命名为 Populate - 使用 PascalCase。
【解决方案2】:

如果您只是尝试使用属性链,您可以将参数更改为 Expression&lt;Func&lt;T, string&gt;&gt;,然后提取所涉及的属性名称 - 您需要剖析您得到的 Expression&lt;TDelegate&gt; ...你会期望 Body 将是一个 MemberExpression 代表一个属性访问。如果您有多个 (school.address.FirstLine),则一个成员访问的目标表达式将是另一个成员,依此类推。

由此,您可以构建一个字符串以在DataValueField(和DataTextField)中使用。当然,调用者仍然可以把你搞砸:

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

...但是您可以检测到它并抛出异常,并且您仍然可以为 good 调用者提供重构证明。

【讨论】:

    【解决方案3】:

    根据您的尝试,即使您确实让它编译/运行,它仍然是错误的,因为值和文本字段将被设置为列表中的值而不是属性名称(即, 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");
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-15
      • 2021-11-30
      • 2014-08-27
      • 1970-01-01
      • 2015-04-04
      • 2021-07-10
      相关资源
      最近更新 更多