views:

58

answers:

2

We have some code that given a property name uses reflection to implement a Comparer.

I would like to store a delegate/Func to get the value rather than paying the reflection price each time we need to get a value.

Given a class like this:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

I tried to write a function that would create a delegate for me

Func<T, object> CreateGetFuncFor<T>(string propertyName)
{
    PropertyInfo prop = typeof(T).GetProperty(propertyName);

    return (Func<T, object>)Delegate.CreateDelegate(typeof(Func<T, object>), 
                                                    null, 
                                                    prop.GetGetMethod());
}

The following code works fine for the getting the Name

var person = new Person { Name = "Dave", Age = 42 };

var funcitonToGetName = CreateGetFuncFor<Person>("Name");
Console.WriteLine(funcitonToGetName(person));

var functionToGetAge = CreateGetFuncFor<Person>("Age");

but for the Age proerty it throws an ArgumentException with the message "Error binding to target method"

What am I missing? Is there another way to do it?

+1  A: 

Its probably because Age is essentially defined as:

public int Age {get; private set;}

and a method returning an int is not implicitly convertible to a method returning an object, whereas String is.

try:

Func<T, R> CreateGetFuncFor<T, R>(string propertyName)
{
    PropertyInfo prop = typeof(T).GetProperty(propertyName);
    return (Func<T, R>)Delegate.CreateDelegate(typeof(Func<T, R>), 
                                                    null, 
                                                    prop.GetGetMethod());
}

and then

var functionToGetAge = CreateGetFuncFor<Person, int>("Age");
luke
Unfortunately in the code where I want to use this I don't know what type the propery is in advance.
Argos
what are you going to do with Age if you don't know the type? can you expand on your use case a little bit.
luke
eventually the values get used in a call to System.Collections.Comparer.Default.Compare
Argos
+2  A: 

It seems odd that you know the declaring type at compile-time but not the property type. Anyway...

You'll need an extra step to convert the property value to an object so that it matches the Func<T,object> delegate's return type. (The extra step isn't strictly necessary for reference-typed properties, but doesn't do any harm.)

Func<T, object> CreateGetFuncFor<T>(string propertyName)
{
    var parameter = Expression.Parameter(typeof(T), "obj");
    var property = Expression.Property(parameter, propertyName);
    var convert = Expression.Convert(property, typeof(object));
    var lambda = Expression.Lambda(typeof(Func<T, object>), convert, parameter);

    return (Func<T, object>)lambda.Compile();
}
LukeH
Oh I agree - but I can't currently change those parts of the code.
Argos