¿Hay una mejor manera de obtener el nombre de la Propiedad cuando se pasa a través de una expresión lambda? Esto es lo que tengo actualmente.
p.ej.
GetSortingInfo<User>(u => u.UserId);
Funcionó lanzándolo como una expresión membere solo cuando la propiedad era una cadena. porque no todas las propiedades son cadenas, tuve que usar un objeto, pero luego devolvería una expresión no lineal para ellas.
public static RouteValueDictionary GetInfo<T>(this HtmlHelper html,
Expression<Func<T, object>> action) where T : class
{
var expression = GetMemberInfo(action);
string name = expression.Member.Name;
return GetInfo(html, name);
}
private static MemberExpression GetMemberInfo(Expression method)
{
LambdaExpression lambda = method as LambdaExpression;
if (lambda == null)
throw new ArgumentNullException("method");
MemberExpression memberExpr = null;
if (lambda.Body.NodeType == ExpressionType.Convert)
{
memberExpr =
((UnaryExpression)lambda.Body).Operand as MemberExpression;
}
else if (lambda.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpr = lambda.Body as MemberExpression;
}
if (memberExpr == null)
throw new ArgumentException("method");
return memberExpr;
}
c#
linq
lambda
expression-trees
Schotime
fuente
fuente
MemberExpression
enfoque que se enumera aquí solo para obtener el nombre del miembro, no para obtener el verdadero enMemberInfo
sí mismo, porqueMemberInfo
no se garantiza que la devolución sea del tipo reflejado en ciertos escenarios "derivados: base". Ver lambda-expression-not-return-expect-memberinfo . Me hizo tropezar una vez. La respuesta aceptada también sufre de esto.Respuestas:
Recientemente hice algo muy similar para hacer que un método OnPropertyChanged sea seguro.
Aquí hay un método que devolverá el objeto PropertyInfo para la expresión. Lanza una excepción si la expresión no es una propiedad.
El
source
parámetro se utiliza para que el compilador pueda hacer inferencia de tipos en la llamada al método. Puedes hacer lo siguientefuente
u => u.OtherType.OtherTypesProperty
crearía un caso en el que la última declaración esté buscando.if (type != propInfo.ReflectedType && !type.IsSubclassOf(propInfo.ReflectedType) && !propInfo.ReflectedType.IsAssignableFrom(type))
para permitir interfaces también.if(!propInfo.ReflectedType.IsAssignableFrom(type))
?Encontré otra forma de hacerlo: tener la fuente y la propiedad fuertemente tipadas e inferir explícitamente la entrada para la lambda. No estoy seguro si esa es la terminología correcta pero aquí está el resultado.
Y luego llámalo así.
y listo funciona.
Gracias a todos.
fuente
GetInfo(nameof(u.UserId))
var name = ((MemberExpression) ((UnaryExpression) accessor.Body).Operand).Member.Name
Estaba jugando con lo mismo y resolví esto. No se ha probado completamente, pero parece manejar el problema con los tipos de valor (el problema de expresión no lineal que encontró)
fuente
o => o.Thing1.Thing2
regresaríaThing2
, noThing1.Thing2
, lo cual es incorrecto si está tratando de usarlo en EntityFramework incluyeEsto maneja expresiones miembro y unarias. La diferencia es que obtendrá un
UnaryExpression
si su expresión representa un tipo de valor, mientras que obtendrá unMemberExpression
si su expresión representa un tipo de referencia. Todo se puede convertir a un objeto, pero los tipos de valor deben estar encuadrados. Por eso existe la UnaryExpression. Referencia.En aras de la legibilidad (@Jowen), aquí hay un equivalente ampliado:
fuente
Con coincidencia de patrones C # 7:
Ejemplo:
[Actualización] C # 8 coincidencia de patrones:
fuente
ahora en C # 6 simplemente puede usar nameof como este
nameof(User.UserId)
lo que tiene muchos beneficios, entre ellos es que esto se hace en tiempo de compilación , no en tiempo de ejecución
https://msdn.microsoft.com/en-us/magazine/dn802602.aspx
fuente
Esta es una implementación general para obtener el nombre de cadena de campos / propiedades / indexadores / métodos / métodos de extensión / delegados de struct / class / interface / delegate / array. He probado con combinaciones de variantes estáticas / de instancia y no genéricas / genéricas.
Esto también se puede escribir en un
while
bucle simple :Me gusta el enfoque recursivo, aunque el segundo podría ser más fácil de leer. Uno puede llamarlo así:
para imprimir el último miembro.
Nota:
En el caso de expresiones encadenadas como
A.B.C
, se devuelve "C".Esto no funciona con
const
s, indexadores de matriz oenum
s (imposible de cubrir todos los casos).fuente
Hay un caso extremo cuando se trata de
Array
.Longitud. Mientras 'Longitud' está expuesta como una propiedad, no puede usarla en ninguna de las soluciones propuestas anteriormente.Ahora ejemplo de uso:
Si
PropertyNameFromUnaryExpr
no se verificabaArrayLength
, "someArray" se imprimiría en la consola (el compilador parece generar acceso directo al campo Longitud de respaldo , como una optimización, incluso en Depuración, por lo tanto, el caso especial).fuente
Aquí hay una actualización del método propuesto por Cameron . El primer parámetro no es obligatorio.
Puedes hacer lo siguiente:
Métodos de extensión:
Usted puede:
fuente
u
como algún tipo, no puede hacerlo porque no hay un tipo para inferir. Lo que puedes hacer esGetPropertyInfo<SomeType>(u => u.UserID)
Descubrí que algunas de las respuestas sugeridas que profundizan en
MemberExpression
/UnaryExpression
no capturan / subpropiedades anidadas.ex)
o => o.Thing1.Thing2
devuelve enThing1
lugar deThing1.Thing2
.Esta distinción es importante si está intentando trabajar con EntityFramework
DbSet.Include(...)
.He descubierto que solo analizar el
Expression.ToString()
parece funcionar bien, y relativamente rápido. He comparado contra de laUnaryExpression
versión, e incluso conseguirToString
fuera de laMember/UnaryExpression
para ver si eso era más rápido, pero la diferencia era insignificante. Corrígeme si es una idea terrible.El metodo de extension
(Comprobar el delimitador podría incluso ser excesivo)
Demo (LinqPad)
Demostración + código de comparación: https://gist.github.com/zaus/6992590
fuente
o => o.Thing1.Thing2
no regresaThing1
como dices peroThing2
. De hecho, su respuesta devuelve algo como loThing1.Thing2
que puede o no ser deseado.Thing1.Thing2
Thing1
Thing2
o.Thing1.Thing2
Thing1
? No creo que vuelva a repetir eso en absoluto.Estoy usando un método de extensión para proyectos anteriores a C # 6 y el nombre de () para aquellos que apuntan a C # 6.
Y lo llamo así:
Funciona bien con campos y propiedades.
fuente
Bueno, no hay necesidad de llamar
.Name.ToString()
, pero en general eso es todo, sí. La única consideración que puede necesitar es six.Foo.Bar
debe devolver "Foo", "Bar" o una excepción, es decir, si necesita iterar.(re comentario) para obtener más información sobre la clasificación flexible, consulte aquí .
fuente
ToString
debería dar resultados feos para expresiones unarias.Creé un método de extensión en ObjectStateEntry para poder marcar las propiedades (de las clases POCO de Entity Framework) como modificadas de forma segura, ya que el método predeterminado solo acepta una cadena. Aquí está mi forma de obtener el nombre de la propiedad:
fuente
He realizado una
INotifyPropertyChanged
implementación similar al método a continuación. Aquí las propiedades se almacenan en un diccionario en la clase base que se muestra a continuación. Por supuesto, no siempre es deseable utilizar la herencia, pero para los modelos de vista, creo que es aceptable y proporciona referencias de propiedades muy claras en las clases de modelos de vista.La clase base algo más compleja se muestra a continuación. Maneja la traducción de la expresión lambda al nombre de la propiedad. Tenga en cuenta que las propiedades son realmente pseudo propiedades ya que solo se usan los nombres. Pero aparecerá transparente para el modelo de vista y referencias a las propiedades en el modelo de vista.
fuente
public bool IsLoading { get { return GetValue(MethodBase.GetCurrentMethod().Name); } set { SetPropertyValue(MethodBase.GetCurrentMethod().Name, value); } }
. Podría ser más lento, pero más genérico y directo.Esta es otra respuesta:
fuente
ModelMetadata
Existe en elSystem.Web.Mvc
espacio de nombres. Tal vez no sea adecuado para el caso generalDejo esta función si quieres obtener múltiples campos:
fuente
Aquí hay otra forma de obtener el PropertyInfo basado en esta respuesta. Elimina la necesidad de una instancia de objeto.
Se le puede llamar así:
fuente
He actualizado la respuesta de @ Cameron para incluir algunas comprobaciones de seguridad contra
Convert
expresiones lambda escritas:fuente
A partir de .NET 4.0, puede usar
ExpressionVisitor
para buscar propiedades:Así es como usas este visitante:
fuente
Esto podría ser óptimo
fuente
fuente