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é: 4 948 / 137

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

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

Commentaire sur cette source (15)
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.
 

Fichier Zip

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

Historique

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.

Commentaires et avis

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.

signaler à un administrateur
Commentaire de TheManu le 24/03/2009 15:16:21 9/10

Sympa ta classe !
Par contre :
- Dans 'Clear()', pourquoi ne pas utiliser 'RemoveAt(0)' plutôt que 'Remove(this[0])' ?
De plus (et de toute façon), si un autre thread supprime entre le 'while' et le 'Remove' (ou le 'this[0]') un élément ET que celui-ci été le dernier t'es marron ! Je sais que c'est plus pour le tutorial mais je ne pense pas que tu puisses (pour simplifier l'écriture) te passer d'un 'lock' englobant la boucle d'effacement et le lancement d'évènements. Pour simplifier il faudrait (je pense) l'appel en boucle (et dans un 'lock') d'un "RemoveAtNonSafeThread(0)"...
- De même : l'énumération sur la liste (grâce à 'GetEnumerator') n'est pas "thread-safe" !

signaler à un administrateur
Commentaire de yoannd le 25/03/2009 13:51:57

très juste TheManu ;)

Ajouter un commentaire

Discussions en rapport avec ce code source dans le forum

Serialization Soap [ par zaka48 ] j'ai un probleme avec la declaration de l'esapce de nom using System.Runtime.Serialization;using System.Runtime.Serialization.Formatters.Soap;c'est ce Serialization sélective dans un formulaire [ par Padkartiai ] Bonjour,Sauriez-vous s'il est possible de faire une s&#233;rialisation s&#233;lective dans un formulaire windows ?Je souhaite uniquement conserver la [C#] SOAP & Serialization [ par scoubidou944 ] mon code marche enfin &#224; moiti&#233;. lors de la d&#233;s&#233;rialisation, je me prend souvent SerializationException : Erreu d'analyse, aucun as C# Probleme Serialization [ par Wolf007 ] Bonjour &#224; tous.Je d&#233;sire faire pass&#233; en&nbsp;r&#233;seau&nbsp;un tableau de string. Pour cela j'utilise la s&#233;rialization mais je n remoting icollection [ par dieulapin ] Bonjour,J'essaye de faire du remoting en .net. Je veux creer un objet activable de type icollectionvoila comment j'ai proced&#233; :une classe Personu [C#] Serialization XML [ par scoubidou944 ] voici un article sympa :http://alain.vizzini.free.fr/article01.htmlil fonctionne moyennant une erreur dans un parametre qui doit etre un double au lie Problème sérialisation XML WebService [ par blat ] Bonjour,Voila mon problème : je tente de créer un webservice qui accède à un autre webservice. Pour ce faire, j'ai crée un proxy du webservice que je Serialization et XSL [ par MasterShadows ] Bonjour à toutes et à tous, alors dans le cadre de mon projet pour sauvegarder des objets j'utilise la sérialization ( l'espace de noms System.Xml.Ser Serialization ou Parsing ? [ par Flamandier ] Bonjour,J'ai vu beaucoup d'exemples sur la serialization / deserialization d'objets instance de classes Serializable.Mais ces exemples restent simples [c#] Serialization XML [ par cedkat ] Bonjour,Voilà mon problème, j'ai créer un objet que je serialize en xml. Cette oblet contient une ArrayList comme ceci :[XmlArray("voitures", IsNullab


Nos sponsors

Sondage...

CalendriCode

Juillet 2009
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
2728293031  

Consulter la suite du CalendriCode

Comparez les prix Nouvelle version

Photothèque Nouveau !



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 0,718 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.