begin process at 2008 05 16 23:36:31
1 173 851 membres
655 nouveaux aujourd'hui
13 973 membres club

Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum.
Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

EXTENSION DES LISTES GÉNÉRIQUES (DESIGN PATTERN "DECORATEUR")


Information sur la source

Catégorie :.NET Source .NET ( DotNet ) Classé sous : design pattern, thread safe, génériques, méthode anonyme, Serialization Niveau : Expert Date de création : 01/10/2007 Date de mise à jour : 02/10/2007 15:03:12 Vu / téléchargé: 3 048 / 94

Note :
9 / 10 - par 2 personnes
9,00 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

Commentaire sur cette source (13)
Ajouter un commentaire et/ou une note


Description

La source que je vous présente aujourd'hui permet de mettre en avant plusieurs principes de développement assez intéressants :
- La création d'une classe avec les génériques,
- L'implémentation de interface IList,
- L'extension des actions de la classe générique List<T>,
- Les méthodes anonymes,
- La sauvegarde des évènements lors d'une sérialisation,
- Le principe "Thread-safe".


Le but principal de la classe EvtList (qui fait l'objet principal de cette source), est de fournir des évènements lors de l'ajout/suppression d'un élément dans celle-ci. On travaille ici sur une liste de dates, mais peu importe, vous pouvez utiliser cette liste avec n'importe quel type d'objets.



La création d'une classe avec les génériques :
La classe EvtList doit être instanciée en fournissant le type de la donnée qui est stockée dans la liste. On ne va pas revenir sur les avantages des génériques ici, mais je suppose que vous en connaissez les bénéfices (sécurité de type, ect...). Au passage, si vous voulez contraindre la liste à ne stocker qu'un certain type d'objet (et ses sous-types), vous pouvez utiliser la clause where, que j'ai ici laissée en commentaire.



L'implémentation de interface IList :
Vous pouvez voir ici une implémentation de la classe IList.



L'extension des actions de la classe générique List<T> :
On a dans l'EvtList une instance (privée) d'une classe implémentant l'interface IList<T>. Comme on fait de nouvelles actions lors de l'ajout/suppression d'un élément, on peut dire que l'on attache de nouvelles responsabilités à l'objet. Fin du fin, on peut déclarer la EvtList de deux manières : Sans paramètres dans le constructeur (C'est comme si on déclarait une List, mais avec des évènements en plus), ou avec un objet implémentant l'interface IList<T> en paramètre d'entrée du constructeur. Dans ce cas de figure, cela signifie que l'on attache de nouvelles responsabilités à un objet déjà existant ! Par exemple, la méthode Add, en plus d'ajouter un élément à la liste, appelle maintenant divers évènements. Si ce principe vous dit quelque chose, c'est car il s'agit ici du design pattern décorateur.


Les méthodes anonymes :
Vous pourrez voir dans le code du formulaire principal quelques méthodes anonymes. Autrement dit, on ne prend plus la peine de déclarer des méthodes pour répondre aux évènements, mais on fait déclare des méthodes dans des méthodes. A voir si vous trouvez ça plus lisible ou pas, mais rappelons nous que nous sommes dans un tutorial ;-)

La sauvegarde des évènements lors d'une sérialisation :
Notre liste lance des évènements mais il faut savoir une chose : ils sont sauvegardés lors de la sérialisation. Or, même si notre objet est sérialisable, la sauvegarde va échouer si l'objet posant l'écouteur sur la liste n'est pas sérialisable. Dans ce but, les évènements onAdd et onRemove sont des champs (fields) marqués non sérialisables.
Le souci, c'est que l'on peut vouloir inclure cette définition de la classe EvtList dans notre DLL pour gérer nos objets métiers. Or, tous les objets de notre DLL sont sérialisables, et nous voulons pouvoir sauvegarder les références aux différents évènements et ne pas les perdre lors des opérations de sérialisation/désérialisation. C'est pour cela que nous avons dans la EvtList les évènements onAddInternal et onRemoveInternal, qui sont sérialisables, comme le reste de la classe, mais qui ne sont accessibles qu'à notre namespace (internal).

Le principe "Thread-safe" :
Les Thread.BeginCriticalRegion(); permettent ici d'avoir des listes qui sont thread-safe, en quelque sorte.
Je ne sais pas si vous avez vu, mais on lève une exception si un même élément est ajouté 2 fois à la liste. Imaginons 2 threads qui utilisent tous les 2 la méthode Add en même temps, et avec le même objet à ajouter. S'ils arrivent tous les 2 en même temps à la ligne "if (InternalList.Contains(item))", ils vont passer le test avec succès, et ajouter 2 fois (1 fois par thread) le même élément à la liste. Le lock permet d'éviter ce souci en indiquant à la CLR que tout ce qui ce trouve entre les accolades de ce mot clé ne doit être compris que comme une seule et même instruction "unitaire". Il s'agit d'un vérou par exclusion mutuelle. (merci à Yxion : j'avais oublié de mettre ça dans la description de ma classe).

Source

  • /// <summary>
  • /// Classe permettant de stocker une liste d'objets dont le type est de type T.
  • /// Cette liste peut lever des évènements lors d'un ajout ou lors d'une suppression.
  • /// Cette liste peut aussi lever des exception lorsque :
  • /// - On tente d'ajouter un objet null à la liste,
  • /// - On tente de retirer un objet null de la liste,
  • /// - On tente d'ajouter un même objet plusieurs fois à la liste,
  • /// - On tente de supprimer un objet de la liste alors que celui-ci n'existe pas.
  • /// </summary>
  • /// <typeparam name="T">Type de l'objet stocké dans la liste.</typeparam>
  • [Serializable]
  • public class EvtList <T> : IList<T>, IEnumerable, IEnumerable<T> // where T : VotreTypeObjetFari
  • {
  • #region Privates
  • /// <summary>
  • /// Permet de stocker les éléments de la liste.
  • /// </summary>
  • private IList<T> InternalList = null;
  • #endregion
  • #region Events
  • /// <summary>
  • /// Survient lors de l'ajout d'un élément à la liste.
  • /// </summary>
  • public event EventHandler onAddInternal;
  • /// <summary>
  • /// Survient lors de l'ajout d'un élément à la liste.
  • /// </summary>
  • [field: NonSerialized]
  • public event EventHandler onAdd;
  • /// <summary>
  • /// Survient lors de la suppression d'un élément de la liste.
  • /// </summary>
  • internal event EventHandler onRemoveInternal;
  • /// <summary>
  • /// Survient lors de la suppression d'un élément de la liste.
  • /// </summary>
  • [field: NonSerialized]
  • public event EventHandler onRemove;
  • /// <summary>
  • /// Permet de lancer tous les évènements associés à l'ajout d'un élément à la liste.
  • /// </summary>
  • /// <param name="itemAdded">L'objet ajouté.</param>
  • protected void LaunchAddEvents(T itemAdded) {
  • try {
  • // Construction de l'objet AddEventArgs permettant de communiquer quel objet
  • // a été ajouté à la liste.
  • EvtListAddEventArgs args = new EvtListAddEventArgs(itemAdded);
  • // Lancement des évènements onAddInternal puis onAdd.
  • if (onAddInternal != null) onAddInternal(this, args);
  • if (onAdd != null) onAdd(this, args);
  • } catch (Exception ex) {
  • throw ex;
  • }
  • }
  • /// <summary>
  • /// Permet de lancer tous les évènements associés à la suppression d'un élément à la liste.
  • /// </summary>
  • /// <param name="itemRemoved">L'objet supprimé.</param>
  • protected void LaunchRemoveEvents(T itemRemoved) {
  • try {
  • // Construction de l'objet AddEventArgs permettant de communiquer quel objet
  • // a été supprimé à la liste.
  • EvtListRemoveEventArgs args = new EvtListRemoveEventArgs(itemRemoved);
  • // Lancement des évènements onAddInternal puis onAdd.
  • if (onRemoveInternal != null) onRemoveInternal(this, args);
  • if (onRemove != null) onRemove(this, args);
  • } catch (Exception ex) {
  • throw ex;
  • }
  • }
  • #endregion
  • #region Constructors
  • public EvtList(){
  • InternalList = new List<T>();
  • }
  • public EvtList(IList<T> list){
  • InternalList = list;
  • }
  • #endregion
  • #region IList<T> Members
  • /// <summary>
  • /// Permet de récupérer l'index d'un élément de la liste.
  • /// </summary>
  • /// <param name="item">L'élément recherché.</param>
  • /// <returns>L'index de l'élément recherché.</returns>
  • public int IndexOf(T item) {
  • return InternalList.IndexOf(item);
  • }
  • /// <summary>
  • /// Permet d'insérer un élément dans la liste, à un index précis.
  • /// </summary>
  • /// <param name="index">L'index ou l'objet doit être ajouté.</param>
  • /// <param name="item">L'élément à ajouter.</param>
  • public void Insert(int index, T item) {
  • try{
  • lock(InternalList){
  • // Si l'élément qui est ajouté est null ou déjà dans la liste, on lève une exception.
  • if (item == null) throw new ObjectAddedToEvtListNullError();
  • if (InternalList.Contains(item)) throw new ObjectAllreadyInEvtListError();
  • InternalList.Insert(index, item); // Insertion de l'élément dans la liste.
  • LaunchAddEvents(item); // Lancement des évènements qui doivent survenir lors de l'ajout d'un élément de la liste.
  • }
  • } catch (Exception ex) {
  • throw ex;
  • }
  • }
  • /// <summary>
  • /// Permet de savoir si la liste est en lecture seule ou non.
  • /// </summary>
  • public bool IsReadOnly
  • {
  • get { return false; }
  • }
  • /// <summary>
  • /// Permet de supprimer un élément de la liste à l'emplacement spécifié.
  • /// </summary>
  • /// <param name="index">L'index de l'élément à supprimer.</param>
  • public void RemoveAt(int index) {
  • try{
  • T RemovedObject = InternalList[index]; // Stockage de l'élément supprimé pour l'envoyer aux gestionnaires d'évènements.
  • InternalList.RemoveAt(index); // Suppression de l'élément dans la liste.
  • LaunchRemoveEvents(RemovedObject); // Lancement des évènements qui doivent survenir lors de la suppression d'un élément de la liste.
  • } catch (Exception ex) {
  • throw ex;
  • }
  • }
  • /// <summary>
  • /// Accesseur indexé. Permet d'accéder ou de modifier l'un des éléments de la liste à partir
  • /// d'un index.
  • /// </summary>
  • /// <param name="index">L'index recherché.</param>
  • /// <returns>L'élémént recherché.</returns>
  • public T this[int index] {
  • get { return InternalList[index]; }
  • set {
  • try{
  • //if (InternalList[index] != value) {
  • LaunchRemoveEvents(InternalList[index]);
  • InternalList[index] = value;
  • LaunchAddEvents(InternalList[index]);
  • //}
  • } catch (Exception ex) {
  • throw ex;
  • }
  • }
  • }
  • #endregion
  • #region ICollection<T> Members
  • /// <summary>
  • /// Permet d'ajouter un élément à la liste.
  • /// </summary>
  • /// <param name="item"></param>
  • public void Add(T item) {
  • try {
  • //Thread.BeginCriticalRegion(); // Début d'une région de thread critique.
  • lock(InternalList){
  • // Si l'élément qui est ajouté est null ou déjà dans la liste, on lève une exception.
  • if (item == null) throw new ObjectAddedToEvtListNullError();
  • if (InternalList.Contains(item)) throw new ObjectAllreadyInEvtListError();
  • InternalList.Add(item); // Insertion de l'élément dans la liste.
  • LaunchAddEvents(item); // Lancement des évènements qui doivent survenir lors de l'ajout d'un élément de la liste.
  • }
  • } catch (Exception ex) {
  • throw ex;
  • }
  • }
  • /// <summary>
  • /// Permet de vider la liste de tous ses éléments.
  • /// </summary>
  • public void Clear() {
  • while (Count > 0) Remove(this[0]);
  • }
  • /// <summary>
  • /// Permet de savoir si l'élément spécifié appartient à la liste.
  • /// </summary>
  • /// <param name="item">L'élément recherché.</param>
  • /// <returns>True si l'élément appartient à la liste, false sinon.</returns>
  • public bool Contains(T item) {
  • return InternalList.Contains(item);
  • }
  • /// <summary>
  • /// Permet de copier les éléments de la liste dans un tableau.
  • /// </summary>
  • /// <param name="array">Le tableau dans lequel copier les éléments.</param>
  • /// <param name="arrayIndex">L'index de l'élément à partir duquel doit commencer la copie.</param>
  • public void CopyTo(T[] array, int arrayIndex) {
  • InternalList.CopyTo(array, arrayIndex);
  • }
  • /// <summary>
  • /// Permet de récupérer le nombre d'éléments de la liste.
  • /// </summary>
  • public int Count {
  • get { return InternalList.Count; }
  • }
  • /// <summary>
  • /// Permet de supprimer un élément de la liste.
  • /// </summary>
  • /// <param name="item">L'élément à supprimer.</param>
  • public bool Remove(T item) {
  • try {
  • lock(InternalList){
  • // Si l'élément qui est ajouté est null ou déjà dans la liste, on lève une exception.
  • if (item == null) throw new ObjectRemovedToEvtListNullError();
  • if (!InternalList.Contains(item)) throw new ObjectRemovedFromHLD_ListObjectsDoesNotExistsError();
  • InternalList.Remove(item); // Suppression de l'élément dans la liste.
  • LaunchRemoveEvents(item); // Lancement des évènements qui doivent survenir lors de la suppression d'un élément de la liste.
  • }
  • }catch (Exception ex) {
  • throw ex;
  • }
  • return true;
  • }
  • #endregion
  • #region IEnumerable<T> Members
  • public IEnumerator<T> GetEnumerator(){
  • return InternalList.GetEnumerator();
  • }
  • #endregion
  • #region IEnumerable Members
  • IEnumerator IEnumerable.GetEnumerator(){
  • return InternalList.GetEnumerator();
  • }
  • #endregion
  • }
/// <summary>
    /// Classe permettant de stocker une liste d'objets dont le type est de type T.
    /// Cette liste peut lever des évènements lors d'un ajout ou lors d'une suppression.
    /// Cette liste peut aussi lever des exception lorsque :
    /// - On tente d'ajouter un objet null à la liste,
    /// - On tente de retirer un objet null de la liste,
    /// - On tente d'ajouter un même objet plusieurs fois à la liste,
    /// - On tente de supprimer un objet de la liste alors que celui-ci n'existe pas.
    /// </summary>
    /// <typeparam name="T">Type de l'objet stocké dans la liste.</typeparam>
    [Serializable]
    public class EvtList <T> : IList<T>, IEnumerable, IEnumerable<T> // where T : VotreTypeObjetFari
    {
    	#region Privates

        /// <summary>
        /// Permet de stocker les éléments de la liste.
        /// </summary>
        private IList<T> InternalList = null;

        #endregion

        #region Events

        /// <summary>
        /// Survient lors de l'ajout d'un élément à la liste.
        /// </summary>
        public event EventHandler onAddInternal;

        /// <summary>
        /// Survient lors de l'ajout d'un élément à la liste.
        /// </summary>
        [field: NonSerialized]
        public event EventHandler onAdd;

        /// <summary>
        /// Survient lors de la suppression d'un élément de la liste.
        /// </summary>
        internal event EventHandler onRemoveInternal;

        /// <summary>
        /// Survient lors de la suppression d'un élément de la liste.
        /// </summary>
        [field: NonSerialized]
        public event EventHandler onRemove;

        /// <summary>
        /// Permet de lancer tous les évènements associés à l'ajout d'un élément à la liste.
        /// </summary>
        /// <param name="itemAdded">L'objet ajouté.</param>
        protected void LaunchAddEvents(T itemAdded) {
            try {
        		// Construction de l'objet AddEventArgs permettant de communiquer quel objet
                // a été ajouté à la liste.
                EvtListAddEventArgs args = new EvtListAddEventArgs(itemAdded);

                // Lancement des évènements onAddInternal puis onAdd.
                if (onAddInternal != null) onAddInternal(this, args);
                if (onAdd != null) onAdd(this, args);

        	} catch (Exception ex) {
                throw ex;
            }
        }

        /// <summary>
        /// Permet de lancer tous les évènements associés à la suppression d'un élément à la liste.
        /// </summary>
        /// <param name="itemRemoved">L'objet supprimé.</param>
        protected void LaunchRemoveEvents(T itemRemoved) {
            try {
                // Construction de l'objet AddEventArgs permettant de communiquer quel objet
                // a été supprimé à la liste.
                EvtListRemoveEventArgs args = new EvtListRemoveEventArgs(itemRemoved);

                // Lancement des évènements onAddInternal puis onAdd.
                if (onRemoveInternal != null) onRemoveInternal(this, args);
                if (onRemove != null) onRemove(this, args);

        	} catch (Exception ex) {
                throw ex;
        	}
        }

        #endregion

        #region Constructors
        public EvtList(){
        	InternalList = new List<T>();
        }
        
        public EvtList(IList<T> list){
        	InternalList = list;
        }
    	#endregion
    	
        #region IList<T> Members

        /// <summary>
        /// Permet de récupérer l'index d'un élément de la liste.
        /// </summary>
        /// <param name="item">L'élément recherché.</param>
        /// <returns>L'index de l'élément recherché.</returns>
        public int IndexOf(T item) {
            return InternalList.IndexOf(item);
        }

        /// <summary>
        /// Permet d'insérer un élément dans la liste, à un index précis.
        /// </summary>
        /// <param name="index">L'index ou l'objet doit être ajouté.</param>
        /// <param name="item">L'élément à ajouter.</param>
        public void Insert(int index, T item) {
            try{
        		lock(InternalList){
        		
	                // Si l'élément qui est ajouté est null ou déjà dans la liste, on lève une exception.
	                if (item == null) throw new ObjectAddedToEvtListNullError();
	                if (InternalList.Contains(item)) throw new ObjectAllreadyInEvtListError();
	
	                InternalList.Insert(index, item); // Insertion de l'élément dans la liste.
	                LaunchAddEvents(item); // Lancement des évènements qui doivent survenir lors de l'ajout d'un élément de la liste.
        		}
        	} catch (Exception ex) {
        		throw ex;
        	}
        }
        
        /// <summary>
        /// Permet de savoir si la liste est en lecture seule ou non.
        /// </summary>
        public bool IsReadOnly
        {
            get { return false; }
        }

        /// <summary>
        /// Permet de supprimer un élément de la liste à l'emplacement spécifié.
        /// </summary>
        /// <param name="index">L'index de l'élément à supprimer.</param>
        public void RemoveAt(int index) {
            try{
                T RemovedObject = InternalList[index]; // Stockage de l'élément supprimé pour l'envoyer aux gestionnaires d'évènements.
                InternalList.RemoveAt(index); // Suppression de l'élément dans la liste.
                LaunchRemoveEvents(RemovedObject); // Lancement des évènements qui doivent survenir lors de la suppression d'un élément de la liste.
            
        	} catch (Exception ex) {
        		throw ex;
        	}
        }

        /// <summary>
        /// Accesseur indexé. Permet d'accéder ou de modifier l'un des éléments de la liste à partir
        /// d'un index.
        /// </summary>
        /// <param name="index">L'index recherché.</param>
        /// <returns>L'élémént recherché.</returns>
        public T this[int index] {
            get { return InternalList[index]; }
            
            set {
                try{
                    //if (InternalList[index] != value) {
                        LaunchRemoveEvents(InternalList[index]);
                        InternalList[index] = value;
                        LaunchAddEvents(InternalList[index]);
                    //}
                } catch (Exception ex) {
            		throw ex;
            	}
            }
        }

        #endregion

        #region ICollection<T> Members

        /// <summary>
        /// Permet d'ajouter un élément à la liste.
        /// </summary>
        /// <param name="item"></param>
        public void Add(T item) {
            try {
                //Thread.BeginCriticalRegion(); // Début d'une région de thread critique.
                lock(InternalList){
                	// Si l'élément qui est ajouté est null ou déjà dans la liste, on lève une exception.
	                if (item == null) throw new ObjectAddedToEvtListNullError();
	                if (InternalList.Contains(item)) throw new ObjectAllreadyInEvtListError();
	
	                InternalList.Add(item); // Insertion de l'élément dans la liste.
	                LaunchAddEvents(item); // Lancement des évènements qui doivent survenir lors de l'ajout d'un élément de la liste.
                }
            } catch (Exception ex) {
            	throw ex;
            }
        }

        /// <summary>
        /// Permet de vider la liste de tous ses éléments.
        /// </summary>
        public void Clear() {
            while (Count > 0) Remove(this[0]);
        }

        /// <summary>
        /// Permet de savoir si l'élément spécifié appartient à la liste.
        /// </summary>
        /// <param name="item">L'élément recherché.</param>
        /// <returns>True si l'élément appartient à la liste, false sinon.</returns>
        public bool Contains(T item) {
            return InternalList.Contains(item);
        }

        /// <summary>
        /// Permet de copier les éléments de la liste dans un tableau.
        /// </summary>
        /// <param name="array">Le tableau dans lequel copier les éléments.</param>
        /// <param name="arrayIndex">L'index de l'élément à partir duquel doit commencer la copie.</param>
        public void CopyTo(T[] array, int arrayIndex) {
            InternalList.CopyTo(array, arrayIndex);
        }

        /// <summary>
        /// Permet de récupérer le nombre d'éléments de la liste.
        /// </summary>
        public int Count {
            get { return InternalList.Count; }
        }

        /// <summary>
        /// Permet de supprimer un élément de la liste.
        /// </summary>
        /// <param name="item">L'élément à supprimer.</param>
        public bool Remove(T item) {
            try {
        		lock(InternalList){
	        		// Si l'élément qui est ajouté est null ou déjà dans la liste, on lève une exception.
	                if (item == null) throw new ObjectRemovedToEvtListNullError();
	                if (!InternalList.Contains(item)) throw new ObjectRemovedFromHLD_ListObjectsDoesNotExistsError();
	
	                InternalList.Remove(item); // Suppression de l'élément dans la liste.
	                LaunchRemoveEvents(item); // Lancement des évènements qui doivent survenir lors de la suppression d'un élément de la liste.
        		}
        	}catch (Exception ex) {
        		throw ex;
        	}
            return true;
        }

        #endregion

        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator(){
            return InternalList.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator(){
            return InternalList.GetEnumerator();
        }

        #endregion
    }

Conclusion

Code créé avec SharpDevelop.
Les listes génériques sont apparues avec la version 2 du Framework .net. Toute tentative de compilation avec une version antérieure du Framework se soldera par un échec.
Pour les "Membres Club", vous pouvez télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip

02 octobre 2007 11:15:46 :
MAJ sur la description du côté thread-safe de cette liste (suite à la question de Yxion)
02 octobre 2007 15:03:12 :
Remplacement des Thread.BeginCriticalRegion par des lock.
  • signaler à un administrateur
    Commentaire de Yxion le 02/10/2007 10:43:44

    Bien, tu me coupes pour ainsi dire l'herbe sous le pieds, je voulais déposer une source un peux dans ce genre la... mais pas tout à fais quand même.
    Juste une question, le "Thread.BeginCriticalRegion();" et celui de fin, c'est pas que je critique, mais il servent à quoi... je n'ai jamais utilisé ca.
    Ce qui peux etre intéressant, ce serait d'avoir l'index dans l'eventargs, ca m'est arrivé d'en avoir besoin pour séléctionner le précédent ou suivant après une suppression.

  • signaler à un administrateur
    Commentaire de yoannd le 02/10/2007 11:09:41

    Salut,

    Les Thread.BeginCriticalRegion(); permettent ici d'avoir des listes qui sont thread-safe, en quelque sorte.

    Je ne sais pas si tu as vu, mais on lève une exception si un même élément est ajouté 2 fois à la liste.
    Imaginons 2 threads qui utilisent tous les 2 la méthode Add en même temps, et avec le même objet à ajouter. S'ils arrivent tous les 2 en même temps à la ligne "if (InternalList.Contains(item))", ils vont passer le test avec succès, et ajouter 2 fois (1 fois par thread) le même élément à la liste.

    Thread.BeginCriticalRegion(); permet d'éviter ce souci en indiquant à la CLR que tout ce qui ce trouve entre le begin et le end ne doit être compris que comme une seule et même instruction "unitaire".

    Pour ce qui est de l'index dans le eventargs, pas de souci, il peut très bien être ajouté sans trop de problèmes, mais là encore, le Thread.BeginCriticalRegion(); est important. Si un thread ajoute un élément, et que l'autre en retire un en même temps, alors les index envoyés dans les event args ne seront pas forcément justes au moment ou ils seront récupérés par l'écouteur de l'évènement si on est pas thread-safe...

    Et sinon, désolé de t'avoir coupé l'herbe sous le pied, mais si tu publies tout de même ta source, préviens-moi, je serais ravi d'y jeter un coup d'oeuil ;-)

  • signaler à un administrateur
    Commentaire de Yxion le 02/10/2007 11:24:15 10/10

    Ben merci pour cette réponse.
    Ta source est mieux et à beaucoup plus d'intéret par le fait qu'elle soit thread-safe.
    Je voulais juste faire un genre de ListEvent<T>... une List<> avec un événement add remove.. bien trop léger pour en faire un post... quand je dis que tu m'as coupé l'herbe sous le pieds, c'est que ces temps si j'utilise souvent des IList avec events, et j'avais dans l'idée d'en faire quelque chose de plus Générique... après 4 collections de suites, on se dis qu'on se prends la tete pour pas grand choses lol.

  • signaler à un administrateur
    Commentaire de yoannd le 02/10/2007 11:39:26

    Et bien si cette solution de liste peut te rendre des services, ma foi ^^
    Il est vrai en plus que l'explication du thread-safe manquait cruellement !
    Au fait, les petites étoiles à côté de ton pseudo dans le second message, ça veut dire quoi ? C'est pour indiquer que c'est toi qui as mis la note (Si c'est le cas, merki) ?

  • signaler à un administrateur
    Commentaire de Yxion le 02/10/2007 11:48:55

    Oui, j'avais pas vu, heureusement que j'ai pas mis une sale note, j'aurais été vendu aussitôt... lol

  • signaler à un administrateur
    Commentaire de yoannd le 02/10/2007 11:51:29

    lol

    N'empèche, c'est pas mal ce système, parceque parfois, tu en as qui mettent des salles notes sans se dévoiler... un peu facile, je trouve :-)

    En tout cas merci pour le 10 ^^

  • signaler à un administrateur
    Commentaire de Yxion le 02/10/2007 11:56:26

    Oui, ca va éviter certains de mettre une sale note sans expliquer pourquoi.

  • signaler à un administrateur
    Commentaire de billou_13 le 02/10/2007 11:56:47

    Salut,

    Merci pour ton explication, elle a l'air fortement intéressante. Je l'ai lu en diagonale (faut quand même bosser un peu ^^) pour l'instant et je me pencherais dessus plus tard.

    Beaucoup de choses m'intéresse au premier abord et je voudrais te poser deux petites questions (pour l'instant) :

    - je voudrais me rassurer en te demandant quelle est la différence entre lock(this) { } et Thread.BeginCriticalRegion() ? Pour moi, cela viendrait juste du fait que tout l'objet est bloqué pour le lock tandis que pour l'autre cas, juste le morceau de code à exécuter est bloqué. Ai-je bien compris ? (j'avoue pêcher encore toutes ces histoires ^^)

    - Pour ma part, lorsque je veux faire un peu la même chose que toi, j'avais pour habitude d'hériter de la classe List<T> directement et non pas IList<T>. C'était la première solution que j'avais trouvé et je m'en suis bien contenter. Alors je me dis que peut être j'ai fait des conneries ou pas... Peux-tu m'expliquer pourquoi ton choix va vers l'interface IList<T> plutot que la classe List<T> ?

  • signaler à un administrateur
    Commentaire de yoannd le 02/10/2007 13:22:46

    Salut Billou_13,

    Alors pour répondre à tes questions :

    - Il me semble que Lock permet de poser un vérou sur un objet précis, interdisant la lecture/écriture sur une instance d'un objet tant que le vérou n'est pas levé. Si le thread 1 pose un lock sur l'objet "Obj", le thread 2 sera mis en attente jusqu'à ce que le Thread 1 lève son lock. Quand au Thread.BeginCriticalRegion, il permet de ne traiter un ensemble d'instruction qu'en une seule fois, comme s'il s'agissait d'un seul et même bloc. Pour tout t'avouer, je ne suis pas très callé sur ce point là moi non plus, et je pense que je vais creuser ce point dans quelques heures/jours.

    - Sinon, j'ai effectivement dérivé moi aussi de l'objet List<T>, au début, mais la solution présentée ici (et qui implémente l'interface IList) a pour particularité de pouvoir ajouter de nouvelles responsabilités à un objet List<T>. Concrètement, si tu as un objet dont tu ne dispose pas du code, et qui est de type List, tu peux le "décorer" (c'est le nom du pattern) avec l'objet EvtList. Ton objet de liste aura alors des responsabilités supplémentaires, celle-ci étant de lever un évènement lors d'un Add ou lors d'un Remove. Bon concrètement, il est possible de dériver de List<T>, mais il faut mieux implémenter IList<T> (implémenter une interface plutôt que dériver d'une classe semble être un principe assez admis chez les architectes, dites-moi si je me trompe)...

    voilou :-)

  • signaler à un administrateur
    Commentaire de billou_13 le 02/10/2007 13:37:09

    Merci beaucoup pour tes réponses. Elles me semblent honorables et avoir une raison d'être (si c'est pas beau ça ^^)

    Je prends plus que note de ton exemple et je n'hésiterais pas à le suivre dans mes prochains développements.

    PS: désolé si j'ai pas mis de note, comme je t'ai dit, je ne l'ai lu qu'en diagonale pour l'instant. Je vais creuser aussi ce point dans quelques heures/jours... LoL

  • signaler à un administrateur
    Commentaire de yoannd le 02/10/2007 14:55:59

    Je viens de compulser l'aide msdn, et il s'avère que le lock correspond à ce que je voulais faire, alors que le thread.BeginCriticalRegion fait autre chose.

    Petites explications :
    lock : bloque l'accès à une ressource et traite toutes les instructions qu'il contient en une fois. Autrement dit, il y a exclusion mutuelle : ce mot clé permet de s'assurer qu'un thread ne se trouve pas dans la partie de code concernée en même temps qu'un autre thread.

    thread.BeginCriticalRegion : permet de spécifier au sein d'un thread que la région entourée par le begin et le end ne peut pas être annulée sans mettre en péril les données de l'application. On indique ainsi qu'il y a une région critique entre le begin et le end. Définition officielle de la msdn : "Avertit un hôte que l'exécution est sur le point d'entrer dans une région de code dans laquelle les effets d'un abandon de thread ou d'une exception non gérée peuvent compromettre d'autres tâches dans le domaine d'application.".

    Voila pour ce point, cela veut dire qu'il me faut modifier un peu ma source :-)

  • signaler à un administrateur
    Commentaire de Malkuth le 17/11/2007 18:35:24

    Petite précision sur le lock, il est déconseillé de faire un
        lock(this){ ... }

    Car d'autre partie du programme pourais vouloir posé un vérou
    sur l'objet pour des raison totalement différente que celle de
    l'ajout d'un nouvel item dans la liste, pour ma part, je déclare
    un un objet suplémentaire dans ma classe pour géré le lock :
        private object modif_LOCK = new object();

    On utilise ensuite :
        lock(this.modif_LOCK) { ... }

    On peut aussi noté que lock n'est pas une instruction du framework
    à proprement parlé mais un artifice du compilateur C# qu'on peut
    interprété comme ceci :
        try
        {
            Sytem.Threading.Monitor.Enter(this.modif_LOCK);
            ...
        }
        finally
        {
            Sytem.Threading.Monitor.Exit(this.modif_LOCK);
        }

    Il est possible de dans certain cas d'utilisé d'autre classe que
    Sytem.Threading.Monitor pour géré les vérroux comme par example :
        ReaderWriterLock
    qui permet de diférencié les opérations de lectures(qui ne se lock pas entre elles) des opérations d'écriture (qui sont exclusive).

  • signaler à un administrateur
    Commentaire de flashkel le 23/11/2007 10:45:00 8/10

    Sauf qu'ici il fait un lock(base) ce qui revient (dans le contexte d'une implémentation d'une IList<T>) à ce que tu conseilles MALKUTH mais le conseil est judicieux pour d'autre cas.

Ajouter un commentaire

Discussions en rapport avec ce code source

Appels d'offres

Pub



Snippets en rapport

CalendriCode

Mai 2008
LMMJVSD
   1234
567891011
12131415161718
19202122232425
262728293031 

VS Express FR Gratuit !

VS Express en français et 100% gratuit !

Téléchargements

Logiciels à télécharger sur le même thème :

Boutique

Boutique de goodies CodeS-SourceS