views:

117

answers:

2

I want to know if there is a better way (than what I'm currently doing) to obtain and hold a reference to a property in another object using only the object and property string names. Particularly, is there a better way to do this with the new dynamic functionality of .Net 4.0?

Here is what I have right now.

I have a "PropertyReference<T>" object that takes an object name and property name in the constructor.

An Initialize() method uses reflection to find the object and property and stores the property Getter as an Action<T> and the property Setter as an Func<T>.

When I want to actually call the property I do something like this:

int x = _propertyReference.Get();

or

_propertyReference.Set(2);

Here is my PropertyReference<T> code. Please dissect and make suggestions for improvement.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Xml;

namespace WindowsFormsApplication2
{
    public class PropertyReference<T> : IPropertyReference
    {
        public string ComponentName { get; set; }
        public string PropertyName { get; set; }
        public bool IsInitialized
        {
            get
            {
                return (_action != null && _func != null);
            }
        }

        Action<T> _action;
        Func<T> _func;

        public PropertyReference() { }

        public PropertyReference(string componentName, string propertyName)
        {
            ComponentName = componentName;
            PropertyName = propertyName;
        }

        public void Initialize(IEntity e)
        {            
            Object component = e.GetByName(ComponentName);
            if (component == null) return;

            Type t = e.GetByName(ComponentName).GetType();
            PropertyInfo pi = t.GetProperty(PropertyName);

            _action = (T a) => pi.SetValue(component, a, null);
            _func = () => (T)pi.GetValue(component, null);
        }

        public void Reset()
        {
            _action = null;
            _func = null;

        }

        public void Set(T value)
        {
            _action.Invoke(value);
        }

        public T Get()
        {
            return _func();
        }

    }
}

Note: I can't use the "Emit" functionality as I need this code to work on the new Windows Phone 7 and that does not support Emit.

UPDATE:

Just did some speed tests after replacing:

_action = (T a) => pi.SetValue(component, a, null);
_func = () => (T)pi.GetValue(component, null);

With

_action = Action<T>)Delegate.CreateDelegate(typeof(Action<T>),component,pi.GetSetMethod());
_func = (Func<T>)Delegate.CreateDelegate(typeof(Func<T>), component, pi.GetGetMethod());

As suggested by dtb below.

Tested by making 100,000 calls to the Get() property. Here are the results.

_func = () => (T)pi.GetValue(component, null)

took about 200ms

_func = (Func<T>)Delegate.CreateDelegate(typeof(Func<T>), component, pi.GetGetMethod());

took about 10ms

Huge difference. Wasn't expecting that, but cool!

Still open to more improvements.

+4  A: 

You could obtain delegates directly representing the getter and setter method:

object component;
PropertyInfo pi;

_action = (Action<T>)Delegate.CreateDelegate(typeof(Action<T>),
                                             component,
                                             pi.GetSetMethod());

_func = (Func<T>)Delegate.CreateDelegate(typeof(Func<T>),
                                         component,
                                         pi.GetGetMethod());
dtb
This looks more concise then what I have. I'll try to incorporate this. Still looking for other ideas if anyone has them.
Jeff Weber
A: 

It really depends on how often you're going to be calling it. If it isn't massive throughput, then fine - but note that the reflection-based GetValue / SetValue are pretty slow. You could cache the delegates, but another simple approach might be to look at HyperDescriptor - this uses the same API as PropertyDescriptor (so you again get GetValue / SetValue), but it uses dynamic methods underneath. The API then is something like:

PropertyDescriptor prop = TypeDescriptor.GetProperties(type)["PropertyName"];

or

PropertyDescriptor prop = TypeDescriptor.GetProperties(obj)["PropertyName"];

then

object value = prop.GetValue(component);
prop.SetValue(component, newValue);
Marc Gravell
Fasterflect (http://fasterflect.codeplex.com) seems to be a more comprehensive library with a purpose similar to that of HyperDescriptor, although he did mention that Reflection.Emit was not an option (which would rule out both of these suggestions).
Morten Mertner
@Morten - I missed that caveat in the question - so yes, that rules them out.
Marc Gravell
Bummer, this sounded like a good solution. I did look at Fasterflect and emailed the devs which is how I found out Emit does not work on the Windows Phone 7
Jeff Weber