J'ai déjà parlé à plusieurs reprises des reactives extensions, que l'on appelle plus familièrement Rx. Je me suis dit que ce serait une bonne idée que de vous les présenter plus en détail. Je ne vais pas écrire un article qui va tout vous expliquer depuis le début, car il existe déjà beaucoup de ressources. (ici, ici, ou bien ici, ou même encore là) Je me suis donc dit que ça serait peut-être une meilleure idée que de vous expliquer comment ça peut fonctionner grâce à un exemple concret. Et comme j'avais déjà un article expliquant comment réaliser une opération asynchrone en utilisant la TPL, j'ai repris cet exemple, en utilisant cette fois Rx.
Monday 12 April 2010
Introduction aux Reactive Extensions : Télécharger un fichier en asynchrone
By Michael DELVA on Monday 12 April 2010, 14:58
Tuesday 6 April 2010
Aide à l'injection de dépendances pour Ninject grâce à une interface "fluent"
By Michael DELVA on Tuesday 6 April 2010, 14:48
Je vais aujourd'hui vous présenter le résultat d'un refactoring de masse dans une série de tests unitaires, qui a permis d'accroitre très fortement la lecture des tests, et surtout leur compréhension.
La classe testée a pour but de gérer les mises à jour d'une liste de programmes installés sur un ordinateur. Cette classe est très "high-level" dans la mesure où elle permet d'aller récupérer la liste de toutes les mises à jour de toute une gamme de produits, de savoir pour chaque programme quelles sont les mises à jour à installer, puis bien évidemment de télécharger ces mises à jour et les installer.
Comme vous le voyez, cette classe consiste en beaucoup de sous-tâches, qui vont toutes communiquer avec le monde extérieur (internet notamment, pour l'obtention des mises à jours et leur téléchargement). Dans le but d'obtenir des tests unitaires les plus isolés possibles, j'en suis bien sûr arrivé à décomposer chacune de ces sous-tâches et à les identifier par des interfaces bien distinctes, pour lesquelles je laisse à Ninject le soin de les injecter comme il faut.
Rien que du très classique en somme. Le hic étant que cette classe nécessite du coup l'injection de beaucoup de dépendances (5 !) et que la mise en place des mocks dans chaque test rend le test trop long, et sa relecture difficile.
Voici donc une solution pour éviter ce genre de soucis. Solution qui se base, comme vous allez le constater, sur une hiérarchie de Providers Ninject, et une classe de construction d'un Module, qu'on rendra "Fluent".
Monday 8 March 2010
Reactive Extensions
By Michael DELVA on Monday 8 March 2010, 10:14
En fin de semaine dernière est sortie la dernière release des Reactive Extensions.
Beaucoup de changements sur la release note, mais on notera principalement la compatibilité .Net 4 RC.
A voir également, une vidéo de 22' sur Channel 9.
Source : Bart De Smet'S
Saturday 20 February 2010
Resharper 5 beta 2
By Michael DELVA on Saturday 20 February 2010, 19:27
Un petit billet pour vous informer, si vous ne l'étiez pas déjà bien sûr, de la disponibilité toute récente de Resharper en beta 2 s'il vous plait!
A télécharger donc d'urgence si vous bossez sous VS2010!
Wednesday 10 February 2010
Copie d'objets
By Michael DELVA on Wednesday 10 February 2010, 21:47
La copie d'objets en C# est vraiment un vaste débat, dans lequel il est facile de se perdre au vu des différentes solutions proposées lorsque vous faites une petite recherche sur Google.
Je vais essayer dans cet article d'expliquer pourquoi il ne vaut mieux pas utiliser l'interface ICloneable du framework, les 2 types de copie réalisables en C#, et je finirai par un exemple concret qui mettra en évidence (qui le tentera du moins) quelle méthodologie appliquer en fonction de ce que vos classes ont à copier.
Monday 25 January 2010
Classe Helper pour gérer soi-même le DoubleBuffering d'un contrôle Winforms
By Michael DELVA on Monday 25 January 2010, 14:57
Le souci
J'ai récemment du implémenter le dessin d'une grille (comme dans l'éditeur de Visual Studio par exemple) sur un contrôle de type Panel. Innocemment, mon premier essai a été de m'abonner à l'évènement OnPaint du contrôle, et d'ajouter le code suivant:
private void Foo_Paint(object sender, PaintEventArgs e)
{
using (Pen pen = new Pen(Color.Gray, 1))
for (int x = 10; x < e.ClipRectangle.Width; x += 10)
for (int y = 10; y < e.ClipRectangle.Height; y += 10)
e.Graphics.DrawRectangle(pen, new Rectangle(new Point(x, y), new Size(1, 1)));
}
Je lance l'application, j'ai bien une grille qui est dessinée. Souci: dès que la form est rafraichie (quand on la redimensionne, ou qu'on change les propriétés d'un de ses contrôles enfants), la grille est redessinée, ce qui entraine de violentes lenteurs de l'affichage, avec du bon gros flickering.
La solution pour éviter ce souci est d'utiliser la technique du double-buffering.
La solution
Nous allons implémenter cette stratégie du double-buffer en créant une nouvelle classe, qui prendra en paramètre le contrôle à utiliser, ce qui nous permettra de réutiliser facilement et à moindre frais le code. L'idée est d'utiliser les classes BufferedGraphicsContext et BufferedGraphics pour dessiner l'arrière-plan du contrôle dans un buffer, et quand le contrôle doit se redessiner, on applique le contenu du buffer directement dans le contrôle. Et c'est donc en évitant le dessin progressif de l'arrière-plan du contrôle que l'on va éviter le scintillement.
Mais voici tout de suite la première version de notre classe:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace Tools.GUI
{
public class DoubleBuffer : IDisposable
{
private readonly Control control;
private bool dirty;
private BufferedGraphicsContext bufferedGraphicsContext;
private BufferedGraphics bufferedGraphics;
public DoubleBuffer(Control control)
{
this.control = control;
bufferedGraphicsContext = new BufferedGraphicsContext();
CreateGraphicsBuffer();
this.control.SizeChanged += control_SizeChanged;
}
void control_SizeChanged(object sender, EventArgs e)
{
CreateGraphicsBuffer();
}
private void CreateGraphicsBuffer()
{
if (bufferedGraphics != null)
{
bufferedGraphics.Dispose();
bufferedGraphics = null;
}
if (bufferedGraphicsContext == null || control.DisplayRectangle.Width <= 0 || control.DisplayRectangle.Height <= 0)
return;
using (Graphics graphics = control.CreateGraphics())
bufferedGraphics = bufferedGraphicsContext.Allocate(graphics, control.DisplayRectangle);
Dirty = true;
}
private bool Dirty
{
get { return dirty; }
set
{
if (!value)
return;
dirty = true;
control.Invalidate();
}
}
public void Dispose()
{
if (bufferedGraphics != null)
{
bufferedGraphics.Dispose();
bufferedGraphics = null;
}
if (bufferedGraphicsContext != null)
{
bufferedGraphicsContext.Dispose();
bufferedGraphicsContext = null;
}
}
}
}
On commence par créer une nouvelle instance de la classe BufferedGraphicsContext, puis on crée l'instance de BufferedGraphics grâce à l'appel de la fonction CreateGraphicsBuffer, où l'on commence par supprimer l'instance existante, puis où l'on crée effectivement la nouvelle instance grâce à un appel à la fonction Allocate de BufferedGraphicsContext, qui prend en paramètre le Graphics du contrôle, ainsi que le rectangle correspondant à la surface affichée du contrôle. Vous aurez remarqué que l'on ne crée pas le buffer si le contrôle a une largeur ou une hauteur égale à zéro.
Comme on a un nouveau buffer, on met le flag Dirty à true, qui aura pour conséquence de forcer un ré-affichage du contrôle via l'appel de Invalidate() (dans le setter de Dirty). Dans le constructeur de la classe, on s'abonne également à l'évènement SizeChanged du contrôle, car nous devons toujours avoir un buffer qui ait les mêmes dimensions que le contrôle.
C'est bien beau, mais nous n'affichons toujours rien pour le moment. Nous allons corriger ça de suite, on commençant par nous abonner à l'évènement OnPaint du contrôle, dans le constructeur de la classe:
public DoubleBuffer(Control control)
{
this.control = control;
bufferedGraphicsContext = new BufferedGraphicsContext();
CreateGraphicsBuffer();
this.control.SizeChanged += control_SizeChanged;
this.control.Paint += control_Paint;
}
Nous allons ensuite définir la fonction control_Paint:
void control_Paint(object sender, PaintEventArgs e)
{
if (bufferedGraphics == null)
{
Draw(e.Graphics);
return;
}
if (Dirty)
{
Dirty = false;
Draw(bufferedGraphics.Graphics);
}
bufferedGraphics.Render(e.Graphics);
}
Le fonctionnement du double buffer se fait ici: si on n'a pas de buffer (par exemple, le contrôle a sa largeur ou sa hauteur qui est nulle) , on va dessiner le contenu de l'arrière-plan du contrôle normalement, en passant à la fonction Draw le Graphics du contrôle, auquel on accède grâce à la propriété Graphics de PaintEventArgs. Si par contre on a un buffer, on commence par regarder l'état du flag Dirty. S'il est à true, c'est que l'on doit redessiner le buffer, ce qui est fait en passant la propriété Graphics de la classe BufferedGraphics. Et pour terminer, on appelle la fonction Render de BufferedGraphics, qui va écrire le contenu du buffer directement dans l'objet Graphics donné en paramètre, c'est à dire dans le Graphics du contrôle, provoquant donc l'affichage du contrôle.
Nous faisons mention dans le code précédent d'une fonction Draw, que voici:
private void Draw(Graphics graphics)
{
if (control.ClientRectangle.Width <= 0 || control.ClientRectangle.Height <= 0)
return;
using (SolidBrush backBrush = new SolidBrush(control.BackColor))
graphics.FillRectangle(backBrush, control.ClientRectangle);
}
Tout ce que fait cette fonction pour le moment, c'est juste de remplir l'arrière-plan du contrôle avec la couleur définie par la propriété BackColor. Pas très intéressant donc. Il serait bien de pouvoir définir des fonctions à exécuter dans la fonction Draw, afin de personnaliser l'affichage, et de profiter du double buffering mis en place. Nous allons arriver à ça en utilisant une liste d'actions, qui seront exécutées à la fin de la fonction Draw. Les actions étant enregistrées par l'utilisateur de la class DoubleBuffer via une fonction RegisterPaintAction.
Nous allons commencer par ajouter un nouveau membre à notre classe:
private readonly List<Action<Graphics, Rectangle>> paintDelegates = new List<Action<Graphics, Rectangle>>();
Puis nous exécutons la liste des actions dans la fonction Draw:
private void Draw(Graphics graphics)
{
if (control.ClientRectangle.Width <= 0 || control.ClientRectangle.Height <= 0)
return;
using (SolidBrush backBrush = new SolidBrush(control.BackColor))
graphics.FillRectangle(backBrush, control.DisplayRectangle);
paintDelegates.ForEach(action => action(graphics, control.DisplayRectangle));
}
Et nous créons les fonctions d'enregistrement et de dés-enregistrement des actions (utile pour par exemple afficher ou pas la grille):
public void RegisterPaintAction(Action paintDelegate)
{
if (paintDelegate == null)
return;
paintDelegates.Add(paintDelegate);
Dirty = true;
}
public void UnregisterPaintDelegate(Action paintDelegate)
{
if (paintDelegates.Remove(paintDelegate))
Dirty = true;
}
On met le flag Dirty à true quand on ajoute une nouvelle action pour forcer un ré-affichage du contrôle immédiat.
Et voilà! Et pour utiliser tout ça, un petit exemple d'affichage de grille dans l'arrière-plan de la form:
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private readonly DoubleBuffer doubleBuffer;
public Form1()
{
InitializeComponent();
doubleBuffer = new DoubleBuffer(this);
doubleBuffer.RegisterPaintAction((graphics, rectangle) =>
{
using (Pen pen = new Pen(Color.Gray, 1))
for (int x = 10; x < rectangle.Width; x += 10)
for (int y = 10; y < rectangle.Height; y += 10)
graphics.DrawRectangle(pen, new Rectangle(new Point(x, y), new Size(1, 1)));
});
}
}
}
C'est tout pour cette fois ;) N'hésitez pas à me laisser vos impressions ou vos retours sur ce code!
PS: je joins à cet article le code complet de la classe DoubleBuffer.
Monday 14 December 2009
"Magic" null argument testing
By Michael DELVA on Monday 14 December 2009, 13:00
Trouvée ici, une astuce vraiment *TRES* tricky, qui permet de simplifier (?) le test sur la "nullité" d'un ou plusieurs arguments, le tout à base d'expression trees et de types anonymes...
Monday 9 March 2009
Savoir si un type est dérivé d'un type générique
By Michael DELVA on Monday 9 March 2009, 10:32
Je cherchais récemment à détecter grâce à la reflection tous les types d’une assembly qui dérivaient d’un type de base générique, afin d’utiliser une version modifiée de la factory que j’ai déjà présenté sur ce blog. Soit par exemple un type comme celui-ci: class Toto : Base<int> Pour cela, j’ai trouvé sur internet une petite fonction qui fait ça bien comme il faut:
private static bool IsTypeDerivedFromGenericType(Type typeToCheck, Type genericType)
{
if (typeToCheck == typeof(object))
return false;
if (typeToCheck == null)
return false;
if (typeToCheck.IsGenericType && typeToCheck.GetGenericTypeDefinition() == genericType)
return true;
return IsTypeDerivedFromGenericType(typeToCheck.BaseType, genericType);
}
A l’utilisation, on a donc:
Assembly assembly = Assembly.GetExecutingAssembly();
// Loop each type in assembly
foreach (Type type in assembly.GetTypes())
{
if (type.IsClass && !type.IsAbstract && IsTypeDerivedFromGenericType(type, typeof(GenericManager<>)))
{
//...
}
}
A mettre d’urgence dans votre DLL à tout faire!
Thursday 20 November 2008
Benchmark sur les itérations
By Michael DELVA on Thursday 20 November 2008, 09:55
Je viens de lire cet article relativement court, mais intéressant, qui permet de mieux situer l'impact sur les performances entre une itération à base de for et à base de foreach, ainsi qu'entre un tableau classique (Array) et une liste générique.
Conclusion: préférez for à foreach quand les performances sont importantes, car il se révèle à peu près 2 fois plus rapide. Mais préférez dans tous les autres cas foreach, pour des questions évidentes de lisibilité.
Wednesday 30 July 2008
Utiliser des mock objects pour émuler une couche de persistance (Correctif)
By Michael DELVA on Wednesday 30 July 2008, 16:27
J'avais donné dans mon article précédent un lien expliquant comment utiliser plusieurs Expect pour une même fonction.
Il se trouve que j'aurais dû être plus consciencieux dans mon article, puisqu'un test unitaire se devait vraiment d'utiliser cette fonctionnalité très puissante de Moq.
En voici la preuve...
Utiliser des mock objects pour émuler une couche de persistance
By Michael DELVA on Wednesday 30 July 2008, 14:36
Après une semaine de vacances bien méritées, j'ai eu l'idée de cet article, qui va (tenter d')expliquer comment utiliser des objets simulacres (mock objects) pour émuler une couche de persistance aux données lors de tests unitaires.
Ce "tutorial" s'appuie sur du code que je suis en train de développer pour la réalisation d'un programme de gestion de connexions en Remote Desktop, et utilise xUnit et Moq.
C'est un tuto qui est assez "expérimental" pour moi puisque je me considère comme étant en phase d'apprentissage sur les notions de couches, mais aussi en TDD et Mock Objects. J'implore donc votre clémence pour ne pas trop me fustiger. J'ai aussi écrit cet article pour me permettre de mettre en forme mes idées, et aussi et surtout pour avoir des retours, afin d'améliorer la "chose".
Mais trêve de blablas, la suite après le saut...
Wednesday 16 July 2008
Sérialisation XML et namespaces
By Michael DELVA on Wednesday 16 July 2008, 10:37
J'ai posté il y a quelques temps un article sur la sérialisation XML où je donnais une astuce pour enlever les attributs xmlns:xsi et xmlns:xsd ajoutés automatiquement par le serializer. Le souci est que si la racine doit contenir un attribut de votre classe, vous êtes roulés, il va disparaître en même temps.
Heureusement il y a une autre méthode, qui est même la méthode officielle. Il suffit de créer une instance de la classe XmlSerializerNamespaces et de passer cette instance dans la fonction Serialize de XmlSerializer.
Démonstration:
XDocument xDoc = new XDocument();
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add( "", "" );
using (XmlWriter xmlWriter = xDoc.CreateWriter())
xmlSerializer_.Serialize(xmlWriter, objectToSerialize, namespaces);
Et voilà le travail :)
Have fun!
Tuesday 8 July 2008
Présentation d'un framework de Tests: xUnit.net
By Michael DELVA on Tuesday 8 July 2008, 16:09
Ca faisait un petit moment que je n'avais pas blogué un petit article (surtout que je ne fais pas trop de C# en ce moment). Je tente de me rattraper avec un article sur le framework de tests que j'utilise xUnit.net, et qui est une sorte de récapitulatif de ce qu'on trouve sur internet.
Sunday 29 June 2008
Quelques fonctions sur les dates
By Michael DELVA on Sunday 29 June 2008, 23:14
Petites fonctions sur les dates que j'ai utilisé au boulot qui mettent en avant une fonctionnalité nouvelle de C# 3.0: les extension methods. Au menu:
- Connaitre la semaine d'une date
- Récupérer le lundi d'une semaine
- Récupérer le lundi d'une semaine donnée
Tuesday 24 June 2008
Programmation parallèle
By Michael DELVA on Tuesday 24 June 2008, 14:15
Je suis tombé hier soir (pas de mal, merci) sur cette page, qui fait office de présentation des extensions parallèles pour .NET.
Je ne m'étais jusqu'à maintenant jamais vraiment intéressé à ce pan de la programmation, et cette lecture m'a permis de me rendre compte à quel point la programmation parallèle va revêtir une importance de plus en plus cruciale avec l'avènement des processeurs multi-coeurs, et qui n'est pas prêt de s'arrêter.
Je n'en suis qu'au début de mes lectures sur ce sujet, mais je suis déjà impressionné par la facilité avec laquelle il est déjà possible d'intégrer ça dans nos programmes.
De manière très simplifiée, il suffit par exemple de changer ça:
for (int i = 0; i < MAX; i++)
{
//Traitement
}
en
Parallel.For(0, MAX, (int i)=>
{
//Traitement
});
pour que votre boucle soit parallélisée sur tous vos processeurs!
A cela s'ajoutent également de nouvelles collections génériques prenant en charge le traitement multi-processeurs (list, queue, stack...), la fonction Parallel.Invoke qui permet de réaliser des tâches asynchrones, la synchronisation de tâches via les classes Task et Future, et enfin la possibilité d'utiliser les extensions parallèles avec LINQ. Vous l'aurez compris, beaucoup de choses!
En ce qui concerne les liens, il y a déjà l'introduction dont j'ai parlé plus haut, ensuite il y a le blog de la team qui développe ces extensions, la page de download, et une page MSDN.
N'oubliez pas d'aller jeter un oeil dans le répertoire d'installation des extensions, et surtout dans le dossier Samples, avec des exemples très intéressants (dont un RayTracer en LINQ dont vous me direz des nouvelles ;) )
Have fun !
Monday 16 June 2008
Benchmark: Concaténation de chaînes de caractères
By Michael DELVA on Monday 16 June 2008, 20:08
Pour concaténer des chaînes de caractères en C#, il existe 3 méthodes:
- l'opérateur += de la classe String
- la fonction String.Concat
- la classe StringBuilder
Vous avez peut-être déjà lu à plusieurs reprises que, concernant les concaténations de chaînes, il valait mieux utiliser StringBuilder. Personnellement, je n'avais jamais vu de benchmarks tendant à prouver cette affirmation (en même temps, j'ai pas vraiment cherché.)
J'ai donc décidé de faire ça moi-même, et voici les résultats, après le break...
Friday 13 June 2008
Factory, et auto-enregistrement
By Michael DELVA on Friday 13 June 2008, 12:07
J'avais déjà été confronté à ce problème en C++ : comment créer une factory en C# qui ne nous oblige pas à modifier son code pour prendre en compte de nouveaux types? Une réponse parmi d'autres après le break...
Thursday 12 June 2008
Singleton Générique
By Michael DELVA on Thursday 12 June 2008, 15:06
Je ne sais pas pourquoi je n'avais jamais pensé à développer une variante du [Design Pattern] [Singleton]. C'est pourtant un des DP que j'utilise le plus souvent...
Bref, c'est maintenant chose faite, et c'est juste après le break
Monday 2 June 2008
Project Euler #1
By Michael DELVA on Monday 2 June 2008, 23:44
Si vous ne connaissez pas, le [Projet Euler] consiste en une série de problèmes mathématiques à résoudre par programmation. Vous résolvez le problème, vous entrez la solution, et si c'est bon, vous avez accès à un forum où vous pouvez lire les solutions et/ou commentaires trouvées par d'autres personnes, grâce à d'autres (ou pas) langages de programmation. A mon sens, c'est donc intéressant pour 2 points de vue:
- Se creuser la tête sur des problématiques "simples" (j'en suis au début pour l'instant), mais pouvant déboucher sur des solutions poussées
- Comparer sa solution avec ce que les autres ont trouvé, ce qui ne peut-être que bénéfique
Pour ma part, je vais essayer à chaque fois de trouver la solution à chaque problème de 2 manières:
- Une approche brute de fonderie en utilisant les "bases" du C#
- une approche fondée sur l'utilisation de LINQ
Ca permettra ainsi de comparer (là encore) les solutions et de constater que même si LINQ est un très bel outil de requête sur des données, il a malheureusement un coût important qui se manifeste par une plus longue exécution pour trouver le même résultat.
Mais sans plus attendre, voici mes solutions pour le premier problème...
Thursday 15 May 2008
Serialisation XML et LINQ To XML
By Michael DELVA on Thursday 15 May 2008, 17:46
Dans un précédent billet j'expliquais comment sérialiser facilement et à moindre frais en XML avec le framework .NET.
Voici maintenant la même chose, mais en utilisant les objets de LINQ To XML, notamment XElement.
« previous entries - page 2 of 3 - next entries »