How can I pass a property as a delegate? - c#

This is a theoretical question, I've already got a solution to my problem that took me down a different path, but I think the question is still potentially interesting.
Can I pass object properties as delegates in the same way I can with methods? For instance:
Let's say I've got a data reader loaded up with data, and each field's value needs to be passed into properties of differing types having been checked for DBNull. If attempting to get a single field, I might write something like:
if(!rdr["field1"].Equals(DBNull.Value)) myClass.Property1 = rdr["field1"];
But if I've got say 100 fields, that becomes unwieldy very quickly. There's a couple of ways that a call to do this might look nice:
myClass.Property = GetDefaultOrValue<string>(rdr["field1"]); //Which incidentally is the route I took
Which might also look nice as an extension method:
myClass.Property = rdr["field1"].GetDefaultOrValue<string>();
Or:
SetPropertyFromDbValue<string>(myClass.Property1, rdr["field1"]); //Which is the one that I'm interested in on this theoretical level
In the second instance, the property would need to be passed as a delegate in order to set it.
So the question is in two parts:
Is this possible?
What would that look like?
[As this is only theoretical, answers in VB or C# are equally acceptable to me]
Edit: There's some slick answers here. Thanks all.

I like using expression trees to solve this problem. Whenever you have a method where you want to take a "property delegate", use the parameter type Expression<Func<T, TPropertyType>>. For example:
public void SetPropertyFromDbValue<T, TProperty>(
T obj,
Expression<Func<T, TProperty>> expression,
TProperty value
)
{
MemberExpression member = (MemberExpression)expression.Body;
PropertyInfo property = (PropertyInfo)member.Member;
property.SetValue(obj, value, null);
}
Nice thing about this is that the syntax looks the same for gets as well.
public TProperty GetPropertyFromDbValue<T, TProperty>(
T obj,
Expression<Func<T, TProperty>> expression
)
{
MemberExpression member = (MemberExpression)expression.Body;
PropertyInfo property = (PropertyInfo)member.Member;
return (TProperty)property.GetValue(obj, null);
}
Or, if you're feeling lazy:
public TProperty GetPropertyFromDbValue<T, TProperty>(
T obj,
Expression<Func<T, TProperty>> expression
)
{
return expression.Compile()(obj);
}
Invocation would look like:
SetPropertyFromDbValue(myClass, o => o.Property1, reader["field1"]);
GetPropertyFromDbValue(myClass, o => o.Property1);

(Adding a second answer because it's on a completely different approach)
To address your original problem, which is more about wanting a nice API for mapping named values in a datareader to properties on your object, consider System.ComponentModel.TypeDescriptor - an often overlooked alternative to doing reflective dirtywork yourself.
Here's a useful snippet:
var properties = TypeDescriptor.GetProperties(myObject)
.Cast<PropertyDescriptor>()
.ToDictionary(pr => pr.Name);
That creates a dictionary of the propertydescriptors of your object.
Now I can do this:
properties["Property1"].SetValue(myObject, rdr["item1"]);
PropertyDescriptor's SetValue method (unlike System.Reflection.PropertyInfo's equivalent) will do type conversion for you - parse strings as ints, and so on.
What's useful about this is one can imagine an attribute-driven approach to iterating through that properties collection (PropertyDescriptor has an Attributes property to allow you to get any custom attributes that were added to the property) figuring out which value in the datareader to use; or having a method that receives a dictionary of propertyname - columnname mappings which iterates through and performs all those sets for you.
I suspect an approach like this may give you the API shortcut you need in a way that lambda-expression reflective trickery - in this case - won't.

Ignoring whether this is useful in your specific circumstances (where I think the approach you've taken works just fine), your question is 'is there a way to convert a property into a delegate'.
Well, there kind of might be.
Every property actually (behind the scenes) consists of one or two methods - a set method and/or a get method. And you can - if you can get a hold of those methods - make delegates that wrap them.
For instance, once you've got hold of a System.Reflection.PropertyInfo object representing a property of type TProp on an object of type TObj, we can create an Action<TObj,TProp> (that is, a delegate that takes an object on which to set the property and a value to set it to) that wraps that setter method as follows:
Delegate.CreateDelegate(typeof (Action<TObj, TProp>), propertyInfo.GetSetMethod())
Or we can create an Action<TProp> that wraps the setter on a specific instance of TObj like this:
Delegate.CreateDelegate(typeof (Action<TProp>), instance, propertyInfo.GetSetMethod())
We can wrap that little lot up using a static reflection extension method:
public static Action<T> GetPropertySetter<TObject, T>(this TObject instance, Expression<Func<TObject, T>> propAccessExpression)
{
var memberExpression = propAccessExpression.Body as MemberExpression;
if (memberExpression == null) throw new ArgumentException("Lambda must be a simple property access", "propAccessExpression");
var accessedMember = memberExpression.Member as PropertyInfo;
if (accessedMember == null) throw new ArgumentException("Lambda must be a simple property access", "propAccessExpression");
var setter = accessedMember.GetSetMethod();
return (Action<T>) Delegate.CreateDelegate(typeof(Action<T>), instance, setter);
}
and now I can get a hold of a 'setter' delegate for a property on an object like this:
MyClass myObject = new MyClass();
Action<string> setter = myObject.GetPropertySetter(o => o.Property1);
That's strongly typed, based on the type of the property itself, so it's robust in the face of refactoring and compile-time typechecked.
Of course, in your case, you want to be able to set your property using a possibly-null object, so a strongly typed wrapper around the setter isn't the whole solution - but it does give you something to pass to your SetPropertyFromDbValue method.

No, there's nothing akin to method group conversions for properties. The best you can do is to use a lambda expression to form a Func<string> (for a getter) or an Action<string> (for a setter):
SetPropertyFromDbValue<string>(value => myClass.Property1 = value,
rdr["field1"]);

Worth mentioning you can do this with some reflection trickery..
something like...
public static void LoadFromReader<T>(this object source, SqlDataReader reader, string propertyName, string fieldName)
{
//Should check for nulls..
Type t = source.GetType();
PropertyInfo pi = t.GetProperty(propertyName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
object val = reader[fieldName];
if (val == DBNull.Value)
{
val = default(T);
}
//Try to change to same type as property...
val = Convert.ChangeType(val, pi.PropertyType);
pi.SetValue(source, val, null);
}
then
myClass.LoadFromReader<string>(reader,"Property1","field1");

As others have pointed, static reflexion is the way to go.
Those classes work out of the box :
http://www.codeproject.com/Articles/36262/Getting-Fun-with-Net-Static-Reflection.aspx

Related

Using Reflection on Type T to create Expression<Func<T, TValue>> for Properties that have attributes

I'm currently exploring the viability to encapsulate some logic that would typically be iterative. Using a 3rd party library it has a generic Html Helper method that allows you to map properties from a Generic T class to a table component. Typically you'd have to write something to the effect of:
HtmlHelper.GenerateTable<ClassName>
.configure(tableDefinition =>{
// Adding each property you want to render here
tableDefinition.add(myClassRef => myClassRef.propertyA);
tableDefinition.add(myClassRef => myClassRef.propertyB);
})
I'm exploring the idea of adding attributes to properties such as a standard Display attribute and then using reflection, adding that property to the container. The add method only accepts an argument of type Expression<Func<T, TValue>>. With my current understanding of reflection I know I can property identify properties that would be applicable by looping through PropertyInfo and checking for the attribute I want using GetCustomAttribute.
What I'm stumped on is if it's possible using reflection to supply the argument type that the add method expects?
I'll throw the helper method logic I've started using so far. My assumption is that this is leading me towards Expression and Lambda classes, but I haven't been able to flesh out anything that works since I don't technically have a TValue.
var t = typeof(T);
List<PropertyInfo> properties = t.GetProperties().ToList();
foreach(var prop in properties)
{
var attr = (DisplayAttribute[])prop.GetCustomAttributes(typeof(DisplayAttribute), false);
if (attr.Length > 0)
{
// TODO: Call add with expression?
}
}
You can achieve this fairly easily with the methods on the Expression class. To start with, it's easiest to write expressions using lambdas (e.g. Expression<Func<A, B>> expr = x => x.PropertyA), then inspect it in a debugger to see what the compiler constructs.
In your case, something like this should work:
// The parameter passed into the expression (myClassRef) in your code
var parameter = Expression.Parameter(typeof(T), "myClassRef");
// Access the property described by the PropertyInfo 'prop' on the
// myClassRef parameter
var propertyAccess = Expression.Property(parameter, prop);
// Since we're returning an 'object', we'll need to make sure we box value types.
var box = Expression.Convert(propertyAccess, typeof(object));
// Construct the whole lambda
var lambda = Expression.Lambda<Func<T, object>>(box, parameter);
tableDefinition.add(lambda);
Note that I'm passing an Expression<Func<T, object>> into add, rather than an Expression<Func<T, TValue>>. I'm guessing that this doesn't matter, and it avoids having to call add using reflection. When I've written methods similar to add in the past, I don't care about TValue at all: I just inspect the Expression and fetch the PropertyInfo from the property access.
If you do need to pass an Expression<Func<T, TValue>> you'll have to do something like:
var delegateType = typeof(Func<,>).MakeGenericType(typeof(T), prop.PropertyType);
var lambda = Expression.Lambda(delegateType, propertyAccess, parameter);
// I'm assuming that your `add` method looks like:
// void add<T, TValue>(Expression<Func<T, TValue>> expr)
// I'm also assuming there's only one method called 'add' -- be smarter in that
// 'GetMethod' call if not.
var addMethod = typeof(TableDefinition)
.GetMethod("add")
.MakeGenericMethod(typeof(T), prop.PropertyType);
addMethod.Invoke(tableDefinition, new object[] { lambda });
Note that you don't need the Expression.Convert(..., typeof(object)) in this case.

How to cast an `IEnumerable<Unknown T>` to `IEnumerable<Whatever>`

I am working on a project to filter a list in a generic way. I am retrieving an IEnumerable<T> at runtime but I don't know what is T. I need to cast the list I am retrieving to IEnumerable<T> and not IEnumerable, because I need extension methods like ToList and Where. Here is my code.
private IList<object> UpdateList(KeyValuePair<string, string>[] conditions)
{
// Here I want to get the list property to update
// The List is of List<Model1>, but of course I don't know that at runtime
// casting it to IEnumerable<object> would give Invalid Cast Exception
var listToModify = (IEnumerable<object>)propertyListInfoToUpdate.GetValue(model);
foreach (var condition in conditions)
{
// Filter is an extension method defined below
listToModify = listToModify.Filter(condition .Key, condition .Value);
}
// ToList can only be invoked on IEnumerable<T>
return listToModify.ToList();
}
public static IEnumerable<T> Filter<T>(this IEnumerable<T> source, string propertyName, object value)
{
var parameter = Expression.Parameter(typeof(T), "x");
var property = Expression.Property(parameter, propertyName);
var propertyType = ((PropertyInfo)property.Member).PropertyType;
Expression constant = Expression.Constant(value);
if (((ConstantExpression)constant).Type != propertyType)
{ constant = Expression.Convert(constant, propertyType); }
var equality = Expression.Equal(property, constant);
var predicate = Expression.Lambda<Func<T, bool>>(equality, parameter);
var compiled = predicate.Compile();
// Where can only be invoked on IEnumerable<T>
return source.Where(compiled);
}
Also please note that I cannot retrieve the List like this
((IEnumerable)propertyListInfoToUpdate.GetValue(model)).Cast<object>()
since it will generate the below exception in the Filter extention
ParameterExpression of type 'Model1' cannot be used for delegate parameter of type 'System.Object'
Use GetGenericArguments and MakeGenericMethod to interface generic signatures.
private IList<object> UpdateList(KeyValuePair<string, string> conditions)
{
var rawList = (IEnumerable)propertyListInfoToUpdate.GetValue(model);
var listItemType = propertyListInfoToUpdate.PropertyType.GetGenericArguments().First();
var filterMethod = this.GetType().GetMethod("Filter").MakeGenericMethod(genericType);
object listToModify = rawList;
foreach (var condition in conditions)
{
listToModify = filterMethod.Invoke(null, new object[] { listToModify, condition.Key, condition.Value })
}
return ((IEnumerable)listToModify).Cast<object>().ToList();
}
Assuming your propertyListInfoToUpdate is a PropertyInfo and the property type is List<T>.
Why are you using Expression at all? It's hard to understand your question without a good Minimal, Complete, and Verifiable code example. Even if we could solve the casting question, you're still returning IList<object>. It's not like the consumer of the code will benefit from the casting.
And it's not really possible to solve the casting problem, at least not in the way you seem to want. A different approach would be to call the Filter() dynamically. In the olden days, we'd have to do this by using reflection, but the dynamic type gives us runtime support. You could get it to work something like this:
private IList<object> UpdateList(KeyValuePair<string, string>[] conditions)
{
dynamic listToModify = propertyListInfoToUpdate.GetValue(model);
foreach (var condition in conditions)
{
// Filter is an extension method defined below
listToModify = Filter(listToModify, condition.Key, condition.Value);
}
// ToList can only be invoked on IEnumerable<T>
return ((IEnumerable<object>)Enumerable.Cast<object>(listToModify)).ToList();
}
NOTE: your original code isn't valid; I made the assumption that conditions is supposed to be an array, but of course if you change it to anything that has a GetEnumerator() method, that would be fine.
All that said, it seems to me that given the lack of a compile-time type parameter, it would be more direct to just change your Filter() method so that it's not generic, and so that you use object.Equals() to compare the property value to the condition value. You seem to be jumping through a lot of hoops to use generics, without gaining any of the compile-time benefit of generics.
Note that if all this was about was executing LINQ query methods, that could be addressed easily simply by using Enumerable.Cast<object>() and using object.Equals() directly. It's the fact that you want to use expressions to access the property value (a reasonable goal) that is complicating the issue. But even there, you can stick with IEnumerable<object> and just build the object.Equals() into your expression.
Creating an expression and compiling it every time is very expensive. You should either use Reflection directly or a library like FastMember (or cache the expressions). In addition, your code uses Expression.Equal which translates into the equality operator (==), which is not a good way of comparing objects. You should be using Object.Equals.
Here is the code using FastMember:
private IList<object> UpdateList(KeyValuePair<string, string>[] conditions)
{
var listToModify = ((IEnumerable)propertyListInfoToUpdate.GetValue(model)).Cast<object>();
foreach (var condition in conditions)
{
listToModify = listToModify.Where(o => Equals(ObjectAccessor.Create(o)[condition.Key], condition.Value));
}
return listToModify.ToList();
}
Side note - your Filter method doesn't really need generics. It can be changed to accept and return an IEnumerable<object> by tweaking the expression, which also would've solved your problem.

Is reflection used when retrieving information from a linq expression?

I have the following extension method:
public static string ToPropertyName<T,E>(this Expression<Func<E, T>> propertyExpression)
{
if (propertyExpression == null)
return null;
string propName;
MemberExpression propRef = (propertyExpression.Body as MemberExpression);
UnaryExpression propVal = null;
// -- handle ref types
if (propRef != null)
propName = propRef.Member.Name;
else
{
// -- handle value types
propVal = propertyExpression.Body as UnaryExpression;
if (propVal == null)
throw new ArgumentException("The property parameter does not point to a property", "property");
propName = ((MemberExpression)propVal.Operand).Member.Name;
}
return propName;
}
I use linq expression when passing property names instead of strings to provide strong typing and I use this function to retrieving the name of the property as a string. Does this method use reflection?
My reason for asking is this method is used quite a lot in our code and I want it to be reasonably fast enough.
As far as I know, reflection is not involved in the sense that some kind of dynamic type introspection happens behind the scenes. However, types from the System.Reflection such as Type or PropertyInfo are used together with types from the System.Linq.Expressions namespace. They are used by the compiler only to describe any Func<T,E> passed to your method as an abstract syntax tree (AST). Since this transformation from a Func<T,E> to an expression tree is done by the compiler, and not at run-time, only the lambda's static aspects are described.
Remember though that building this expression tree (complex object graph) from a lambda at run-time might take somewhat longer than simply passing a property name string (single object), simply because more objects need to be instantiated (the number depends on the complexity of the lambda passed to your method), but again, no dynamic type inspection à la someObject.GetType() is involved.
Example:
This MSDN article shows that the following lambda expression:
Expression<Func<int, bool>> lambda1 = num => num < 5;
is transformed to something like this by the compiler:
ParameterExpression numParam = Expression.Parameter(typeof(int), "num");
ConstantExpression five = Expression.Constant(5, typeof(int));
BinaryExpression numLessThanFive = Expression.LessThan(numParam, five);
Expression<Func<int, bool>> lambda1 =
Expression.Lambda<Func<int, bool>>(
numLessThanFive,
new ParameterExpression[] { numParam });
Beyond this, nothing else happens. So this is the object graph that might then be passed into your method.
Since you're method naming is ToPropertyName, I suppose you're trying to get the typed name of some particular property of a class. Did you benchmark the Expression<Func<E, T>> approach? The cost of creating the expression is quite larger and since your method is static, I see you're not caching the member expression as well. In other words even if the expression approach doesn't use reflection the cost can be high. See this question: How do you get a C# property name as a string with reflection? where you have another approach:
public static string GetName<T>(this T item) where T : class
{
if (item == null)
return string.Empty;
return typeof(T).GetProperties()[0].Name;
}
You can use it to get name of property or a variable, like this:
new { property }.GetName();
To speed up further, you would need to cache the member info. If what you have is absolutely Func<E, T> then your approach suits. Also see this: lambda expression based reflection vs normal reflection
A related question: Get all the property names and corresponding values into a dictionary

Property Lambda expression gets an additional Convert(p=>p.Property)

I have a problem where in some cases (appears to be where property type is bool) a lambda expression used to refer to a property. I use this to get its name; the problem is sometime the expression is getting modified to have an additional Convert() function.
e.g.
GetPropertyName<TSource>(Expression<Func<TSource, object>> propertyLambda) {...}
var str = GetPropertyName<MyObject>(o=>o.MyBooleanProperty);
What's happening it that the propertyLambda looks like Convert(o.MyBooleanProperty) and not o.MyBooleanProperty that i'd expect.
The Convert is added, because o.MyBooleanProperty is a bool, but the result has to be an object. If you made your method generic both in the source object type and the result type, then there would be no Convert:
GetPropertyName<TSource, TResult>(Expression<Func<TSource, TResult>> propertyLambda)
Unfortunately this means you have to specify TResult explicitly:
GetPropertyName<MyObject, bool>(o => o.MyBooleanProperty)
If you don't want to do that, you would have to find some way to infer MyObject, or avoid needing it.
For example, if the current object is MyObject (and you're in an instance method), you could change your code to take Func<TResult>:
GetPropertyName(() => this.MyBooleanProperty)
Or you could include another parameter of type TSource that will help you infer the type:
GetPropertyName(myObject, o => o.MyBooleanProperty)

Pass a property of a Linq entity in a method to set and get result

I'm trying to pass in a property of a Linq entity to be used by my method. I can easily pass a property to be queried
Func<Entities.MyEntity, ResultType> GetProperty = ent => ent.Property;
However this returns ResultType and cannot be used to set the property.
I thought about using reflection to get a propertyInfo, but this will let me fetch the property but then I can't use Linq syntax to call my property. Is there any guru out there that knows how to do this?
I have a hunch I could do it by constructing a chunk of an expression tree and applying it onto the query...
I was really hoping to do something like:
var value = myQueryEntity.CallMagicFunction(); //typesafe
myQueryEntity.CallMagicFunction() = value; //typesafe
Indeed, an expression tree should work; for basic member access (a field/property directly off the object):
static MemberInfo ReadMember(LambdaExpression expr)
{
if(expr == null) throw new ArgumentNullException("expr");
MemberExpression me = expr.Body as MemberExpression;
if(me == null || !ReferenceEquals(me.Expression, expr.Parameters[0])) {
throw new ArgumentException("expr");
}
return me.Member;
}
with
Expression<Func<Customer, int>> func = c => c.Id;
MemberInfo member = ReadMember(func);
// for simplicity assume prop:
PropertyInfo prop = (PropertyInfo)member;
From there you can do pretty much anything; in particular you can get the get/set accessors (if you want to create a delegate), or use GetValue / SetValue.
Note that in .NET 4.0 you can set properties directly on an Expression (but the C# compiler doesn't add any extra support for this, so you'd need to write your own Expression by hand).

Categories

Resources