5

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?

0

2 Answers 2

8

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();
}
Sign up to request clarification or add additional context in comments.

1 Comment

Oh I agree - but I can't currently change those parts of the code.
2

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");

3 Comments

Unfortunately in the code where I want to use this I don't know what type the propery is in advance.
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.
eventually the values get used in a call to System.Collections.Comparer.Default.Compare

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.