2010-07-31 4 views
1

Cette question prendra probablement un certain temps pour expliquer, et je dois fournir ... fondObtenez l'instance d'objet qui contient une propriété spécifiée

Ceci est juste quelque chose que je joue avec et sur les ISN » t pour la production, mais au moment où j'ai un code qui ressemble à ceci:

var myDataModel = new DataModel(); 

myDataModel.PropertyChanged += myDataModel_PropertyChanged; 

myDataModel.ChangeProperty(t => t.TestValue, 2); 

Ainsi, plutôt que d'utiliser myDataModel.TestValue = 2 directement, j'utilise une méthode d'extension ChangeProperty pour que je puisse gérer tous les événements de changement et fais tout ce que je veux dans un endroit. Ma méthode d'extension ressemble à ceci (et oui, je sais qu'il est hacky):

public static class NotifyPropertyChangedExtensions 
{ 
    public static void ChangeProperty<T, U>(
             this T instance, 
             Expression<Func<T, U>> propertyToChange, 
             U newValue) 
    { 
     var member = propertyToChange.Body as MemberExpression; 

     if (member != null) 
     { 
      if (!propertyToChange.Compile().Invoke(instance).Equals(newValue)) 
      { 
       var setProperty = instance.GetType().GetProperty(
             member.Member.Name, 
             BindingFlags.SetProperty | 
             BindingFlags.Public | 
             BindingFlags.Instance); 

       if (setProperty != null) 
       { 
        // actually set the property 
        setProperty.SetValue(instance, newValue, null); 

        // raise the property changed event 
        if (typeof(INotifyPropertyChanged).IsAssignableFrom(
                   typeof(T))) 
        { 
         var delegatesToCall = 
          instance.GetType().GetField("PropertyChanged", 
             BindingFlags.Instance | 
             BindingFlags.NonPublic) 
            .GetValue(instance) as MulticastDelegate; 


         if (delegatesToCall != null) 
         { 
          var eventArgs = new PropertyChangedEventArgs(
                  setProperty.Name); 
          foreach (var @delegate in 
             delegatesToCall.GetInvocationList()) 
          { 
           @delegate.Method.Invoke(
             @delegate.Target, 
             new object[] { instance, eventArgs }); 
          } 
         } 

        } 
       } 
      } 
     } 
     else 
     { 
      throw new ArgumentException(
        string.Format(
          "Cannot determine the property to change {0}", 
          propertyToChange)); 
     } 
    } 
} 

Avec cette architecture, mon modèle de données est assez propre:

public class DataModel : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    public int TestValue { get; set; } 
} 

C'est, je peux utiliser les propriétés d'auto- et ne pas besoin de se soucier des événements de collecte etc.

maintenant, ce que je veux vraiment faire quelque chose plus proche de celle-ci:

var dataModel = new DataModel(); 

myDataModel.PropertyChanged += myDataModel_PropertyChanged; 

myDataModel.TestValue.Set(2); // this is what I want... 

Donc, je pense que je vais avoir besoin d'une méthode d'extension - mais je ne vois que comment envoyer la propriété elle-même (le TestValue dans ce cas), et la nouvelle valeur. Alors, je me suis demandé s'il était possible, avec une propriété, de trouver l'instance de la classe à laquelle il appartenait?

+0

Tbh Je ne ferais pas cela. Vous imputez la responsabilité de déclencher correctement l'événement PropertyChanged de la classe à son utilisateur. Cela casse le principe d'encapsulation. La classe doit s'assurer qu'elle se comporte comme prévu, pas le code qui l'utilise. – dtb

+0

@dtb: Ceci est une question "est-ce possible", je ne mettrais pas ce code de production proche ... –

+0

Au lieu de 'var setProperty = instance.GetType(). GetProperty (...)', qui repose sur la propriété * name *, vous devriez probablement obtenir la propriété de propertyToChange. Par exemple, dans votre code, '(PropertyInfo) member.Member' le ferait. – Timwi

Répondre

1
  1. Ne faites pas cela. Il casse l'encapsulation. En myDataModel.TestValue.Set(2);, la méthode d'extension sera toujours appelée sur la valeur renvoyée par la propriété. Il n'y a aucun moyen d'obtenir la classe, l'instance ou la propriété qui a renvoyé la valeur.

  2. Vous pourriez faire quelque chose comme ceci:

    var t = new DataModel(); 
    ((Expression<Func<int>>)(() => t.Foo)).Set(100); 
    

    avec

    static class Extensions 
    { 
        public static void Set<T>(this Expression<Func<T>> expression, T value) 
        { ... } 
    } 
    

    mais ce qui est laid, presque illisible, peu clair, inefficace et sujette aux erreurs.

  3. Vous cherchez des Programmation Orientée Aspect (AOP).

    Jetez un oeil à PostSharp ou LinFu.

  4. Il n'existe pas encore de solution vraiment propre pour implémenter INotifyPropertyChanged. Si taper tous les setters de la propriété est trop de travail ou trop sujet aux erreurs, je les générerais avec un template T4.

Questions connexes