Каков наилучший способ получить и сохранить ссылку на свойство по имени в C # - PullRequest
2 голосов
/ 30 марта 2010

Я хочу знать, есть ли лучший способ (чем то, что я сейчас делаю) получить и сохранить ссылку на свойство в другом объекте, используя только имена строк объекта и свойства. В частности, есть ли лучший способ сделать это с новой динамической функциональностью .Net 4.0?

Вот то, что у меня сейчас есть.

У меня есть объект "PropertyReference<T>", который принимает имя объекта и имя свойства в конструкторе.

Метод Initialize() использует отражение для поиска объекта и свойства и сохраняет свойство Getter как Action<T>, а свойство Setter - как Func<T>.

Когда я на самом деле хочу вызвать свойство, я делаю что-то вроде этого:

int x = _propertyReference.Get();

или

_propertyReference.Set(2);

Вот мой PropertyReference<T> код. Пожалуйста, рассмотрите и внесите предложения по улучшению.

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();
        }

    }
}

Примечание: я не могу использовать функциональность "Emit", так как мне нужен этот код для работы на новом Windows Phone 7, который не поддерживает Emit.

UPDATE:

Только что сделал несколько тестов скорости после замены:

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

С

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

Как указано в dtb ниже.

Протестировано с помощью 100 000 вызовов свойства Get (). Вот результаты.

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

заняло около 200 мс

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

заняло около 10 мс

Огромная разница. Не ожидал этого, но круто!

Все еще открыт для дополнительных улучшений.

Ответы [ 2 ]

5 голосов
/ 30 марта 2010

Вы можете получить делегатов, напрямую представляющих метод получения и установки:

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());
0 голосов
/ 30 марта 2010

Это действительно зависит от того, как часто вы будете звонить. Если это не огромная пропускная способность, тогда хорошо - но обратите внимание, что основанные на отражении GetValue / SetValue довольно медленные. Вы можете кэшировать делегатов, но другой простой подход может выглядеть так: HyperDescriptor - при этом используется тот же API, что и PropertyDescriptor (поэтому вы снова получаете GetValue / SetValue), но при этом используется динамический методы под. Тогда API выглядит примерно так:

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

или

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

тогда

object value = prop.GetValue(component);
prop.SetValue(component, newValue);
...