[C# 2.0] classes abstraites et events

Voilà mon problème qui m’embête un peu :

J’ai une classe abstraite qui déclare quelques méthodes, quelques propriétés, et surtout qui contient un event Modified.
Le but est de gérer un fichier de configuration XML qu’on sérialise / désérialise.

Bref les objets à sérialiser sont tous des instances de classes dérivées de ma classe abstraite, contenant cet event.

Je colle le code de ma classe abstraite :

[code]using System;
using System.ComponentModel;
using System.Xml.Serialization;

namespace PMLSync
{
public abstract class XmlConfigClass
{
private bool m_IsModified;
private bool m_RaiseEvents;

	public XmlConfigClass()
	{
		this.m_IsModified = false;
		this.m_RaiseEvents = true;
	}

	[XmlIgnore()]
	public bool IsModified
	{
		get { return m_IsModified; }
	}

	public event EventHandler Modified;

	protected void SetValue<T>(ref T var, ref T value)
	{
		if (!var.Equals(value))
		{
			var = value;
			if (this.m_RaiseEvents)
			{
				this.m_IsModified = true;
				if (this.Modified != null)
					this.Modified(null, EventArgs.Empty);
			}
		}
	}

	public void ResetModifiedFlag()
	{
		this.m_IsModified = false;
	}
}

}[/code]

Maintenant un bout de code simplifié de l’une des classes dérivées :

[code]using System;
using System.ComponentModel;
using System.Xml.Serialization;

namespace PMLSync
{
[Serializable()]
public class XmlConfigSqlServer : XmlConfigClass
{
private string m_StringAlaCon;

	private const string S_DEFAULT_STRINGALACON = "Powered by Murphy laws";

	[DefaultValue(S_DEFAULT_STRINGALACON)]
	public string StringAlaCon
	{
		get { return this.m_StringAlaCon; }
		set { this.SetValue<string>(ref this.m_StringAlaCon, ref value); }
	}

	public XmlConfigSqlServer()
	{
		this.m_StringAlaCon = S_DEFAULT_STRINGALACON;
	}
}

}[/code]

Le SetValue doit donc lever un event en cas de désérialisation de mon fichier XML (puisqu’il passe par les champs publics, donc traverse le setter pour atteindre le SetValue défini dans ma classe abstraite), ou n’importe quoi d’autre.

Dans mon cas c’est une textbox qui vient changer la valeur via une fenêtre de configuration.
Dans mon appli, j’ajoute bien des EventHandler sur l’event de ma classe dérivé, et depuis le contexte formulaire, le debugger voit bien que l’event est pas null.

En faisant du pas à pas, on traverse le setter, on arrive dans ma classe abstraite, à l’intérieur de SetValue et là, this.Modified est null, l’event est pas levé… :stuck_out_tongue:

A côté de ça, les autres variables, comme m_IsModified, sont bien comme il faut…
Je ne comprend vraiment pas…

Merci d’avance à celui / ceux qui sauront trouver réponse à ça.

J’aurai bien aimé mettre en gras des points importants du code précédent…
Malheureusement c’est impossible, donc je les liste :

Classe abstraite XmlConfigClass, méthode publique SetValue (generic, parce que SAIBON les generic (surtout pour la SECU))

if (this.m_RaiseEvents) { &nbsp; &nbsp;this.m_IsModified = true; &nbsp; &nbsp;if (this.Modified != null) &nbsp; &nbsp; &nbsp; &nbsp;this.Modified(null, EventArgs.Empty); }

this.m_RaiseEvents est toujours vrai dans mon cas, pas de panique.

Classe dérivée XmlConfigSqlServer, accesseur set du champ public StringAlaCon

Peut être que ça aidera un peu plus à la réflexion…

Nota Bene: Bon je sais pas ce que tu as ailleurs dans ton projet donc je vais me permettre des critiques basé sur ce que je vois, j’espere que tu le prendra pas mal. Donc je donne mon avis comme je le sens basé sur le peu de choses que je vois.
Donc:

Bhoulala :stuck_out_tongue: Comment tu over engineer a moooooort. Je pense que tu t’es un peu envolé la sur le coup :stuck_out_tongue:

C’est 50 fois trop compliqué pour ce que tu veux faire :stuck_out_tongue: Et puis les ref, beuuuuuuurk, beurk, beurk, die, die, die. On est pas en C++, die les refs. Et les generics, c’est bien les nouveaux trucs mais faut pas tenter des les mettre a toute les sauces juste parceque c’est c’est cool :P. La par exemple, non quoi. (voir plus loin).

Pas besoin de generic ici, a la limite pas besoin de classe abstraite de base et tout (a part pour lancer la serialization/deserialization a la limite).

Deja le modele de base d’une classe qui lance des events et qui fait ca en optimisé c’est:

[code]public class Control {
   private static readonly object EventModified = new object();
   private bool modified;

   public bool Modified {
       get {
           return modified;
       }
       set {
           if(value != modified) {
               modified = value;
               OnModifiedChanged(EventArgs.Empty);
           }
        }
   }
   public event EventHandler ModifiedChanged {
       add {
           Events.AddHandler(EventModified, value);
       }
       remove {
           Events.RemoveHandler(EventModified, value);
       }
   }
   protected virtual void OnModifiedChanged(EventArgs e) {
       // faire ce qu’il faut faire pour reagir a un truc de config modifie
       // genre serializer ou je sais pas quoi :slight_smile:
     
       // lancer l’event…
       EventHandler eh = Events[EventModified] as EventHandler;
       if (eh != null) {
           eh(this, e);
       }
   }
}[/code]

Comme indiqué sur cette page de MSDN.

Ensuite faire un set des values dans la classe de base avec ta fonction generique c’est mega moche et c’est pas intuitif du tout si je peux me permettre :stuck_out_tongue: Pourquoi donc es tu en train d’aller dans la classe de base pour changer une variable qui est definie PILE POIL la dans ta classe en cours et qui t’es accessible directement? Ca a pas de sens, c’est le plaisir de se prendre la tete et de passer par la boite aux lettres pour repeindre le couloir B)

Le mieux c’est si tu veux faire une serialization custom par objet, c’est d’overrider le processus de serialization en implementant ISerializable dans chaque classe et dans ta classe de base si tu as des choses commune a tout tes fichiers. Donc tu rajoute au serializer tes proprietes que tu veux serializer, tu appelle ta base, elle fait pareil, et basta, serialization custom terminee. Ce que tu as tout en bas dans ta classe abstraite, c’est juste la creation d’un fichier, son ouverture en ecriture et le dump du truc serializé dedans.

Ensuite tu veux pas exposer un ResetModified flag, tu veux que il se reset tout seul une fois que tu as fini de serializer. Expose un Save() qui lance la serialization/sauvegarde si tu veux dans la classe de base, et qui reset le flag tout seul, pas besoin de le faire toi meme.

Ensuite bien sur avec ce systeme tu pourras avoir plein de classes ailleurs dans ton projet qui s’attachent a l’event pour savoir quand la config a changé et reagir en consequence ce qui est je pense un peu le but de la manoeuvre :stuck_out_tongue: Tu veux d’ailleurs savoir quand le flag Modified a changé, pas forcement quand il se fait reset, donc a toi de voir si tu veux pas l’appeler OnModifiedChanged ou juste OnModified et de le lancer uniquement quand modified est mis a true (sinon ton event repartira quand tu reset le flag)…

Enfin la j’ai pas tout ce que tu veux faire mais je crois qu’on peut grandement simplier le truc B)

Pas de soucis :stuck_out_tongue:
J’ai voulu faire simple et éviter de coller ouate-mille lignes de codes, donc j’ai préféré ne pas expliquer ce qu’il y a autour.

En fait tout part d’une classe XmlConfig statique, avec une méthode Load(), une méthode Save(), 3 events (Loaded, Saved, Modified), et un champ contenant l’objet à sérialiser.

Le souci c’est que dans cet objet (qui dérive de ma classe abstraite), il contient d’autre objets eux aussi à sérialiser, tous dérivés de ma classe abstraite, afin de lever chacun un event Modified.

Le principe étant: n’importe quel sous-objet de ma classe statique qui lève un event Modified, lève l’event Modified racine, afin de prévenir que les données sérialisées sont différentes (par exemple, dans une interface, ça active le bouton « Enregistrer les modifications », qui lui, sur action utilisateur, lance la sérialisation).

La première solution que j’avais trouvé pour lever le bon event était la fameuse méthode SetValue dans les setters.

Je regarde ça de plus près, et je refond l’ensemble si ça me convient pas (la solution « clefs-en-mains » des properties VS2005 ne me convenait pas).

Merci encore pour tes réponses Glop :stuck_out_tongue: