2009-04-17 7 views
6

Donc ce que j'essaie de faire est d'appeler une seule propriété propertyWasSet() quand une propriété dans une classe C# est définie (à l'inverse, propertyWasGot() quand il est get). J'aimerais aussi savoir quelle propriété 'get' a été invoquée. Je voudrais maintenir un dicton des propriétés qui sont 'set', et vérifier l'action 'get' si elles ont été encore définies (et lancer une erreur si elle n'a pas été).Existe-t-il un moyen d'appeler une méthode lorsqu'une propriété d'une classe est définie?

Je consulte la documentation msdn pour la réflexion, les délégués, etc ..., mais je ne suis pas entièrement sûr que cela soit possible.

Existe-t-il un moyen de le faire? ou déclencher un événement en appelant une de ces fonctions qui peuvent être interceptées dans une classe de base ou quelque chose?

Répondre

0

Je pense que ce dont vous avez besoin est très similaire au système de propriété de dépendance WPF. Vous pourriez vouloir regarder son implémentation. Quoi qu'il en soit, vous pouvez ajouter le code d'interception à getter et à setter de chaque propriété.

+0

Oui, j'essaie d'éviter d'ajouter une vérification à chaque propriété car il y en a plusieurs, et le code est presque le même pour chacune. –

3

Vous voudrez peut-être regarder dans PostSharp pour ce genre de tâche. Il est conçu pour fonctionner sur C# (ou n'importe quel langage .NET d'ailleurs) et a l'avantage de ne pas encombrer votre code de réflexion en plus. En fait, je ne crois pas que vous puissiez trouver une solution qui utilise simplement C#/Reflection sans ajouter de code manuellement à chacune de vos propriétés, donc je recommanderais définitivement PostSharp comme méthode de travail.

0

Il n'y a rien de tel si vous ne le créez pas vous-même.

+0

J'ai envisagé de créer une nouvelle classe au moment de l'exécution, et d'encapsuler toutes les propriétés de classe dans une méthode qui fait des choses, mais je ne sais pas si c'est possible et ça se complique rapidement. –

7

J'ai écrit un intercepteur l'autre semaine pour Set qui peut facilement être étendu pour Get, il utilise RealProxy, ce qui signifie que votre classe de base doit dériver de MarshalByRefObject.

Une autre option de fantaisie consiste à avoir un résumé de votre classe et à utiliser Reflection Emit pour construire une classe concrète qui enveloppe toutes les propriétés.

Aussi, vous pouvez regarder des générateurs de code pour contourner ce ou PostSharp ...

Performance pour cette solution n'est pas brillante, mais il devrait être beaucoup plus rapide pour lier l'interface utilisateur. Il pourrait être amélioré en générant des méthodes LCG pour l'invocation de proxy.

public interface IInterceptorNotifiable { 
    void OnPropertyChanged(string propertyName); 
} 

/// <summary> 
/// A simple RealProxy based property interceptor 
/// Will call OnPropertyChanged whenever and property on the child object is changed 
/// </summary> 
public class Interceptor<T> where T : MarshalByRefObject, IInterceptorNotifiable, new() { 

    class InterceptorProxy : RealProxy { 
     T proxy; 
     T target; 
     EventHandler<PropertyChangedEventArgs> OnPropertyChanged; 

     public InterceptorProxy(T target) 
      : base(typeof(T)) { 
      this.target = target; 
     } 

     public override object GetTransparentProxy() { 
      proxy = (T)base.GetTransparentProxy(); 
      return proxy; 
     } 


     public override IMessage Invoke(IMessage msg) { 

      IMethodCallMessage call = msg as IMethodCallMessage; 
      if (call != null) { 

       var result = InvokeMethod(call); 
       if (call.MethodName.StartsWith("set_")) { 
        string propName = call.MethodName.Substring(4); 
        target.OnPropertyChanged(propName); 
       } 
       return result; 
      } else { 
       throw new NotSupportedException(); 
      } 
     } 

     IMethodReturnMessage InvokeMethod(IMethodCallMessage callMsg) { 
      return RemotingServices.ExecuteMessage(target, callMsg); 
     } 

    } 

    public static T Create() { 
     var interceptor = new InterceptorProxy(new T()); 
     return (T)interceptor.GetTransparentProxy(); 
    } 


    private Interceptor() { 
    } 

} 

Utilisation:

class Foo : MarshalByRefObject, IInterceptorNotifiable { 
     public int PublicProp { get; set; } 
     public string lastPropertyChanged; 

     public void OnPropertyChanged(string propertyName) { 
      lastPropertyChanged = propertyName; 
     } 

    } 


    [Test] 
    public void TestPropertyInterception() { 

     var foo = Interceptor<Foo>.Create(); 
     foo.PublicProp = 100; 

     Assert.AreEqual("PublicProp", foo.lastPropertyChanged); 

    } 
} 
+0

c'est vraiment intéressant. Je suppose que je pourrais faire le truc Create() dans le constructeur de l'objet, donc je n'aurais pas à modifier explicitement l'instanciation de tous mes objets. Je vais jouer avec ça. Merci beaucoup! –

+0

Je pense que vous allez vraiment avoir besoin d'une fabrique pour vos objets, ce qui peut être une meilleure pratique de toute façon (puisqu'elle vous permet d'échanger des implémentations d'objets à une date ultérieure et des instances d'objet cache) –

0

La partie SET de votre demande est très similaire au système de propriété de dépendance WPF. Mais la partie GET est tellement inhabituelle qu'il manque même dans le système de propriété de dépendance WPF!