I use a method similar to the following to get some precomputed metadata related to a Type's properties.
MyData GetProperty<T, U>(Expression<Func<T, U>> member)
{
// Get the property referenced in the lambda expression
MemberExpression expression = member.Body as MemberExpression;
PropertyInfo property = expression.Member as PropertyInfo;
// get the properties in the type T
PropertyInfo[] candidates = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
// Find the match
foreach (PropertyInfo candidate in candidates)
if (candidate == property)
return GetMetaData<T>(candidate);
throw new Exception("Property not found.");
}
// Returns precomputed metadata
MyData GetMetaData<T>(PropertyInfo property) { ... }
As you would expect, it works when used as follows:
var data = PropertyInfo((Employee e) => e.Name);
But not when used in the following generic method:
void MyGenericMethod<T>(int id) where T : IEmployee
{
var data = PropertyInfo((T e) => e.Name);
}
It fails because the declaring type of property in the first method is now IEmployee, so the property in the lambda doesn't match the property in the type. How can I get them to match, without relying on the names of the properties? (There can be multiple properties with the same name if interfaces are implemented explicitly, so p1.Name == p2.Name won't cut it).
What you'd probably need is an InterfaceMapping. You can get that from the actual type by calling GetInterfaceMap(typeof(interface)), i.e.,
InterfaceMapping mapping = typeof(Employee).GetInterfaceMap(typeof(IEmployee));
Now, the mapping will contain the fields InterfaceMethods which will contain the methods you see when reflecting the interface, and TargetMethods which are the class's implementing methods. Note that this maps the the getter methods from the interface to the getter methods from the target class. You'll need to find the proper interface property by mapping the getter method of the various properties of the class to the found getter method.
Type interfaceType = typeof(IEmployee);
Type classType = typeof(Employee);
PropertyInfo nameProperty = interfaceType.GetProperty("Name");
MethodInfo nameGetter = nameProperty.GetGetMethod();
InterfaceMapping mapping = classType.GetInterfaceMap(interfaceType);
MethodInfo targetMethod = null;
for (int i = 0; i < mapping.InterfaceMethods.Length; i++)
{
if (mapping.InterfaceMethods[i] == nameGetter)
{
targetMethod = mapping.TargetMethods[i];
break;
}
}
PropertyInfo targetProperty = null;
foreach (PropertyInfo property in classType.GetProperties(
BindingFlags.Instance | BindingFlags.GetProperty |
BindingFlags.Public | BindingFlags.NonPublic)) // include non-public!
{
if (targetMethod == property.GetGetMethod(true)) // include non-public!
{
targetProperty = property;
break;
}
}
// targetProperty is the actual property
Caution: Note the use of BindingFlags.NonPublic and GetGetMethod(true) here, for accessing private members. If you've got an explicit interface implementation, there isn't really a public property matching the interface's property, instead there is a private property named Some.NameSpace.IEmployee.Name that is mapped (which is, of course, your explicit implementation).
When you've found the right property, you can just call
ParameterExpression p = Expression.Parameter("e", typeof(T));
Expression<Func<T, U>> lambda = Expression.Lambda<Func<T, U>>(
Expression.Property(p, targetProperty), p);
and you've got yourself a lambda expression that uses the class's properties rather than the interface's properties.
Does BindingFlags.FlattenHierarchy work? If not, you could always iterate through typeof(T).GetInterfaces and call GetProperties on each of them.
You'll need to get the member name from the lambda expression, and use reflection to get that member off of the type you've been given:
public static PropertyInfo PropInfo<TContainer, TMember>(
Expression<Func<TContainer, TMember>> memberGetter)
{
var memberName = GetExpressionMemberName(memberGetter);
return typeof(TContainer).GetProperty(memberName);
}
public static string GetExpressionMemberName<TContainer, TMember>(
Expression<Func<TContainer, TMember>> memberGetter)
{
var expressionType = memberGetter.Body.NodeType;
switch (expressionType)
{
case ExpressionType.MemberAccess:
{
var memberExpr = (MemberExpression) memberGetter.Body;
return memberExpr.Member.Name;
}
case ExpressionType.Convert:
{
var convertExpr = (UnaryExpression) memberGetter.Body;
var memberExpr = (MemberExpression) convertExpr.Operand;
return memberExpr.Member.Name;
}
default:
throw new InvalidOperationException("Expression {0} does not represent a simple member access.");
}
}
Here's proof that it works:
void Main()
{
Console.WriteLine(
MyGenericMethod<Employee>()
.GetGetMethod()
.Invoke(
new Employee {Name = "Bill"},
new object[] {}));
}
public class Employee : IEmployee {
public string Name {get;set;}
string IEmployee.Name { get { throw new Exception(); } }
}
public interface IEmployee {string Name {get;}}
public PropertyInfo MyGenericMethod<T>() where T : IEmployee
{
return PropInfo((T e) => e.Name);
}
Console output:
Bill
Related
I'm using simplemvvmtoolkit for validation (INotifyDataErrorInfo). Instead of me repeating my self over and over for each property within the view model, Id like to use reflection to get all the properties and validate them, I can't seem to figure out what to pass in the validateProperty method though.
private void ValidateInput()
{
var unitProperties = this.GetType().GetProperties()
.Where(x => x.CanRead);
foreach (var prop in unitProperties)
ValidateProperty(prop, prop.GetValue(this, null)); //????
//? ^ get errors here
}
ValidateProperty takes in:
protected virtual void ValidateProperty<TResult>(Expression<Func<TViewModel, TResult>> property, object value);
The problem is Expression<Func<TViewModel, TResult>> has absolutely no relation to PropertyInfo (the type returned by GetProperties). You'll also run into problems because the type of the result is not known at compile time.
The easiest solution would be to change ValidateProperty to accept a PropertyInfo:
protected virtual void ValidateProperty(PropertyInfo property, object value);
You could also convert the PropertyInfo to an Expression, but that's a bit more difficult:
var method = this.GetType().GetMethod("ValidateProperty");
foreach (var prop in unitProperties)
{
var parameter = Expression.Parameter(this.GetType(), "_");
var property = Expression.Property(parameter, prop);
var lambda = Expression.Lambda(property, parameter);
var genericMethod = method.MakeGenericMethod(prop.PropertyType);
genericMethod.Invoke(this, new object[] { lambda, prop.GetValue(this, null) });
}
When I try to get the custom attributes from an object the function returns null. Why?
class Person
{
[ColumnName("first_name")]
string FirstName { get; set; }
Person()
{
FirstName = "not important";
var attrs = AttributeReader.Read(FirstName);
}
}
static class AttributeReader
{
static object[] Read(object column)
{
return column.GetType().GetCustomAttributes(typeof(ColumnNameAttribute), false);
}
}
You are passing a string, "not important" to that method. The Type is therefore typeof(string). Which does not have those attributes. Further, even Person doesn't have that attribute: only the MemberInfo (FirstName) has them.
There are ways of doing that by passing an Expression:
public static ColumnNameAttribute[] Read<T>(Expression<Func<T>> func)
{
var member = func.Body as MemberExpression;
if(member == null) throw new ArgumentException(
"Lambda must resolve to a member");
return (ColumnNameAttribute[])Attribute.GetCustomAttributes(
member.Member, typeof(ColumnNameAttribute), false);
}
with
var attrs = AttributeReader.Read(() => FirstName);
However! I should advise that I'm not sure that the Person constructor is an appropriate place for this. Probably needs caching.
If you don't want to use lambdas, then passing a Type and the member-name would work too, i.e.
var attrs = AttributeReader.Read(typeof(Person), "FirstName");
(and do reflection from there) - or mixing with generics (for no real reason):
var attrs = Attribute.Read<Person>("FirstName");
What I want to do is take any class type and create a list of 'get' accessors to all of the properties in the object graph.
The exact format, order, etc of the collection doesn't matter, I just don't quite know how to start off identifying and creating accessors to all of the properties. It might take the form of something like this:
public static List<Func<T,object>> CreateAccessors<T>()
{
Type t = typeof(T);
// Identify all properties and properties of properties (etc.) of T
// Return list of lambda functions to access each one given an instance of T
}
public void MyTest()
{
MyClass object1;
var accessors = CreateAccessors<MyClass>();
var myVal1 = accessors[0](object1);
var myVal2 = accessors[1](object1);
// myVal1 might now contain the value of object1.Property1
// myVal2 might now contain the value of object1.Property4.ThirdValue.Alpha
}
You can use reflection to extract the properties and expression-trees to help build the delegates targeting the property getters:
// Start with all public instance properties of type
var accessors = from property in type.GetProperties
(BindingFlags.Instance | BindingFlags.Public)
// Property must be readable
where property.CanRead
//Assemble expression tree of the form:
// foo => (object) foo.Property
// foo
let parameter = Expression.Parameter(type, "foo")
// foo.Property
let propertyEx = Expression.Property(parameter, property)
// (object)foo.Property - We need this conversion
// because the property may be a value-type.
let body = Expression.Convert(propertyEx, typeof(object))
// foo => (object) foo.Property
let expr = Expression.Lambda<Func<T,object>>(body, parameter)
// Compile tree to Func<T,object> delegate
select expr.Compile();
return accessors.ToList();
Note that although Delegate.CreateDelegate seems like an obvious choice, you will have some problems boxing value-type properties. Expression-trees dodge this problem elegantly.
Note that you'll need some more work to be able to get "nested" properties out too, but hopefully I've given ypu enough to get you started (hint: recurse). One final pointer with that: watch out for cycles in the object graph!
public static List<Func<T, object>> CreateAccessors<T>()
{
var accessors = new List<Func<T, object>>();
Type t = typeof(T);
foreach (PropertyInfo prop in t.GetProperties(BindingFlags.Instance | BindingFlags.Public)) {
if (prop.CanRead) {
var p = prop;
accessors.Add(x => p.GetValue(x, null));
}
}
return accessors;
}
EDIT:
Here is a variant which returns compiled expressions and should be much faster than the previous one using reflection
public static List<Func<T, object>> CreateAccessorsCompiled<T>()
{
var accessors = new List<Func<T, object>>();
Type t = typeof(T);
foreach (PropertyInfo prop in t.GetProperties(BindingFlags.Instance | BindingFlags.Public)) {
if (prop.CanRead) {
ParameterExpression lambdaParam = Expression.Parameter(t, "instance");
Expression bodyExpression;
MemberExpression memberAccessExpression = Expression.MakeMemberAccess(Expression.Convert(lambdaParam, t), prop);
if (prop.PropertyType == typeof(object)) { // Create lambda expression: (instance) => ((T)instance).Member
bodyExpression = memberAccessExpression;
} else { // Create lambda expression: (instance) => (object)((T)instance).Member
bodyExpression = Expression.Convert(memberAccessExpression, typeof(object));
}
var lambda = Expression.Lambda<Func<T, object>>(bodyExpression, lambdaParam);
accessors.Add(lambda.Compile());
}
}
return accessors;
}
Suppose I have:
class Person
{
[ColumnAttribute("ID"]
public int Id;
[ColumnAttribute("Name"]
public string Name;
[ColumnAttribute("DateOfBirth"]
public date BirthDate;
}
i want to create a method that will be called as following
GetPropertyColumn<Person>(e=>e.Name)
this method will return a string that is defined by the ColumnAttribute attribute.
the problem is that i am not able to define this method.
i tried
public string GetPropertyColumn<T,U>(Expression<Func<T, U>> Lamda)
but the problem is that i can only specify the T and not the U so its not working.
any help?
thanks
Edit:
thanks for the answers but i got a lot of answers in which you need to instantinate Person but i dont want to.
because i only want to know the column given a property defined inside a Class.
If you have a generic method with 2 generic types (T and U) then both most be specified, or both most be inferred. If that isn't possible, consider an expression taking Func<T,object> (remove U), and remove the cast/convert node from the expression tree when inspecting it at runtime. You can also do things to make both types inferred, but that may need more of a refactor.
[EDIT 3]
public static string GetPropertyColumn<T>(Expression<Func<T,object>> f)
{
Type t = typeof(T);
MemberExpression memberExpression = null;
if (f.Body.NodeType == ExpressionType.Convert)
{
memberExpression = ((UnaryExpression)f.Body).Operand as MemberExpression;
}
else if (f.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpression = f.Body as MemberExpression;
}
string name = memberExpression.Member.Name;
System.Reflection.FieldInfo fi = t.GetField(name);
object[] attrs = fi.GetCustomAttributes(true);
foreach (var attr in attrs)
{
ColumnAttribute columnAttr = attr as ColumnAttribute;
if (columnAttr != null)
{
return columnAttr.Name;
}
}
return string.Empty;
}
using:
Console.WriteLine(GetPropertyColumn<Person>(e => e.Name));
Make it an extension method:
static class ExtensionMethods
{
public static string GetPropertyColumn<T,U>(this T obj, Expression<Func<T, U>> selector)
{
... // whatever
}
}
And use it as follows:
Person person = ...
string propertyColumn = person.GetPropertyColumn(p => p.Name);
You don't need to specify T, because it's inferred from the first parameter, and you don't need to specify U either because it is inferred from the return type of the lambda
Note that you don't need to define it as an extension method, it could also be a normal method. You would then use it like that:
Person person = ...
string propertyColumn = GetPropertyColumn(person, p => p.Name);
I am trying to write a function that will pull the name of a property and the type using syntax like below:
private class SomeClass
{
Public string Col1;
}
PropertyMapper<Somewhere> propertyMapper = new PropertyMapper<Somewhere>();
propertyMapper.MapProperty(x => x.Col1)
Is there any way to pass the property through to the function without any major changes to this syntax?
I would like to get the property name and the property type.
So in the example below i would want to retrieve
Name = "Col1" and Type = "System.String"
Can anyone help?
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.
This can be easily done in C# 6. To get the name of property use nameof operator.
nameof(User.UserId)
and to get type of property use typeof operator.
typeof(User.UserId)
I found this very useful.
public class PropertyMapper<T>
{
public virtual PropertyInfo PropertyInfo<U>(Expression<Func<T, U>> expression)
{
var member = expression.Body as MemberExpression;
if (member != null && member.Member is PropertyInfo)
return member.Member as PropertyInfo;
throw new ArgumentException("Expression is not a Property", "expression");
}
public virtual string PropertyName<U>(Expression<Func<T, U>> expression)
{
return PropertyInfo<U>(expression).Name;
}
public virtual Type PropertyType<U>(Expression<Func<T, U>> expression)
{
return PropertyInfo<U>(expression).PropertyType;
}
}
I made this little class to follow the original request.
If you need the name of the property you can use it like this:
PropertyMapper<SomeClass> propertyMapper = new PropertyMapper<SomeClass>();
string name = propertyMapper.PropertyName(x => x.Col1);
I just thought I would put this here to build on the previous approach.
public static class Helpers
{
public static string PropertyName<T>(Expression<Func<T>> expression)
{
var member = expression.Body as MemberExpression;
if (member != null && member.Member is PropertyInfo)
return member.Member.Name;
throw new ArgumentException("Expression is not a Property", "expression");
}
}
You can then call it in the following fashion:
Helpers.PropertyName(() => TestModel.TestProperty);
I should also point out that with VS 2015 and C# 6.0 you can simply use nameof.
https://msdn.microsoft.com/en-us/library/dn986596.aspx