2010-03-29 5 views
2

Je veux savoir s'il y a une meilleure façon (que ce que je fais actuellement) d'obtenir et de conserver une référence à une propriété dans un autre objet en utilisant seulement les noms de chaîne de l'objet et de la propriété. En particulier, existe-t-il une meilleure façon de le faire avec la nouvelle fonctionnalité dynamique de .Net 4.0?Quel est le meilleur moyen d'obtenir et de tenir la référence de la propriété par son nom dans C#

Voici ce que j'ai en ce moment.

J'ai un objet "PropertyReference<T>" qui prend un nom d'objet et un nom de propriété dans le constructeur.

Une méthode Initialize() utilise la réflexion pour trouver l'objet et la propriété et stocke la propriété Getter comme Action<T> et la propriété Setter comme Func<T>.

Quand je veux appeler réellement la propriété que je fais quelque chose comme ceci:

int x = _propertyReference.Get(); 

ou

_propertyReference.Set(2); 

Voici mon code PropertyReference<T>. S'il vous plaît disséquer et faire des suggestions pour l'amélioration.

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: Je ne peux pas utiliser la fonctionnalité « Emettre » comme je l'ai besoin de ce code pour travailler sur le nouveau Windows Phone 7 et qui ne prend pas en charge Emit.

MISE À JOUR:

juste fait quelques tests de vitesse après le remplacement:

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

Avec

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

Comme le suggère DTB ci-dessous.

Testé en effectuant 100 000 appels à la propriété Get(). Voici les résultats.

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

a pris environ 200ms

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

a pris environ 10ms

énorme différence. Je ne m'y attendais pas, mais cool!

Toujours ouvert à plus d'améliorations.

+1

Êtes-vous même certain que 'dynamic' sera disponible sur Win7 Smartphones? C'est un comportement assez sophistiqué que je ne suis pas soutenu. – LBushkin

+0

Il n'y a pas de remède magique pour Reflection, toutes les solutions proposées l'utilisent. Y compris * dynamique *. –

+0

Non, je ne suis pas sûr ... Je vais y jeter un coup d'oeil. –

Répondre

5

Vous pouvez obtenir directement les délégués représentant la méthode getter et setter:

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

Cela semble plus concis alors ce que j'ai. Je vais essayer d'incorporer cela. Toujours à la recherche d'autres idées si quelqu'un les a. –

0

Cela dépend vraiment de combien de fois vous allez être l'appeler. Si ce n'est pas le débit massif, alors bien - mais notez que les GetValue/SetValue basés sur la réflexion sont assez lents. Vous pouvez mettre en cache les délégués, mais une autre approche simple pourrait être de regarder HyperDescriptor - cela utilise la même API que PropertyDescriptor (donc vous obtenez à nouveau GetValue/SetValue), mais il utilise des méthodes dynamiques ci-dessous.L'API est alors quelque chose comme:

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

ou

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

puis

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

Fasterflect (http://fasterflect.codeplex.com) semble être une bibliothèque plus complète avec un but similaire à celui de HyperDescriptor, bien qu'il ait mentionné que Reflection.Emit n'était pas une option (ce qui exclurait ces deux suggestions)). –

+0

@Morten - J'ai manqué cette mise en garde dans la question - alors oui, cela les exclut. –

+0

Bummer, cela semblait être une bonne solution. J'ai regardé Fasterflect et envoyé par courriel les devs, ce qui est la façon dont j'ai découvert que Emit ne fonctionne pas sur Windows Phone 7 –