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.
Get the property name of a property of an anonymous type from an expression
The expression body is a UnaryExpression
not because of the anonymous type, but because the property is a value type that needs to be boxed as an object
for your Expression<Func<object>>
; see this answer.
You can avoid this by changing your method signature to take a generic type parameter:
public static string GetPropertyName<T>(this Expression<Func<T>> property)
Get a property name from a lambda expression *without* an object instance
You can use a property expression the same way as EditorFor
. You can do this without an instance.
The one difference is you'll have to specify the type of the expression's parameter to tell the compiler what the object type is. So instead of
EditorFor( x => x.Name ); //Specifies a property but not a type
...your expression would look like this:
EditorFor( (MyType x) => x.Name ); //Specifies property and the type it belongs to
You can accomplish this with a short method like this one:
static PropertyInfo GetPropertyInfo<TIn, TOut>(Expression<Func<TIn, TOut>> expression)
{
var memberExp = expression.Body as MemberExpression;
return memberExp?.Member as PropertyInfo;
}
And then you can do this:
var p1 = GetPropertyInfo((string s) => s.Length);
Console.WriteLine("{0}.{1}", p1.DeclaringType.FullName, p1.Name);
var p2 = GetPropertyInfo((DateTime d) => d.Minute);
Console.WriteLine("{0}.{1}", p2.DeclaringType.FullName, p2.Name);
var p3 = GetPropertyInfo((Stream s) => s.CanSeek);
Console.WriteLine("{0}.{1}", p3.DeclaringType.FullName, p3.Name);
Output:
System.String.Length
System.DateTime.Minute
System.IO.Stream.CanSeek
Notice that we never needed any instances.
Get property name and type using lambda expression
Here's enough of an example of using Expressions to get the name of a property or field to get you started:
public static MemberInfo GetMemberInfo<T, U>(Expression<Func<T, U>> expression)
{
var member = expression.Body as MemberExpression;
if (member != null)
return member.Member;
throw new ArgumentException("Expression is not a member access", "expression");
}
Calling code would look like this:
public class Program
{
public string Name
{
get { return "My Program"; }
}
static void Main()
{
MemberInfo member = ReflectionUtility.GetMemberInfo((Program p) => p.Name);
Console.WriteLine(member.Name);
}
}
A word of caution, though: the simple statment of (Program p) => p.Name
actually involves quite a bit of work (and can take measurable amounts of time). Consider caching the result rather than calling the method frequently.
How to get property name from expression
Something like:
private static string GetMemberName(Expression expression)
{
switch(expression.NodeType)
{
case ExpressionType.MemberAccess:
return ((MemberExpression)expression).Member.Name;
case ExpressionType.Convert:
return GetMemberName(((UnaryExpression)expression).Operand);
default:
throw new NotSupportedException(expression.NodeType.ToString());
}
}
with:
public void Exec<T>(Expression<Func<T, object>> expression)
{
string name = GetMemberName(expression.Body);
// ...
}
Get a List of property name with Lambda
I agree with Jon Skeet's comment: nameof
is a better way to get arbitrary property names. But assuming that's absolutely not an option, here's one way to do it using expression trees:
public string[] GetPropNames<T1, T2>(Expression<Func<T1, T2>> expression)
{
var newExp = expression.Body as NewExpression;
if (newExp == null)
{
throw new ArgumentException();
}
var props = new List<string>(newExp.Arguments.Count);
foreach (var argExp in newExp.Arguments)
{
var memberExp = argExp as MemberExpression;
if (memberExp == null)
{
throw new ArgumentException();
}
props.Add(memberExp.Member.Name);
}
return props.ToArray();
}
Usage:
GetPropNames((MyObject o) => new { o.MyProp, o.MySecondProp, o.EtcProp })
This is pretty brittle, but it should demonstrate the general principle.
Pass property name by lambda expression for reading attribute values
You can split the work into two functions in order to bypass specifying all generic parameter type for a generic method restriction
public static object[] GetPropertyAttributes<TObject, TProperty>(
this TObject instance,
Expression<Func<TObject, TProperty>> propertySelector)
{
//consider handling exceptions and corner cases
var propertyName = ((PropertyInfo)((MemberExpression)propertySelector.Body).Member).Name;
var property = instance.GetType().GetProperty(propertyName);
return property.GetCustomAttributes(false);
}
public static T GetFirst<T>(this object[] input) where T : Attribute
{
//consider handling exceptions and corner cases
return input.OfType<T>().First();
}
then use it like
foo.GetPropertyAttributes(f => f.Bar)
.GetFirst<StringLengthAttribute>()
.MaximumLength;
Related Topics
Convert Webpage to Image from Asp.Net
C# Variance Problem: Assigning List≪Derived≫ as List≪Base≫
How to Get a Consistent Byte Representation of Strings in C# Without Manually Specifying an Encoding
Automating the Invokerequired Code Pattern
Can't Operator == Be Applied to Generic Types in C#
Filtering on Include in Ef Core
Entity Framework Code First - Two Foreign Keys from Same Table
Why Is Jsonrequestbehavior Needed
Communicate Between Two Windows Forms in C#
Create Generic Method Constraining T to an Enum
Validate a Username and Password Against Active Directory
What Does the =≫ Operator Mean in a Property
Create a Strongly Typed C# Object from Json Object With Id as the Name