Я нашел другой способ, которым вы можете сделать, это строго указать тип источника и свойства и явно сделать вывод для лямбда-выражения. Не уверен, что это правильная терминология, но вот результат.
public static RouteValueDictionary GetInfo<T,P>(this HtmlHelper html, Expression<Func<T, P>> action) where T : class
{
var expression = (MemberExpression)action.Body;
string name = expression.Member.Name;
return GetInfo(html, name);
}
А потом назовите это так.
GetInfo((User u) => u.UserId);
и вуаля это работает.
Спасибо всем.
Начиная с .NET 4.0 вы можете использовать ExpressionVisitor
, чтобы найти свойства:
class ExprVisitor : ExpressionVisitor {
public bool IsFound { get; private set; }
public string MemberName { get; private set; }
public Type MemberType { get; private set; }
protected override Expression VisitMember(MemberExpression node) {
if (!IsFound && node.Member.MemberType == MemberTypes.Property) {
IsFound = true;
MemberName = node.Member.Name;
MemberType = node.Type;
}
return base.VisitMember(node);
}
}
Вот как вы используете этого посетителя:
var visitor = new ExprVisitor();
visitor.Visit(expr);
if (visitor.IsFound) {
Console.WriteLine("First property in the expression tree: Name={0}, Type={1}", visitor.MemberName, visitor.MemberType.FullName);
} else {
Console.WriteLine("No properties found.");
}
Я создал метод расширения для ObjectStateEntry, чтобы иметь возможность помечать свойства (классов POCO Entity Framework) как модифицированные безопасным способом, поскольку метод по умолчанию принимает только строку. Вот мой способ получить имя от собственности:
public static void SetModifiedProperty<T>(this System.Data.Objects.ObjectStateEntry state, Expression<Func<T>> action)
{
var body = (MemberExpression)action.Body;
string propertyName = body.Member.Name;
state.SetModifiedProperty(propertyName);
}
Ну, нет никакой потребности звонить .Name.ToString()
, но широко который является об этом, да. Единственное соображение, в котором Вы, возможно, нуждались бы, состоит в том, должен ли x.Foo.Bar
возвратить "Нечто", "Панель" или исключение - т.е. сделать необходимо выполнить итерации вообще.
(комментарий ре) для больше на гибкой сортировке, см. здесь .
Я недавно сделал очень похожую вещь сделать безопасный с точки зрения типов метод OnPropertyChanged.
Вот метод, это возвратит объект PropertyInfo для выражения. Это выдает исключение, если выражение не является свойством.
public PropertyInfo GetPropertyInfo<TSource, TProperty>(
TSource source,
Expression<Func<TSource, TProperty>> propertyLambda)
{
Type type = typeof(TSource);
MemberExpression member = propertyLambda.Body as MemberExpression;
if (member == null)
throw new ArgumentException(string.Format(
"Expression '{0}' refers to a method, not a property.",
propertyLambda.ToString()));
PropertyInfo propInfo = member.Member as PropertyInfo;
if (propInfo == null)
throw new ArgumentException(string.Format(
"Expression '{0}' refers to a field, not a property.",
propertyLambda.ToString()));
if (type != propInfo.ReflectedType &&
!type.IsSubclassOf(propInfo.ReflectedType))
throw new ArgumentException(string.Format(
"Expression '{0}' refers to a property that is not from type {1}.",
propertyLambda.ToString(),
type));
return propInfo;
}
source
параметр используется так, компилятор может сделать вывод типа на вызове метода. Можно сделать следующий
var propertyInfo = GetPropertyInfo(someUserObject, u => u.UserID);
static void Main(string[] args)
{
var prop = GetPropertyInfo<MyDto>(_ => _.MyProperty);
MyDto dto = new MyDto();
dto.MyProperty = 666;
var value = prop.GetValue(dto);
// value == 666
}
class MyDto
{
public int MyProperty { get; set; }
}
public static PropertyInfo GetPropertyInfo<TSource>(Expression<Func<TSource, object>> propertyLambda)
{
Type type = typeof(TSource);
var member = propertyLambda.Body as MemberExpression;
if (member == null)
{
var unary = propertyLambda.Body as UnaryExpression;
if (unary != null)
{
member = unary.Operand as MemberExpression;
}
}
if (member == null)
{
throw new ArgumentException(string.Format("Expression '{0}' refers to a method, not a property.",
propertyLambda.ToString()));
}
var propInfo = member.Member as PropertyInfo;
if (propInfo == null)
{
throw new ArgumentException(string.Format("Expression '{0}' refers to a field, not a property.",
propertyLambda.ToString()));
}
if (type != propInfo.ReflectedType && !type.IsSubclassOf(propInfo.ReflectedType))
{
throw new ArgumentException(string.Format("Expression '{0}' refers to a property that is not from type {1}.",
propertyLambda.ToString(), type));
}
return propInfo;
}
Я играл с тем же самым и придумал вот это. Он не полностью протестирован, но, похоже, решает проблему с типами значений (проблема с неарифметическим выражением, с которой вы столкнулись)
public static string GetName(Expression<Func<object>> exp)
{
MemberExpression body = exp.Body as MemberExpression;
if (body == null) {
UnaryExpression ubody = (UnaryExpression)exp.Body;
body = ubody.Operand as MemberExpression;
}
return body.Member.Name;
}