2010-05-17 2 views
1

J'utilise .Net 3.5 pour l'instant.Existe-t-il un meilleur moyen d'éviter une boucle infinie en utilisant des winforms?

À l'heure actuelle, j'utilise une astuce using pour désactiver et activer les événements autour de certaines sections du code. L'utilisateur peut changer les jours, les heures, les minutes ou le nombre total de minutes, ce qui ne devrait pas provoquer une cascade infinie d'événements (par exemple, changement total des minutes, changement total des minutes, etc.)./façon plus directe. En connaissez-vous?

Pour les points musculeux:

Ce contrôle sera utilisé par plusieurs équipes - Je ne veux pas faire embarrassant. Je soupçonne que je n'ai pas besoin de réinventer la roue en définissant des heures dans un jour, des jours dans la semaine, etc. Une autre bibliothèque standard .Net là doit l'avoir. D'autres remarques concernant le code? Cette using (EventHacker.DisableEvents(this)) entreprise - qui doit être un modèle commun dans .Net ... changer le réglage temporairement. Quel est le nom? J'aimerais pouvoir y faire référence dans un commentaire et en lire plus sur les implémentations actuelles. Dans le cas général, non seulement il faut se souvenir d'une poignée de l'objet en cours de modification, mais aussi de l'état précédent (dans ce cas, l'état précédent n'a pas d'importance - les événements sont activés et désactivés de manière inconditionnelle). Ensuite, il y a aussi une possibilité de multi-threaded hacking. On pourrait également utiliser des génériques pour rendre le code sans doute plus propre. Comprendre tout cela peut conduire à un article de blog de plusieurs pages. Je serais heureux d'entendre certaines des réponses.

P.S. Est-ce que j'ai l'impression de souffrir d'un trouble obsessionnel compulsif? Certaines personnes aiment faire avancer les choses et passer à autre chose; J'aime les garder ouvertes ... il y a toujours un meilleur moyen.

// Corresponding Designer class is omitted. 
using System; 
using System.Windows.Forms; 

namespace XYZ // Real name masked 
{ 
    interface IEventHackable 
    { 
     void EnableEvents(); 
     void DisableEvents(); 
    } 

    public partial class PollingIntervalGroupBox : GroupBox, IEventHackable 
    { 
     private const int DAYS_IN_WEEK  = 7; 
     private const int MINUTES_IN_HOUR = 60; 
     private const int HOURS_IN_DAY  = 24; 
     private const int MINUTES_IN_DAY = MINUTES_IN_HOUR * HOURS_IN_DAY; 
     private const int MAX_TOTAL_DAYS = 100; 

     private static readonly decimal MIN_TOTAL_NUM_MINUTES = 1; // Anything faster than once per minute can bog down our servers. 
     private static readonly decimal MAX_TOTAL_NUM_MINUTES = (MAX_TOTAL_DAYS * MINUTES_IN_DAY) - 1; // 99 days should be plenty. 
     // The value above was chosen so to not cause an overflow exception. 
     // Watch out for it - numericUpDownControls each have a MaximumValue setting. 

     public PollingIntervalGroupBox() 
     { 
      InitializeComponent(); 

      InitializeComponentCustom(); 
     } 

     private void InitializeComponentCustom() 
     { 
      this.m_upDownDays.Maximum   = MAX_TOTAL_DAYS - 1; 
      this.m_upDownHours.Maximum   = HOURS_IN_DAY  - 1; 
      this.m_upDownMinutes.Maximum  = MINUTES_IN_HOUR - 1; 
      this.m_upDownTotalMinutes.Maximum = MAX_TOTAL_NUM_MINUTES; 
      this.m_upDownTotalMinutes.Minimum = MIN_TOTAL_NUM_MINUTES; 
     } 

     private void m_upDownTotalMinutes_ValueChanged(object sender, EventArgs e) 
     { 
      setTotalMinutes(this.m_upDownTotalMinutes.Value); 
     } 

     private void m_upDownDays_ValueChanged(object sender, EventArgs e) 
     { 
      updateTotalMinutes(); 
     } 

     private void m_upDownHours_ValueChanged(object sender, EventArgs e) 
     { 
      updateTotalMinutes(); 
     } 

     private void m_upDownMinutes_ValueChanged(object sender, EventArgs e) 
     { 
      updateTotalMinutes(); 
     } 

     private void updateTotalMinutes() 
     { 
      this.setTotalMinutes(
       MINUTES_IN_DAY * m_upDownDays.Value + 
       MINUTES_IN_HOUR * m_upDownHours.Value + 
       m_upDownMinutes.Value); 
     } 

     public decimal TotalMinutes 
     { 
      get 
      { 
       return m_upDownTotalMinutes.Value; 
      } 
      set 
      { 
       m_upDownTotalMinutes.Value = value; 
      } 
     } 

     public decimal TotalHours 
     { 
      set 
      { 
       setTotalMinutes(value * MINUTES_IN_HOUR); 
      } 
     } 

     public decimal TotalDays 
     { 
      set 
      { 
       setTotalMinutes(value * MINUTES_IN_DAY); 
      } 
     } 

     public decimal TotalWeeks 
     { 
      set 
      { 
       setTotalMinutes(value * DAYS_IN_WEEK * MINUTES_IN_DAY); 
      } 
     } 

     private void setTotalMinutes(decimal nTotalMinutes) 
     { 
      if (nTotalMinutes < MIN_TOTAL_NUM_MINUTES) 
      { 
       setTotalMinutes(MIN_TOTAL_NUM_MINUTES); 
       return; // Must be carefull with recursion. 
      } 
      if (nTotalMinutes > MAX_TOTAL_NUM_MINUTES) 
      { 
       setTotalMinutes(MAX_TOTAL_NUM_MINUTES); 
       return; // Must be carefull with recursion. 
      } 
      using (EventHacker.DisableEvents(this)) 
      { 
       // First set the total minutes 
       this.m_upDownTotalMinutes.Value = nTotalMinutes; 

       // Then set the rest 
       this.m_upDownDays.Value = (int)(nTotalMinutes/MINUTES_IN_DAY); 
       nTotalMinutes = nTotalMinutes % MINUTES_IN_DAY; // variable reuse. 
       this.m_upDownHours.Value = (int)(nTotalMinutes/MINUTES_IN_HOUR); 
       nTotalMinutes = nTotalMinutes % MINUTES_IN_HOUR; 
       this.m_upDownMinutes.Value = nTotalMinutes; 
      } 
     } 

     // Event magic 
     public void EnableEvents() 
     { 
      this.m_upDownTotalMinutes.ValueChanged += this.m_upDownTotalMinutes_ValueChanged; 
      this.m_upDownDays.ValueChanged += this.m_upDownDays_ValueChanged; 
      this.m_upDownHours.ValueChanged += this.m_upDownHours_ValueChanged; 
      this.m_upDownMinutes.ValueChanged += this.m_upDownMinutes_ValueChanged; 
     } 

     public void DisableEvents() 
     { 
      this.m_upDownTotalMinutes.ValueChanged -= this.m_upDownTotalMinutes_ValueChanged; 
      this.m_upDownDays.ValueChanged -= this.m_upDownDays_ValueChanged; 
      this.m_upDownHours.ValueChanged -= this.m_upDownHours_ValueChanged; 
      this.m_upDownMinutes.ValueChanged -= this.m_upDownMinutes_ValueChanged; 
     } 

     // We give as little info as possible to the 'hacker'. 
     private sealed class EventHacker : IDisposable 
     { 
      IEventHackable _hackableHandle; 

      public static IDisposable DisableEvents(IEventHackable hackableHandle) 
      { 
       return new EventHacker(hackableHandle); 
      } 

      public EventHacker(IEventHackable hackableHandle) 
      { 
       this._hackableHandle = hackableHandle; 
       this._hackableHandle.DisableEvents(); 
      } 

      public void Dispose() 
      { 
       this._hackableHandle.EnableEvents(); 
      } 
     } 
    } 
} 
+3

Personnellement, je n'aime pas cette utilisation de ** using **. C'est mignon et intelligent comment vous re-proposant une construction de langage, mais ce ne sont pas des traits de bon code. Vous obtenez un code légèrement plus propre au détriment de la compréhensibilité générale (considérez les programmeurs novices qui regardent votre code). J'utiliserais simplement try..finally blocks, rendant le code plus auto-documenté. –

+0

Hm, vous pouvez avoir un point. J'ai vu ce (ab) usage employé par d'autres mais dans un différent de. D'autres l'aiment parce qu'elle économise la frappe. Personnellement, je pense qu'il faut un programmeur C++ novice plus long pour s'appuyer sur des auto-pointeurs qu'un programmeur novice C# pour apprendre ce 'mignon' comme vous le dites. Je ne suis pas sûr que le code soit optimisé pour la maintenance par des développeurs juniors, seniors ou chevronnés. Encore une fois, mon opinion biaisée contre la vôtre - je suis curieux de savoir ce que les autres ont à dire. –

Répondre

2

Je voudrais utiliser un champ booléen pour arrêter la méthode setTotalMinutes d'être exécuté plusieurs fois et déplacer la création des eventhandlers à la méthode InitializeComponentCustom.

Quelque chose comme ceci:

public partial class PollingIntervalGroupBox : GroupBox 
{ 
    private const int DAYS_IN_WEEK = 7; 
    private const int MINUTES_IN_HOUR = 60; 
    private const int HOURS_IN_DAY = 24; 
    private const int MINUTES_IN_DAY = MINUTES_IN_HOUR * HOURS_IN_DAY; 
    private const int MAX_TOTAL_DAYS = 100; 

    private static readonly decimal MIN_TOTAL_NUM_MINUTES = 1; // Anything faster than once per minute can bog down our servers. 
    private static readonly decimal MAX_TOTAL_NUM_MINUTES = (MAX_TOTAL_DAYS * MINUTES_IN_DAY) - 1; // 99 days should be plenty. 
    // The value above was chosen so to not cause an overflow exception. 
    // Watch out for it - numericUpDownControls each have a MaximumValue setting. 
    private bool _totalMinutesChanging; 

    public PollingIntervalGroupBox() 
    { 
     InitializeComponent(); 
     InitializeComponentCustom(); 
    } 

    private void InitializeComponentCustom() 
    { 
     this.m_upDownDays.Maximum = MAX_TOTAL_DAYS - 1; 
     this.m_upDownHours.Maximum = HOURS_IN_DAY - 1; 
     this.m_upDownMinutes.Maximum = MINUTES_IN_HOUR - 1; 
     this.m_upDownTotalMinutes.Maximum = MAX_TOTAL_NUM_MINUTES; 
     this.m_upDownTotalMinutes.Minimum = MIN_TOTAL_NUM_MINUTES; 

     this.m_upDownTotalMinutes.ValueChanged += this.m_upDownTotalMinutes_ValueChanged; 
     this.m_upDownDays.ValueChanged += this.m_upDownDays_ValueChanged; 
     this.m_upDownHours.ValueChanged += this.m_upDownHours_ValueChanged; 
     this.m_upDownMinutes.ValueChanged += this.m_upDownMinutes_ValueChanged; 
    } 

    private void m_upDownTotalMinutes_ValueChanged(object sender, EventArgs e) 
    { 
     setTotalMinutes(this.m_upDownTotalMinutes.Value); 
    } 

    private void m_upDownDays_ValueChanged(object sender, EventArgs e) 
    { 
     updateTotalMinutes(); 
    } 

    private void m_upDownHours_ValueChanged(object sender, EventArgs e) 
    { 
     updateTotalMinutes(); 
    } 

    private void m_upDownMinutes_ValueChanged(object sender, EventArgs e) 
    { 
     updateTotalMinutes(); 
    } 

    private void updateTotalMinutes() 
    { 
     this.setTotalMinutes(
      MINUTES_IN_DAY * m_upDownDays.Value + 
      MINUTES_IN_HOUR * m_upDownHours.Value + 
      m_upDownMinutes.Value); 
    } 

    public decimal TotalMinutes { get { return m_upDownTotalMinutes.Value; } set { m_upDownTotalMinutes.Value = value; } } 

    public decimal TotalHours { set { setTotalMinutes(value * MINUTES_IN_HOUR); } } 

    public decimal TotalDays { set { setTotalMinutes(value * MINUTES_IN_DAY); } } 

    public decimal TotalWeeks { set { setTotalMinutes(value * DAYS_IN_WEEK * MINUTES_IN_DAY); } } 

    private void setTotalMinutes(decimal totalMinutes) 
    { 
     if (_totalMinutesChanging) return; 
     try 
     { 
      _totalMinutesChanging = true; 
      decimal nTotalMinutes = totalMinutes; 
      if (totalMinutes < MIN_TOTAL_NUM_MINUTES) 
      { 
       nTotalMinutes = MIN_TOTAL_NUM_MINUTES; 
      } 
      if (totalMinutes > MAX_TOTAL_NUM_MINUTES) 
      { 
       nTotalMinutes = MAX_TOTAL_NUM_MINUTES; 
      } 
      // First set the total minutes 
      this.m_upDownTotalMinutes.Value = nTotalMinutes; 

      // Then set the rest 
      this.m_upDownDays.Value = (int)(nTotalMinutes/MINUTES_IN_DAY); 
      nTotalMinutes = nTotalMinutes % MINUTES_IN_DAY; // variable reuse. 
      this.m_upDownHours.Value = (int)(nTotalMinutes/MINUTES_IN_HOUR); 
      nTotalMinutes = nTotalMinutes % MINUTES_IN_HOUR; 
      this.m_upDownMinutes.Value = nTotalMinutes; 
     } 
     finally 
     { 
      _totalMinutesChanging = false; 
     } 
    } 
} 
+0

Merci, cela a fonctionné! J'ai d'abord essayé d'utiliser un bool mais je me suis trompé. Des pointeurs pour des améliorations? Où puis-je saisir les constantes telles que 7, 24, 60? Est-ce que le nom 'InitializeComponentCustom semble correct'? Rien d'autre? –

+0

@Hamish, je pense que c'est bien de définir ces constantes par soi-même.En fonction de ce que vous utilisez pour le contrôle, vous pouvez ajouter un événement à notifier lorsque l'intervalle d'interrogation change. J'aurais probablement hérité de 'UserControl' pour pouvoir positionner les contrôles updown plus facilement. Sinon, je pense que tout va bien. –

2

Cela ne semble pas bon. Le code client n'aurait aucune idée de la raison pour laquelle il désactiverait jamais les événements sur un contrôle privé qu'il ne peut pas voir. Il n'y a pas non plus moyen de voir qu'une propriété publique a changé, il n'y a pas d'événements pour dire qu'une propriété publique a changé de valeur. Si elle voulait ignorer les événements de changement, elle se désabonnerait simplement ou ignorerait ces événements (manquants). Considérez les contrôles d'infrastructure .NET comme un guide.

Aucun d'entre eux n'a quelque chose de similaire à un IEventHackable.

+0

Eh bien, je suis content d'avoir posé cette question. –

Questions connexes