Передача строго типизированного имени свойства в качестве аргумента
У меня есть коллекция 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
) тогда целевым выражением доступа одного члена будет другое и т. д.
Исходя из этого, вы можете создать строку для использования в DataValueField
(и DataTextField
). Конечно, звонящий может все-таки вас обмануть:
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");