C#: Getting Names of properties in a chain from lambda expression
Something like this?
public void Foo<T, P>(Expression<Func<T, P>> expr)
{
MemberExpression me;
switch (expr.Body.NodeType)
{
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
var ue = expr.Body as UnaryExpression;
me = ((ue != null) ? ue.Operand : null) as MemberExpression;
break;
default:
me = expr.Body as MemberExpression;
break;
}
while (me != null)
{
string propertyName = me.Member.Name;
Type propertyType = me.Type;
Console.WriteLine(propertyName + ": " + propertyType);
me = me.Expression as MemberExpression;
}
}
Getting Names of properties in a chain from NewExpression members
Maybe you can try on Arguments
instead of Members
.
var body = exp.Body as NewExpression;
foreach (var member in body.Arguments)
{
Console.WriteLine(member);
}
Output is:
dt.Second
dt.Hour
dt.Date.Day
Get the name of the lambda expression with the index from the expression's array
The indexer is translated to a MethodCallExpression
, so you need to override VisitMethodCall
.
You can do a visitor like this:
public class PathVisitor : ExpressionVisitor
{
// You should not use MemberInfo for the list type anymore, since the path can have indexer expressions now!
// Here I convert both the indexer expressions and the member accesses to string in the visitor
// You can do something more fancy, by creating your own class that represents either of these two cases
// and then formatting them afterwards.
internal readonly List<string> Path = new List<string>();
protected override Expression VisitMember(MemberExpression node)
{
if (node.Member is PropertyInfo)
{
Path.Add(node.Member.Name);
}
return base.VisitMember(node);
}
protected override Expression VisitMethodCall(MethodCallExpression node) {
if (node.Method.Name == "get_Item") { // name of the indexer
// you can format this in a better way on your own. This is just an example
Path.Add(node.Arguments.First().ToString());
}
return base.VisitMethodCall(node);
}
}
Usage:
public static IReadOnlyList<string> Get(LambdaExpression expression)
{
var visitor = new PathVisitor();
visitor.Visit(expression.Body);
visitor.Path.Reverse();
return visitor.Path;
}
public static string PathString(Expression<Func<object>> expression)
{
return string.Join(".", Get(expression));
}
This produces an output of Adresses.2.Number
for your lambda.
Note that this assumes the lambdas are only with property accesses and indexers with constant arguments.
Get chained property names from MemberExpression
You need a little recursion:
private static string GetPropertyPath<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression)
{
var propertyPath = new Stack<string>();
var body = (MemberExpression)expression.Body;
do
{
propertyPath.Push(body.Member.Name);
// this will evaluate to null when we will reach ParameterExpression (x in "x => x.Foo.Bar....")
body = body.Expression as MemberExpression;
}
while (body != null);
return string.Join(".", propertyPath);
}
Retrieving Property name from lambda expression
I found another way you can do it was to have the source and property strongly typed and explicitly infer the input for the lambda. Not sure if that is correct terminology but here is the result.
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);
}
And then call it like so.
GetInfo((User u) => u.UserId);
and voila it works.
How to get the full name of the property from a lambda
Or you can use
expression.Compile().Invoke(obj)
if you want to use Expression<>
Write a lambda expression to get a list of properties from a list of objects
If ProjectDetails.primitives
is a single ProjectPrimitives
, you need Select()
to transform the list from ProjectDetails
to ProjectPrimitives
:
var prims = myList.Select(i => i.primitives).ToList();
If it is a collection, such as List<ProjectPrimitives>
, you need SelectMany()
:
var prims = myList.SelectMany(i => i.primitives).ToList();
Pass Lambda to Chain Over Object Members to Function
You can create your own expression visitor that will be able to keep track of all member accesses where the member is being accessed from a parameter.
internal class ParameterAccessesVisitor : ExpressionVisitor
{
private ReadOnlyCollection<ParameterExpression> parameters;
public ParameterAccessesVisitor(
ReadOnlyCollection<ParameterExpression> parameters)
{
VisitedMembers = new List<MemberInfo>();
this.parameters = parameters;
}
protected override Expression VisitMember(MemberExpression node)
{
if (parameters.Contains(node.Expression))
VisitedMembers.Add(node.Member);
return base.VisitMember(node);
}
public List<MemberInfo> VisitedMembers { get; private set; }
}
We can then create a method to use this visitor that accepts an expression (which should be a lambda expression), pulls out the parameters, find all member accesses to those parameters, and then returns the sequence:
public static IEnumerable<MemberInfo> ParameterAccesses<T, TResult>(
Expression<Func<T, TResult>> lambda)
{
var visitor = new ParameterAccessesVisitor(lambda.Parameters);
visitor.Visit(lambda);
return visitor.VisitedMembers;
}
If you want just the member names, rather than the full MemberInfo
, then a simple Select
can select out the names into an array.
We can now write:
var members = ParameterAccesses((List<int> p) => new { p.Count, p.Capacity })
.Select(member => member.Name);
And have it print out:
Count
Capacity
Since you want to grab a value of a given attribute from these types, you can easily add another method to perform such a query based on the above method:
public static IEnumerable<string> GetLogicalNames<T, TResult>(
Expression<Func<T, TResult>> lambda)
{
return from member in ParameterAccesses(lambda)
let attributes = member.GetCustomAttributes<AttributeLogicalNameAttribute>()
where attributes.Any()
select attributes.First().LogicalName;
}
Related Topics
A Pattern for Self-Cancelling and Restarting Task
Returning a String from a C# Dll with Unmanaged Exports to Inno Setup Script
Copying Free Hand Drawing from Panel in Visual Studio 2013
In What Areas Might the Use of F# Be More Appropriate Than C#
Forms' Does Not Exist in the Namespace System.Windows
Get Current System.Web.Ui.Page from Httpcontext
How to Get the Executing Exe's Path in .Net
How Are Nullable Types Implemented Under the Hood in .Net
JSON Deserialization - Map Array Indices to Properties with JSON.Net
Split a String That Has White Spaces, Unless They Are Enclosed Within "Quotes"
Best Practice for Exception Handling in a Windows Forms Application
How to Select a Random Value from an Enumeration
Find Image Format Using Bitmap Object in C#