IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
logo
Sommaire > Modifications du langage
        Qu'est-ce que le typage implicite des variables locales ?
        Qu'est-ce que les initialiseurs d'objets et de collections ?
        Qu'est-ce qu'un type anonyme ?
        Comment utiliser un type anonyme en valeur de retour d'une méthode ?
        Qu'est-ce qu'une méthode d'extension ?



Qu'est-ce que le typage implicite des variables locales ?
auteur : Jérôme Lambert
C# 3.0 a introduit un nouveau mot clé permettant de déclarer des variables sans pour autant spécifier explicitement leur type. Ce nouveau mot clé est "var".
var i = 12;
Comme vous pouvez le voir, l'utilisation de " var " est très simple : une fois la variable déclarée, il vous faut l'initialiser directement. Le compilateur va inférer automatiquement le type de votre variable.
// i est de type System.Int32
var i = 12;
// j est de type System.Single
var f = 5.2;
// s est de type System.String
var s = " Hello World ";
// list est de type System.Collections.Generic.List<Process>
var list = new System.Collections.Generic.List<Process>();
Chose importante à ne pas confondre, var est différent de " Variant " de VB qui permet de définir le type de votre variable quand vous le désirez. Ici, c'est le compilateur qui va inférer le type de votre variable sur base de l'expression d'initialisation que vous allez spécifier.

Evidemment, tout n'est pas permis et voici les règles à ne pas oublier :

  • Les variables déclarées avec var doivent être initialisées au même moment.
// erreur de compilation
var i = null;
  • var ne peut être utilisé que dans les déclarations de variables locales.
// Erreur de compilation
public class MaClass
{
	var i = 1;
}
  • Il est interdit d'utiliser une variable déclarée avec var dans l'expression d'initialisation.
// Erreur de compilation
var i = i + 1;
  • Il n'est pas possible de déclarer plusieurs variables avec var dans la même instruction.
// Erreur de compilation
var i1 = 1, i2 = 2;
Malgré cette nouveauté du langage, Microsoft recommande d'utiliser var uniquement si cela s'avère nécessaire. Une utilisation trop excessive de var risquerait de complexifier votre code inutilement. Par exemple, les types anonymes sont le cas parfait pour l'utilisation de var.

lien : fr Page MSDN sur les variables locales implicitement typées

Qu'est-ce que les initialiseurs d'objets et de collections ?
auteur : Jérôme Lambert
Initialiseurs d'objets

L'initialiseur d'objet vous permet d'instancier une classe tout en initialisant les champs et propriétés de cette classe que vous désirez, le tout en une seule instruction.
Avec C# 2.0
System.Data.DataTable objDataTable2 = new System.Data.DataTable("maDataTable");
objDataTable2.CaseSensitive = true;
objDataTable2.MinimumCapacity = 100;
Avec C# 3.0
System.Data.DataTable objDataTable = new System.Data.DataTable("maDataTable") { CaseSensitive = true, MinimumCapacity = 100 };
Initialiseurs de collections

L'initialiseur de collection fonctionne sur le même principe mais pour les collections.
Avec C# 2.0
List<DataTable> aDataTables = new List<DataTable>();
aDataTables.Add(new DataTable("myFirstDataTable"));
aDataTables.Add(new DataTable("mySecondDataTable"));
aDataTables.Add(new DataTable("myThirdDataTable"));
Avec C# 3.0
List<DataTable> aDataTables = new List<DataTable>()
			{
				new DataTable("myFirstDataTable"),
				new DataTable("mySecondDataTable"),
				new DataTable("myThirdDataTable")
			};
Vous pouvez même aller plus loin en utilisant l'initialiseur d'objet en même temps que l'initialisation de votre collection :
List<DataTable> aDataTables = new List<DataTable>()
		{
			new DataTable("myFirstDataTable")  { CaseSensitive = true, MinimumCapacity = 10 },
			new DataTable("mySecondDataTable") { CaseSensitive = false, MinimumCapacity = 20 },
			new DataTable("myThirdDataTable")  { CaseSensitive = true, MinimumCapacity = 30 }
		};
lien : fr Page MSDN sur les initialiseurs d'objets et de collections

Qu'est-ce qu'un type anonyme ?
auteur : Jérôme Lambert
Une des grandes nouveautés de C# 3 est la possibilité des créer des types anonymes. Un type anonyme offre la possibilité de composer des types "temporaires" avec une ou plusieurs propriétés en lecture seule. Cette nouveauté s'avère extrêmement efficace avec Linq car on va pouvoir interroger des sources de données et récupérer uniquement les informations dont on a besoin au travers d'objets anonymes.
var anonymousType = new { Name = "Jérôme Lambert", Birthday = new DateTime(1984, 2, 25) };
Console.WriteLine("{0} est  le {1}", anonymousType.Name, anonymousType.Birthday.ToString("D"));
Résultat :

Jérôme Lambert est né le samedi 25 février 1984

Dans l'exemple précédent, on peut voir que j'ai créé un type qui n'existe pas, c'est pourquoi je suis obligé de déclarer ma variable "anonymousType" avec le mot clé "var" qui permet un typage implicite de variable. Pour créer un type anonyme, il suffit d'utiliser le mot clé "new" suivi d'accolades qui contiendront les propriétés. Mon objet anonyme contient deux propriétés que j'ai nommé "Name" et "Birthday". Etant donné que j'ai initalisé mes deux propriétés, le compilateur est capable de déduire le type de ces deux propriétés (System.String pour la propriété "Name" et System.DateTime pour la propriété "Birthday").

Comme je l'ai dit précédemment, l'utilisation des types anonymes s'avère extrêmement intéressante avec les requêtes Linq. Prenons l'exemple de la liste des processus de votre ordinateur : si on désire récupérer les processus commençant par la lettre "A", on peut écrire tout simplement la requête suivante :
var result = from process in System.Diagnostics.Process.GetProcesses()
			 where process.ProcessName.StartsWith("A")
			 select process;

foreach (var currentProcess in result)
	Console.WriteLine("{0} (N°{1})", currentProcess.ProcessName, currentProcess.Id);
Le problème avec cette requête, c'est qu'on est obligé de récupérer une collection d'objets de type System.Diagnostics.Process. Or comme on peut voir dans la boucle foreach, je n'ai besoin que du nom du processus et son ID. C'est là qu'interviennent les types anonymes car je vais pouvoir précifier que chaque résultat doit être représenté par un type qui contient uniquement les valeurs dont j'ai besoin, c'est à dire le nom du processus et son ID.
var result = from process in System.Diagnostics.Process.GetProcesses()
			 where process.ProcessName.StartsWith("A")
			 select new { process.ProcessName, process.Id };

foreach (var currentProcess in result)
	Console.WriteLine("{0} (N°{1})", currentProcess.ProcessName, currentProcess.Id);
Petite remarque et ce n'est pas une faute, je n'ai pas précisé le nom des propriétés dans mon objets anonymes. De ce fait, le compilateur a automatiquement créé des propriétés qui ont le même noms que les propriétés utilisées pour initialiser mon objet anonyme.

lien : fr Page MSDN sur les types anonymes

Comment utiliser un type anonyme en valeur de retour d'une méthode ?
auteur : Jérôme Lambert
Prenons un exemple simple qui va être de récupérer la liste des processus triés par nom.
var query = from proc in System.Diagnostics.Process.GetProcesses()
			orderby proc.ProcessName ascending
			select proc;

foreach (var item in query)
	Console.WriteLine("{0}\t {1}", item.Id, item.ProcessName);
Évidemment, on est forcé de récupérer un objet de type "System.Diagnostics.Process" avec toutes les informations qui l'accompagne alors que dans notre cas, le nom de processus et son id nous auraient suffit. C'est là qu'intervient la notion de type anonyme :
var query = from proc in System.Diagnostics.Process.GetProcesses()
			orderby proc.ProcessName ascending
			select new { proc.Id, proc.ProcessName };

foreach (var item in query)
	Console.WriteLine("{0}\t {1}", item.Id, item.ProcessName);
Comme vous pouvez le voir, un type anonyme est créé en utilisant l'opérateur new suivi d'accolades pour initialiser les différentes propriétés que l'on désire voir apparaître dans son objet anonyme. Dans la boucle foreach, on s'aperçoit que "item" propose bien deux propriétés "Id" et "ProcessName". Chose importante à noter, "item" représente un type anonyme, il n'est donc pas possible de le nommer... Il faut donc passer par l'utilisation du mot clé var qui déduira le type automatiquement.

Pour vous en convaincre que "item" n'est pas de type "System.Diagnostics.Process", voici la preuve en image :

La compilateur a donc effectivement créé un type avec les propriétés adéquates. Le type de ces mêmes propriétés a été au passage inféré grâce à l'initialisation. Bien sûr, rien ne vous empêche de nommer vos noms de propriétés comme vous le désirez :
var query = from proc in System.Diagnostics.Process.GetProcesses()
			orderby proc.ProcessName ascending
			select new { ProcessID = proc.Id, Name = proc.ProcessName };

foreach (var item in query)
	Console.WriteLine("{0}\t {1}", item.ProcessID, item.Name);
Si on jette un petit coup d'oeil au niveau du code compilé avec Reflector (en http://www.red-gate.com/products/reflector/), on remarque qu'une classe générique a été créée automatiquement pour représenter notre type anonyme.

Cette nouveauté de types anonymes n'est donc possible que grâce à un changement du compilateur C#, le CLR (Common Language Runtime) n'a en aucun cas été modifié.

Maintenant que vous avons mis les choses à plat sur ce qu'est un type anonyme, penchons nous sur le sujet de cette question et qui est de savoir comment il est possible de renvoyer un type anonyme à partir d'une méthode.

Comme je l'ai dit tout à l'heure, le type anonyme n'ayant pas de nom, on est obligé de passer par le mot clé var pour manipuler de tels objets. Si on désire encapsuler dans une méthode notre code qui permet de récupérer cette fois-ci le premier processus, cette méthode sera obligée de renvoyer un objet de type "System.Object". Effectivement, peu importe qu'on ait un type anonyme ou pas, tous dérivent de la classe "System.Object".
static System.Object GetProcesses()
{
	return (from proc in System.Diagnostics.Process.GetProcesses()
			orderby proc.ProcessName ascending
			select new { ProcessID = proc.Id, Name = proc.ProcessName }).First();
}
Seulement petit problème, on perd l'intellisense en ayant converti notre type anonyme en "System.Object", donc impossibilité de récupérer les valeurs dans les propriétés "ProcessID" et "Name". Solution que vous allez de suite me proposer : La réflexion ! Et vous avez raison sur le point que ça fonctionnera mais niveau performance, on sera loin... En fait, il y a une solution beaucoup plus simple et c'est en allant voir le code compilé que nous en aurons la preuve !

Tout d'abord, nous avons notre objet anonyme de la méthode "GetProcesses" qui est composé des éléments suivants :

  • Une propriété "ProcessID" de type System.Int32
  • Une propriété "Name" de type System.String
Maintenant, nous allons faire un test en créant exactement le même type anonyme mais en dehors de cette méthode, par exemple dans la méthode "Main" :
var anonymousType = new { ProcessID = 0, Name = "" };
Et si on vérifie le code compilé, on s'aperçoit que le compilateur a détecté que dans notre programme, il y avait deux types anonymes exactement les mêmes (même noms de propriétés, dans le même ordre et avec le même type) : il a donc tout simplement généré une et une seule classe pour ces deux types anonymes identiques.

A partir de cette idée, on peut très facilement implémenter une méthode générique qui prendra en paramètres :

  • L'instance de notre objet anonyme de type "System.Object"
  • Et une autre instance "bidon" de ce même type mais passée de manière générique
Une simple conversion et le tour est joué !
class Program
{
    static void Main(string[] args)
    {
        var result = CastAnonymousObject(GetProcesses(), new { ProcessID = 0, Name = "" });
        Console.WriteLine("{0}\t {1}", result.ProcessID, result.Name);
        Console.Read();
    }

    static System.Object GetProcesses()
    {
        return (from proc in System.Diagnostics.Process.GetProcesses()
                orderby proc.ProcessName ascending
                select new { ProcessID = proc.Id, Name = proc.ProcessName }).First();
    }

    static A CastAnonymousObject<A>(object anomymousObject, A anonymousType)
    {
        return (A)anomymousObject;
    }
}
lien : faq Qu'est-ce qu'un type anonyme ?
lien : Lien vers l'article original

Qu'est-ce qu'une méthode d'extension ?
auteur : Jérôme Lambert
Les méthodes d'extension sont des méthodes statiques qui vous permettent d'étendres les types existants sans avoir besoin de créer un type dérivé ou bien modifier/recompiler le type original. Au final, il n'y a aucune différence entre appeler une méthode d'extension ou une méthode qui existent réellement dans le type concerné. Les méthodes d'extension sont considérées comme des méthodes d'instance de type pour le développeur.

Pour vous montrer un exemple concrêt, nous allons créer une méthode d'extension pour le type System.String qui permettra de convertir un string en System.Int32 si cela s'avère possible, dans le cas contraire nous lancerons une exception de type System.FormatException.
Version classique
class Program
{
	static void Main(string[] args)
	{
		string inputString = Console.ReadLine();

		// Tentative de conversion
		try
		{
			int result = UtilityString.ToInt32(inputString);
			Console.WriteLine("Conversion réussie !");
		}
		catch(System.FormatException)
		{
			Console.WriteLine("Conversion impossible !");
		}

		Console.Read();
	}
}

static class UtilityString
{
	public static System.Int32 ToInt32(string inputString)
	{
		return System.Int32.Parse(inputString);
	}
}
Et non, ce n'est pas encore ça une méthode d'extension. Le code ci-dessous est ce que vous auriez écrit avec C# 2.0. Par contre le code ci-dessous illustre l'utilisation d'une méthode d'extension :
Version avec méthode d'extension
class Program
{
	static void Main(string[] args)
	{
		string inputString = Console.ReadLine();

		// Tentative de conversion
		try
		{
			int result = inputString.ToInt32();
			Console.WriteLine("Conversion réussie !");
		}
		catch (System.FormatException)
		{
			Console.WriteLine("Conversion impossible !");
		}

		Console.Read();
	}
}

static class UtilityStringExtensionMethods
{
	public static System.Int32 ToInt32(this string inputString)
	{
		return System.Int32.Parse(inputString);
	}
}
Comme vous pouvez le remarquer, j'ai pu appeler la méthode "ToInt32" directement sur mon type System.String. Cela est possible grâce au mot clé this suivi du type concerné en paramètre à la méthode ToInt32. Le premier paramètre d'une méthode d'extension représente donc le type qui est concerné et la variable est tout simplement l'instance qui a appelé notre méthode d'extension.

Cependant, les méthodes d'extension sont utilisables aussi avec des paramètres. Par exemple, on pourrait "améliorer" notre méthode d'extension pour qu'elle puisse forcer une conversion, c'est à dire que si une conversion du type System.String vers System.Int32 n'est pas possible, on ne lancera pas d'exception pour renvoyer tout simplement la valeur 0.
Version avec méthode d'extension et paramètre
class Program
{
	static void Main(string[] args)
	{
		string inputString = Console.ReadLine();

		int result = inputString.ToInt32(true);
		Console.WriteLine("La valeur convetie vaut '{0}'", result);

		Console.Read();
	}
}

static class UtilityStringExtensionMethods
{
	public static System.Int32 ToInt32(this string inputString, bool forceConversion)
	{
		int result = 0;
		if (System.Int32.TryParse(inputString, out result))
		{
			return result;
		}
		else
		{
			if (forceConversion)
			{
				return result;
			}
			else
			{
				throw new FormatException();
			}
		}

	}
}
Pour ceux qui ont déjà touché à C# 3.0, vous avez surement déjà vu une série de nouvelles méthodes lorsque vous manipulez des collections impplémentant l'interface System.Collections.Generic.IEnumerable<T>, exemple Where, Select, OrderBy, etc. Ce sont tout simplement des méthodes d'extensions appelées plus particulièrement des opérateurs de requête standard Linq (Pour en savoir plus sur les opérateurs de requêtes : faq Qu'est-ce qu'un opérateur de requête ?).

Pour utiliser les méthodes d'extension, il y a quelques contraintes à respecter :

  • Une méthode d'extension doit obligatoirement se trouver dans une méthode statique
  • Une méthode d'extension doit obligatoirement être statique
  • Une méthode d'extension ne sera jamais appelée si elle a la même signature qu'une méthode existante du type concerné
lien : fr Page MSDN sur les méthodes d'extension
lien : faq Qu'est-ce qu'un opérateur de requête ?


Consultez les autres F.A.Q's


Valid XHTML 1.1!Valid CSS!

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2008 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.