1
votes

Notifications de modification hiérarchiques dans une hiérarchie d'objets

J'ai une hiérarchie récursive de trois types d'objets dans une bibliothèque C #. Appelons-les Boîtes , Écrous et Boulons . Les boîtes peuvent contenir d'autres boîtes ou des écrous et des boulons . Les écrous et les boulons ne peuvent évidemment rien contenir.

Supposons que chaque Box a des ObservableCollections de Box , Nut et Bolt . Chaque écrou et boulon implémente INotifyPropertyChanged .

Existe-t-il une bonne pratique acceptée pour propager les notifications de modifications aux collections observables, ou les modifications de propriété sur un Nut ou un Bolt à un objet qui contient une référence au la boîte la plus élevée? Ou des modèles de conception particuliers que vous recommanderiez?

MODIFIER : pour donner un aperçu de ce problème, je dirige la Chimie pour Projet Word . Vous pouvez voir le composant qui affiche les structures en temps réel sur la gauche. Navigateur Chem4Word Maintenant, croyez-le ou non, cela dessine actuellement tout via la liaison de données. Chacun de ces affichages de molécules sur le LHS est un ItemsControl. (Et oui, j'utilise suis WPF avec MVVM!) Cela s'est avéré avoir trop de frais généraux et un manque de flexibilité pour une solution à long terme. Je suis donc revenu à la génération directe de DrawingVisuals . Cette approche permet un contrôle beaucoup plus fin. Les Boîtes , Écrous et Boulons dans mon exemple d'origine sont des Molécules , Atomes et Obligations . Si l'un de ces éléments est ajouté, supprimé ou modifié, l'affichage doit en être informé pour pouvoir être mis à jour. Comme j'ai déjà implémenté les interfaces et les objets pour la liaison de données, je souhaite exploiter le code que j'ai déjà.


6 commentaires

Pourquoi cette question? Quel est le problème réel? Si vous vous liez à Nut , vous n'avez pas besoin de faire remonter le changement jusqu'à Box ou à ObservableCollection . Vous ne devriez pas avoir besoin de recharger et de redessiner toutes les cases juste pour mettre à jour l'élément d'interface utilisateur qui se lie à un Nut . Modifiez-vous l'interface utilisateur directement au lieu d'utiliser peut-être la liaison de données? Ou utilisez-vous la liaison unidirectionnelle pour Nut ?


@dymanoid INotifyPropertyChanged et ObservableCollection sont principalement utilisés dans WFP, XAML, UWP. Winforms dépendait des événements à la place et ASP.NET ne se soucie pas des événements côté serveur. Même si la question ne concerne pas l’une des technologies liées au XAML, quel est le problème réel?


Quel est le problème? Comment trouver le parent? INotifyPropertyChanged est le choix parfait, le parent peut l'utiliser. Mais ObservableCollection ne surveillera pas ses enfants. C'est votre rôle ou (si vous parlez de wpf) c'est ce que la liaison fera. En fonction de la tâche, vous pouvez envisager une autre conception, lorsque chaque enfant obtient la référence de son parent et est capable d'appeler la méthode du parent lors du changement.


Le problème réel est que j'affiche des diagrammes de molécules contenant des atomes, des liaisons et d'autres molécules. J'ai essayé la liaison de données et je l'ai fait fonctionner pendant un certain temps en production. Les frais généraux et le manque de flexibilité signifient que j'ai dû retourner à la planche à dessin, dessinant les formes correspondantes directement sur une toile. J'ai donc déjà INotifyPropertyChanged sur chaque objet. Je souhaite informer l'objet de niveau supérieur de toutes les modifications apportées au modèle de données afin de pouvoir mettre à jour en conséquence


Voir ma modification de la publication d'origine.


Que chaque conteneur de choses soit également une usine de choses qu'il contient ... puis que les choses contenues aient des références faibles à leurs conteneurs. Ensuite, n'importe quel nœud feuille ou branche a un canal de communication à propagation ascendante ultra-rapide ... avec des appels de sécurité nulle: parent? .SomethingHappenedTo (originNode);


3 Réponses :


1
votes

Pourquoi n'appelez-vous pas le parent dans la notification de modification. Quelque chose comme le pseudo code suivant:

Bolt()
{
    NotifyPropertyChanged(property)
    {
         PropertyChanged(this, property);
    }

    ChildNutPropertyChanged(Nut child, property)
    {
         PropertyChanged(this, child + property);
    }
}


Nut(Bolt parentBolt)
{ 
    parent = parentBolt;

    NotifyPropertyChanged(property)
    {
         PropertyChanged(this, property);
         parent.NotifyPropertyChanged(this, property);
    }
}


0 commentaires

1
votes

Si vous encapsulez votre ObservableCollection de Nuts and Bolts et que vous ne rendez publique qu'une ReadOnlyObservableCollection , vous pouvez rendre une méthode Add (Nut nut) (et un autre pour les boulons) qui s'inscrit dans l'événement NotifyPropertyChanged de Nut ajouté.

De cette façon, vous saurez dans la Box quand une propriété d'un enfant a changé et vous prendrez des mesures.


1 commentaires

ReadOnlyObservableCollection est un nouveau sur moi, merci. Je vais enquêter plus loin.



2
votes

J'ai eu un modèle similaire avec un accès rapide aux instances Node en amont dans des graphes acycliques dirigés. Un Node a une référence faible à son parent immédiat. Node a une propriété pour obtenir la racine ... qui tente de renvoyer la racine de son parent. S'il n'y a pas de parent, alors ce nœud est une racine. L'enracinement est basé uniquement sur le confinement. Notez que le parent n'est pas une collection ... car parfois le nœud enfant n'est même pas dans une collection. Quelque chose de plus ou moins comme ...

public abstract class Node
{
  WeakReference<Node> parent;

  public Node Root
  {
    get { return Parent?.Root ?? this; }
  }

  public Node Parent
  {
    get
    {
      if ( parent != null )
      {
        if ( parent.TryGetTarget( out Node parentNode ) )
        {
          return parentNode;
        }
      }
      return this;
    }
    internal set { /*...*/ } //--> if you're brave...
  }
}

Modifier :

Concernant les références faibles ... une des choses que nos graphiques peuvent avoir est des références aux nœuds dans d'autres graphiques. Nous avons un service de résolution de nœuds qui récupérera ces autres nœuds. Ces références extérieures sont représentées par une valeur d'identité (un GUID ou un Long ) et une référence faible associée. De cette façon, nous pouvons charger le nœud spécifié selon les besoins, mais pas le garder plus longtemps que nécessaire. Le résolveur maintient un cache LRU de nœuds résolus de cette manière.

Si une telle référence résolue doit résoudre son propre parent, un mécanisme similaire existe pour permettre au nœud dépendant de résoudre son parent. Même les nœuds enfants collectés d'un nœud peuvent être chargés paresseusement via le service de résolution (bien qu'il existe des annotations qui indiquent à notre framework quand charger paresseusement et quand non).

Ainsi, les références faibles aident avec tout cela accessoirement - scénarios résolus. Euh ... plus précisément, ils nous aident à ne pas bousiller le ramasse-miettes dans de tels scénarios.

Dans certains scénarios analytiques, nous aurons des centaines de milliers de nœuds entrant et sortant. Je pourrais imaginer des dynamiques similaires dans la modélisation chimique.


7 commentaires

De nombreuses personnes ont mentionné des références faibles. Quels sont les avantages de les utiliser par rapport aux plus puissants?


Les références faibles sont utiles dans les scénarios où A fait référence à B et B fait référence à C et C référence A. Cela peut gommer le ramasse-miettes. Si vous avez des références fortes vers le bas jusqu'à trois, mais des références faibles vers le haut, vous n'avez rien à faire de bizarre pour faciliter le garbage collection. Lorsque vous détachez une branche de nœuds de l'arborescence, elle sera correctement collectée.


Cela dit, si un arbre entier est hors de portée, je pense que le GC peut toujours résoudre toutes ces références et tout rassembler. C'est peut-être plus important dans d'autres GC déterministes comme Swift ... donc cela pourrait être exagéré ici.


Il vaut toujours la peine d'enquêter. Ce ne sont pas, en principe, de grands arbres d'objets. Mais ils pourraient être en désordre. Merci pour vos précieux commentaires.


En y réfléchissant, je peux voir certains scénarios où nous avons besoin de références à des nœuds dans d'autres graphiques. Les WeakReferences sont donc encore plus applicables.


L'autre chose intéressante à propos de tout ce scénario est la façon dont il se sérialise. Vous pouvez sérialiser un graphe et le reconstituer facilement, car les nœuds sérialisés ont leurs identifiants et identifiants parents ... et vous disposez d'un service de résolution pour récupérer les nœuds par identifiant. Vous pouvez donc être "paresseux" sur les références hors graphique. Comment un graphe sérialise et désérialise est un gros problème et vaut la peine d'y mettre quelques cycles.


En effet, c'est un excellent point. Le modèle actuel que nous utilisons est un cauchemar à sérialiser facilement.