c# - dinamico - lenguajes fuertemente y débilmente tipados
Pasar el nombre de propiedad fuertemente tipado como argumento (3)
Con lo que estabas intentando, incluso si lo hicieras compilar / ejecutar, seguiría siendo incorrecto porque los campos de Valor y Texto se habrían establecido en un valor en la lista en lugar del nombre de la propiedad (es decir, DataValueField = "TX"; DataTextField = "Texas";
lugar de DataValueField = "stateCode"; DataTextField = "stateName";
como realmente desea).
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");
Tengo una colección de IEnumerable<School>
que se está IEnumerable<School>
a un método de extensión que rellena DropDownList
. También me gustaría pasar DataValueField
y DataTextField
como argumento, pero quería que estuvieran fuertemente tipados.
Básicamente, no quiero pasar una string
para los argumentos DataValueField
y DataTextField
, es propenso a errores.
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();
}
Llamado así ...
myDropDownList.populateDropDownList(states,
school => school.stateCode,
school => school.stateName);
Mi pregunta es, ¿cómo puedo pasar DataValueField
y DataTextField
fuertemente tipeado como un argumento para poblar DataTextField
?
Si solo está tratando de usar cadenas de propiedades, puede cambiar el parámetro a Expression<Func<T, string>>
y luego extraer los nombres de las propiedades implicadas; necesitará diseccionar Expression<TDelegate>
para obtener ... es de esperar que el Body
sea una MemberExpression
represente un acceso a la propiedad. Si tiene más de uno ( school.address.FirstLine
), la expresión de destino del acceso de un miembro será otra, etc.
A partir de eso, puede construir una cadena para usar en DataValueField
(y DataTextField
). Por supuesto, la persona que llama aún puede atornillarlo:
myDropDownList.populateDropDownList(states,
school => school.stateCode.GetHashCode().ToString(),
school => school.stateName);
... pero puedes detectarlo y lanzar una excepción, y todavía eres resistente a los refactores para las buenas personas que llaman.
Basado en la respuesta de Jon y en esta publicación, me dio una idea. Pasé DataValueField
y DataTextField
como Expression<Func<TObject, TProperty>>
a mi método de extensión. MemberInfo
un método que acepta esa expresión y devuelve el MemberInfo
para esa propiedad. Entonces todo lo que tengo que llamar es .Name
y tengo mi string
.
Oh, y cambié el nombre del método de extensión para populate
, era feo.
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.");
}
Llamado así ...
myDropDownList.populate(states,
school => school.stateCode,
school => school.stateName);