Su primer patrón, que creo que es lo mejor que puede llegar tan lejos como la legibilidad va
yo no creo que se pueda mejorar en este aspecto teniendo en cuenta las reglas de C# relacionados con las expresiones lambda. Dicho esto, creo que se pueden hacer pocas mejoras.
Primero, extienda la funcionalidad a otros tipos de expresiones lambda. Necesitará más de Func<T>
tipos para manejar todos los casos. Decida cuáles son los tipos de expresiones que debe manejar. Por ejemplo, si tiene un tipo Func<S, T>
(como en su pregunta - Bar
en Foo
), se ve mejor.Compare esto
myclass.GetMemberName(() => new Foo().Bar);
con
myclass.GetMemberName<Foo>(x => x.Bar);
yo diría que estas sobrecargas haría:
//for static methods which return void
public static string GetMemberName(Expression<Action> expr);
//for static methods which return non-void and properties and fields
public static string GetMemberName<T>(Expression<Func<T>> expr);
//for instance methods which return void
public static string GetMemberName<T>(Expression<Action<T>> expr);
//for instance methods which return non-void and properties and fields
public static string GetMemberName<S, T>(Expression<Func<S, T>> expr);
Ahora bien, estos pueden ser utilizados no sólo en los casos mencionados en los comentarios, seguramente hay superposición escenarios. Por ejemplo, si ya tiene una instancia de Foo
, entonces es más fácil llamar a la sobrecarga de la segunda sobrecarga (Func<T>
) para el nombre de la propiedad Bar
, como myclass.GetMemberName(() => foo.Bar)
.
En segundo lugar, implemente una funcionalidad GetMemberName
común a todas estas sobrecargas. Un método de extensión en Expression<T>
o LambdaExpression
haría. Prefiero lo último para poder llamarlo incluso en escenarios sin tipeo fuerte. Lo escribiría así, from this answer:
public static string GetMemberName(this LambdaExpression memberSelector)
{
Func<Expression, string> nameSelector = null;
nameSelector = e => //or move the entire thing to a separate recursive method
{
switch (e.NodeType)
{
case ExpressionType.Parameter:
return ((ParameterExpression)e).Name;
case ExpressionType.MemberAccess:
return ((MemberExpression)e).Member.Name;
case ExpressionType.Call:
return ((MethodCallExpression)e).Method.Name;
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
return nameSelector(((UnaryExpression)e).Operand);
case ExpressionType.Invoke:
return nameSelector(((InvocationExpression)e).Expression);
case ExpressionType.ArrayLength:
return "Length";
default:
throw new Exception("not a proper member selector");
}
};
return nameSelector(memberSelector.Body);
}
Por último, GetMemberName
no es un buen nombre si estás llamarlo manera no extensión. expression.GetMemberName()
suena más lógico. Member.NameFrom<int>(x => x.ToString())
o MemberName.From<string>(x => x.Length)
etc. son nombres más descriptivos para llamadas estáticas.
Así que en general la clase podría ser:
public static class Member
{
public static string NameFrom(Expression<Action> expr)
{
return expr.GetMemberName();
}
public static string NameFrom<T>(Expression<Func<T>> expr)
{
return expr.GetMemberName();
}
public static string NameFrom<T>(Expression<Action<T>> expr)
{
return expr.GetMemberName();
}
public static string NameFrom<T>(Expression<Func<T, object>> expr)
{
return expr.GetMemberName();
}
}
y uso:
var name1 = Member.NameFrom(() => Console.WriteLine());
var name2 = Member.NameFrom(() => Environment.ExitCode);
var name3 = Member.NameFrom<Control>(x => x.Invoke(null));
var name4 = Member.NameFrom<string>(x => x.Length);
más concisa y limpia.
Para las propiedades y campos, que se puede transformar en una clase anónima y luego utilizando la reflexión el nombre del miembro se puede leer, as shown here.
public static string GetMemberName<T>(T item) where T : class
{
if (item == null)
return null;
return typeof(T).GetProperties()[0].Name;
}
llamar así
var name = GetMemberName(new { new Foo().Bar });
Es más rápido, pero tiene ciertos caprichos, como no muy amigable con los refactores, y no ayuda en el caso de los métodos como miembros. Ver el hilo ..
Funcionó para mí .. –
No estoy seguro de seguir su comentario, funcionó para usted sin primero convertir a la expresión específica 'Expresión> 'o este estilo de llamada es satisfactorio? –
mlorbetske
Su respuesta funcionó para mí '((Expresión>) (() => new Foo(). Bar)). GetMemberName()' –