Передача строго типизированного имени свойства в качестве аргумента

У меня есть коллекция IEnumerable<School> который передается в метод расширения, который заполняет DropDownList, Я также хотел бы передатьDataValueField а также DataTextField в качестве аргумента, но я хотел, чтобы они были строго напечатаны.

В принципе, я не хочу передавать string для DataValueField а также DataTextField аргументы, это склонно к ошибкам.

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?

3 ответа

Решение

Исходя из ответа Джона и этого поста, это дало мне идею. Я прошел DataValueField а также DataTextField как 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);

Если вы пытаетесь использовать только цепочки свойств, вы можете изменить параметр на Expression<Func<T, string>> а затем извлеките соответствующие имена свойств - вам нужно будет проанализировать Expression<TDelegate> вы получите... вы ожидаете, что Body будет MemberExpression представляющий доступ к собственности. Если у вас есть более одного (school.address.FirstLine) тогда целевым выражением доступа одного члена будет другое и т. д.

Исходя из этого, вы можете создать строку для использования в DataValueFieldDataTextField). Конечно, звонящий может все-таки вас обмануть:

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

... но вы можете обнаружить его и сгенерировать исключение, и вы по-прежнему защищены от рефакторинга для хороших абонентов.

С тем, что вы пытались, даже если бы вам удалось его скомпилировать / запустить, это все равно было бы неправильно, поскольку в полях 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");
Другие вопросы по тегам