[.NET] CLR 4.0 (Code Contract, MEF, DLR & Parallel Extensions)
Visual Studio 2010 vient de sortir et il est temps pour moi de m’interesser à la CLR 4.0.
La CLR 4.0 n’est pas un simple ajout de fonctionnalités comme le Framework 3.0 ou 3.5 mais bien une nouvelle entité.

La CLR permet de développer avec de nouveaux langages comme IronPython, F#…
Il est aussi possible de faire tourner la CLR 2.0 dans le même processus que la CLR 4.0 contrairement à l’ancienne CLR, on appelle ça le “In Process Side by Side” SxS.
L’interopabilité est améliorée grâce à un générateur P/Invoke (P/Invoke Interop Assistant) disponible sur codeplex ici.
Il en va de même pour le garbage collector qui a lui aussi le droit à une petite rénovation (GC server, GC client en background pour éviter de “freezer” le thread en cours…).
Nouveaux types
La CLR apporte de nouveaux types comme BigInteger, Tuple…
- BigInteger (System.Numeric.BigInteger) vous permet d’utiliser de gros numériques suivant l’espace mémoire disponible.
- Tuple vous permet d’avoir plusieurs valeurs de retour:
private void Form1_Load(object sender, EventArgs e)
{
MessageBox.Show(GetResults(12, "juliend", 12.0).Item1.ToString());
}
public Tuple<int, string, double> GetResults(int arg1, string arg2, double arg3)
{
return new Tuple<int, string, double>(arg1, arg2, arg3);
}
Code Contract
Il est désormais possible de développer avec du “Code Contract”. Cette nouvelle feature vous permet de poser des conditions avant (Requires), pendant (Invariant) et après (Ensures) une méthode.
Si la condition n’est pas respectée, une exception de type ContractException est levée.
Afin de profiter du Code Contract, il vous faut installer le Managed Contract Tools.
Puis direction les propriétés de votre projet pour activer cette fonctionnalité à l’éxécution et à la compilation:
Premier exemple:
Contract.Requires(condition booléenne) permet de poser une pré-condition à notre méthode. Ce qui déclenchera un warning lors de la compilation si elle n’est pas respectée et une erreur à l’éxécution.
public string Echo(string s)
{
Contract.Requires(s != null);
MessageBox.Show(s);
return s;
}
De même pour Contract.Ensures(condition); pour les posts conditions et Contract. Invariant (condition); pour verifier une variable tout au long du déroulement du traitement.
Managed Extensibility Framework
Avant de commencer à vous présenter cette technologie, voici des liens à lire avant de poursuivre sur cet article:
Nous allons voir un cas pratique, conception d’une architecture logicielle d’un projet WPF composite avec un pattern MVVM pour chaque module.
Le but de MEF est de créer une application dite “composite” (un peu comme Prism v2). On va pouvoir ajouter des fonctionnalités à notre logiciel avec le simple ajout de dll dans un dossier spécifique.
Chaque module est indépendant et peux être architecturé de la façon que vous souhaitez (n-tiers, MVVM, MVC…).
Dans notre cas nous allons utiliser MVVM puisque c’est le pattern le plus en vogue pour WPF en ce moment. Vous trouverez ici la source du projet.
- Création de l’interface commune à tous les modules dans une classlibrary
public interface IModule
{
//Nom du module
string Name { get; set; }
}
- Développement de notre premier module
Ajoutez à votre solution un projet WPF, supprimez y le fichier app.xaml et changez les options de compilation:
Puis architecturez votre module grâce à MVVM comme expliqué dans cet article:
Puis appliquez certaines modifications à votre projet pour qu’il devienne un module MEF:
1/ ViewProduct doit implémenter IModule
public partial class ViewProduct : UserControl, IModule
{
public ViewProduct()
{
InitializeComponent();
DataContext = new ViewModelProduct();
}
string IModule.Name
{
get;
set;
}
}
2/ Ajout de la référence System.ComponentModel.Composition à votre projet
3/ Ajout de l’attribut Export et ExportMetaData au dessus de votre View:
[Export(typeof(IModule))]
[ExportMetadata("Name", "Products")]
A ce stade votre class library est configurée pour MEF
- Création du projet principal qui chargera le module
Créez un projet WPF et ajoutez la référence au projet contenant IModule ainsi que System.ComponentModel.Composition.
Xaml:
<StackPanel x:Name="stack"/>
Code Behind:
public Window1()
{
InitializeComponent();
AggregateCatalog catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog("."));
this.modulesContainer = new CompositionContainer(catalog);
this.modulesContainer.ComposeParts(this);
foreach (var module in this.Modules)
{
this.stack.Children.Add(new TextBlock(){ Text = (String)module.Metadata["Name"] });
this.stack.Children.Add(module.GetExportedObject() as UserControl);
}
}
[Import(typeof(IModule))]
public ExportCollection<IModule> Modules { get; set; }
public CompositionContainer modulesContainer { get; set; }
DLR et C# 4
Grâce à la CLR4, nous voyons arriver une nouveauté telle que la DLR et C#4.
Qu’est-ce que la DLR ?
La DLR a été introduite afin de supporter les nouveaux langages dynamiques dans .net (IronPython, IronRuby et maintenant C# et VB.NET).
C’est une surcouche de la CLR, elle permet, entre autres, de remplacer la reflexion.
Le langage C# qui est un langage impératif/statique permet d’être performant, robuste et surtout analysable afin d’avoir des outils tels que l’intelliscence.
Les langages dynamiques tels que PHP ou JavaScript ont la puissance de pouvoir embarquer du code non compilé, par exemple en JavaScript:
var1 = "juliend"; //Type string
alert(var1);
var1 = 2; //On passe en int
alert(var1);
Vous remarquez que l’on peut typer à la volée la variable et que le compilateur ne peut déterminer son type.
Le JavaScript perd donc en sécurité mais on gagne en souplesse.
Les nouveautés du C#4
C#4 permet enfin d’avoir des paramètres par défaut comme en C++ mais aussi l’utilisation de paramètres nommés:
public int Test(int i, int b = 2)
{
return i + b;
}
private void Form1_Load(object sender, EventArgs e)
{
MessageBox.Show(Test(2).ToString());
}
public int Test(int i, int b = 2)
{
return i + b;
}
private void Form1_Load(object sender, EventArgs e)
{
MessageBox.Show(Test(b:3, i:7).ToString());
}
Avant C# 4, l’utilisation de la réfléxion était fréquente. Par exemple si nous avons une variable dont on ne connait pas son type mais que nous savons qu’il existe la méthode HelloWord alors nous l’utilisions comme ceci:
class Class1
{
public void HelloWord(string t)
{
MessageBox.Show("Hello " + t);
}
}
public object GetObject()
{
return new Class1();
}
private void Form1_Load(object sender, EventArgs e)
{
object monobjet = this.GetObject();
Type montype = monobjet.GetType();
montype.InvokeMember("HelloWord",
BindingFlags.Default |
BindingFlags.Public | BindingFlags.Public |
BindingFlags.Instance | BindingFlags.InvokeMethod,
null, monobjet,
new object[] { "Test" });
}
Il est désormais possible d’utiliser la DLR pour remplacer la réfléxion, voici comment faire (toujours avec Class1):
dynamic monobjet = this.GetObject();
monobjet.HelloWord("test");
La méthode HelloWord ne possède pas d’intelliscence car c’est à l’éxécution qu’il va générer de la réfléxion (compilation à la volée).
Attention à ne pas abuser de ce type (comme en PHP par exemple) pour ne pas se retrouver à faire:
dynamic monobjet = "lol";
monobjet = 2;
MessageBox.Show(monobjet.ToString());
Parallel Extensions (Task Parallel Library, Parallel Linq, Parallel Debugger tool)
La programmation parallèle s’impose de plus en plus puisque nos CPU sont souvent double coeur voir +.
La programmation parrallèle c’est quoi ? C’est la possibilité d’exploiter ces coeurs dans le code de tous les jours.
Cependant, cela implique d’utiliser des deadlocks, des mutex etc… et cela devient vite compliqué.
Parallel Extensions anciennement appelé ParallelFX va nous permettre de faciliter tous ces mécanismes.
Task Parallel Library (TPL)
Prenons comme exemple une boucle qui parcoure des objets (de façon ridicule, le but étant que le temps d’éxécution soit lent):
Stopwatch watch = Stopwatch.StartNew();
foreach (Utilisateur item in mesUsers)
{
foreach (Role r in mesRoles)
if (r.ID.Equals(item.Role.ID))
{
for (i = 0; i < 1000; i++)
{
b++;
}
}
}
watch.Stop();
MessageBox.Show(watch.ElapsedMilliseconds.ToString());
Nous allons maintenant utiliser la programmation parallèle afin d’accèlerer notre code. Pour cela, le framework nous met à disposition la classe Parallel contenant la méthode statique ForEach.
L’un des problèmes de ce code est que la variable b risque de ne pas contenir la bonne valeur (plusieurs threads incrémentant en même temps par exemple).
Pour cela on contourne le problème avec la classe InterLocked qui incrémentera notre variable de façon atomique.
watch = Stopwatch.StartNew();
//Syntaxe: Parallel.ForEach<Type à parcourir>
//Puis n=> est une expression lambda
Parallel.ForEach<Utilisateur>(mesUsers, item =>
{
Parallel.ForEach<Role>(mesRoles, r =>
{
if (r.ID.Equals(item.Role.ID))
{
Parallel.For(0, 1000, z =>
{
Interlocked.Increment(ref b);
});
}
});
});
watch.Stop();

Notre boucle est désormais réalisée en programmation parallèle. Vous remarquez cependant que l’on perd du temps plutôt qu’en gagner !! En effet, les parallèles extensions mettent en place beaucoup de chose pour réaliser votre boucle, chose qui n’est pas intéressante pour aussi peu de données.
Le gain de temps aurait pu s’effectuer si j’avais eu plus de traitement à faire dans mes boucles.
Parallel Linq (PLINQ)
Il existe la possibilité de paralléliser les requêtes Linq-To-Xml et Linq-To-Object.
Par exemple:
Stopwatch watch = Stopwatch.StartNew();
List<Utilisateur> mesUsers2 = (from u in mesUsers
orderby u.Login ascending
select u).ToList();
watch.Stop();
MessageBox.Show(watch.ElapsedMilliseconds.ToString());
watch = Stopwatch.StartNew();
mesUsers2 = (from u in mesUsers.AsParallel()
orderby u.Login ascending
select u).ToList();
watch.Stop();
MessageBox.Show(watch.ElapsedMilliseconds.ToString());
Le code effectue un tri sur une liste, l’un en Linq et l’autre en PLinq grâce à .AsParallel().
Vous pouvez constatez que dans les deux cas, tous les processeurs sont utilisés sauf que PLinq utilisera de façon optimale vos processeurs.
Plus vos listes sont grandes, plus l’utilisation de PLinq s’impose:
Avec PLinq
Sans PLinq