IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Windows Phone 7.


précédentsommairesuivant

I. Définition, plan du cours

L'objet de ce cours est de créer une application pour un téléphone portable Windows Phone 7 et 7.1.
Il n'y a pas de prérequis.
Ce cours est en français !
J'explique les choses à ma manière.
Comme d'habitude ce cours sera complété avec l'expérience de l'auteur et les compléments ou corrections proposés par les lecteurs.
L'auteur ne répondra pas aux questions spécifiques ; il y a les forums pour cela mais il pourra rajouter des compléments au cours.
Merci de me signaler les erreurs, fautes d'orthographe et merci de m'adresser vos critiques.

I-A. Définitions XAML, Silverlight, Wpf, C#

Image non disponible

Dans Windows Phone 7, on peut utiliser deux manières de dessiner l'interface utilisateur:
- avec Silverlight qui utilise un moteur de rendu vectoriel et des accélérations matériels pour afficher. Cela permet d'afficher de la 2D, de la 3D, des animations, des documents, des dessins vectoriels ou Bitmap, de l'audio, de la vidéo ;
-avec XNA pour faire des jeux (non étudié dans ce cours).

L'interface Silverlight est écrite en code XAML.

Que signifie XAML ?

XAML= "eXtensible Application Markup Language", en français: "langage à balises d'applications extensibles" est un langage utilisant la syntaxe XML et permettant de définir des interfaces d'applications. En d'autres termes, quand vous dessinez une interface utilisateur en Silverlight (ou en WPF), elle est enregistrée en XAML.

Image non disponible

Silverlight est une variante allégée de WPF (Windows Présentation Foundation est utilisé sur les ordinateurs). Ce qui fait que Silverlight est portable et utilisé par les navigateurs Web et Windows Phone.

Par contre le code d'exécution du programme est soit en C#, soit en Visual Basic.

Le cours actuel utilise C#.

Pour développer une application Windows Phone 7, il faut Visual Studio version pro (payante) qui permet de développer en C# ou en Visual Basic.

On peut aussi développer sur Visual Studio 2010 Express (gratuit) mais uniquement en C# actuellement.

Le présent cours utilise Visual Studio 2010 for Windows Phone Express (C#) gratuit et téléchargeable ici: http://msdn.microsoft.com/fr-fr/express/aa975050 et Windows Phone 7.1 (Mango) la seconde version de l'OS de WindowsPhone.

I-B. Documentation

Où trouvez de la documentation ?

Documentation officielle en anglais sur MSDN :http://msdn.microsoft.com/fr-fr/library/ff402535%28v=vs.92%29.aspx

Silverlight en anglais (attention, c'est Silverlight 4, Silverlight pour Windows Phone est légèrement différent) :Silverlight

(http://msdn.microsoft.com/en-us/library/cc838158%28v=VS.95%29.aspx)

Windows Phone (Microsoft) en anglais : Windows Phone

(http://msdn.microsoft.com/en-us/library/ff402535%28v=VS.92%29.aspx)

On peut charger Windows Phone Documentation en anglais, sous forme d'un fichier d'aide chm : Windows Phone Documentation (fichier CHM en anglais)

Pour débuter Quick Start sur App Hub en anglais :http://create.msdn.com/en-us/education/quickstarts

App Hub :App Hub

Guideline pour l'aspect des applications en anglais :
http://msdn.microsoft.com/en-us/library/hh202915%28v=VS.92%29.aspx

Exemple de code expliqué en anglais :http://msdn.microsoft.com/en-us/library/ff431744.aspx

Un livre sur Windows Phone en pdf en anglais :http://www.charlespetzold.com/phone/index.html

31 days of Windows Phone 7 :http://www.jeffblankenburg.com/2010/09/30/31-days-of-windows-phone-7/

31 days of Mango :http://jeffblankenburg.com/31daysofmango/

Le recent Dev Center en anglais : http://msdn.microsoft.com/library/windowsphone/develop

Windows Phone sur MSDN ; site en français donnant accès au « Coach Windows Phone », à un tutoriel pour publier, à des toolskits :http://msdn.microsoft.com/fr-fr/windowsphone/default

Les cours sur développez.com en français : https://windowsphone.developpez.com/cours/

Le blog MSDN de Jean-Francois Lavidalle's blog ; une multitude de tutoriels en français(Louis-Guillaume Morand y participe) ; fait partie de MSDN :
http://blogs.msdn.com/b/jflavidalle/archive/2011/09/06/developpement-windows-phone-partie-1.aspx

I-C. Ce qu'il faut comprendre, plan du cours

Avant tout, il faut connaitre le langage C# et le Framework.Net pour écrire le code d'exécution. Le XAML sera utilisé pour écrire du Silverlight qui décrit l'interface utilisateur.

Dans ce cours vous découvrirez :

  • l'IDE de Visual Studio Express ;
  • un micro cours de C# ;
  • un chapitre sur XAML ;
  • les grands principes Silverlight ;
  • les différents contrôles ;
  • les grandes fonctions (IsolatedStorage,Tombstone) ;
  • comment faire: SplashScreen ;
  • compilation distribution.

On insiste sur le fait qu'en Windows Phone il y a séparation de la présentation et de la logique.
Cela signifie que les concepteurs peuvent travailler sur l'apparence d'une application et le comportement de l'apparence en utilisant uniquement XAML pendant que les développeurs travaillent sur la programmation logique à l'aide de C#.

On verra que le comportement de la présentation (disposition des contrôles, affichage de données) est totalement pris en compte par la présentation.

On peut aussi utiliser Expression Blend, un logiciel très puissant sur le plan graphique, pour dessiner l'interface. Pour le moment, ce cours ne l'utilise pas, bien qu'il soit exécuté très simplement à partir de Visual Studio.

II. Visual C# Express= l'IDE

C'est l'Integrated Development Environment (IDE) : environnement de développement intégré de Visual Studio Express 2010 for Windows Phone de Microsoft.
Il permet de dessiner l'interface (les fenêtres, les boutons, les listes, les images…) et d'écrire le code C#.
Chez nous, on peut aussi dire EDI (Environnement de Développement Intégré).

Image non disponible


Lancez le logiciel, vous voyez la page d'ouverture :

Image non disponible

Cliquez sur « Nouveau Projet » en haut à gauche, cela ouvre la fenêtre suivante :

Image non disponible

Choisissez à gauche Silverlight pour Windows Phone, au milieu Application Windows Phone Visual C# donnez un nom (évitez de donner comme nom un mot clé du genre « char » ou « string ») ; en bas, puis cliquez sur « Ok ».

Ensuite vous pouvez choisir la version de Windows Phone 7.0 ou 7.1 (Mango) en déroulant la liste :

Image non disponible

Gardez la version 7.1.

L'espace de travail s'ouvre :

Image non disponible

On est dans l'onglet MainPage.xaml. Il y a le designer à gauche qui permet de dessiner l'interface que verra l'utilisateur. Le designer génère le code XAML correspondant, on le voit à droite, il décrit l'interface en XAML. On peut aussi écrire directement du XAML à droite, ce qui modifie l'interface dans le designer.

Le logiciel a créée une page vide. Nous pouvons ajouter un bouton par exemple, pour cela on ouvre la boite à outils à gauche :

Image non disponible

On clique sur le bouton dans les outils puis sur la page dans le designer, on appuie (bouton gauche de la souris), on déplace puis on lâche (Drag and Drop). Le bouton apparaît.

Image non disponible

On note que le code XAML correspondant au bouton a été ajouté dans la fenêtre de droite.

En bas à droite, on a la fenêtre de propriétés du bouton. Les contrôles font partie de l'espace de noms System.Windows.Controls.

Image non disponible

On remarque qu'il n'y a pas de propriété Text pour le texte du bouton, mais une propriété « Content ». On y reviendra.

Dans cette fenêtre des propriétés, on peut modifier rapidement, le nom du bouton, les propriétés de ce bouton : Content, Fontsize, Background…

À droite du nom des propriétés, dans la première colonne, figure un marqueur de propriété. Ce marqueur de propriété indique s'il y a une liaison de données ou une ressource appliquée à la propriété. Lorsque vous cliquez sur le marqueur de propriété, vous pouvez ouvrir le générateur de liaisons de données ou le sélecteur de ressource.

Une autre manière, moins rapide, de modifier les propriétés de ce bouton est de taper du code XAML dans la fenêtre de droite :

 
Sélectionnez
 <Button Content="Start" Height="68" HorizontalAlignment="Left" Margin="160,176,0,0" Name="button1" VerticalAlignment="Top"
  Width="226" Click="bouton_click" />

Il faut savoir parler l'XAML !


Si on double-clique sur un bouton, par exemple, on se retrouve dans la fonction évènement correspondante (ici l'évènement "button1_Click"), celle qui contient du code C# qui sera exécuté quand l'utilisateur de l'application clique sur le bouton.

Image non disponible

La fonction évènement et le code C# sont dans l'onglet MainPage.xaml.cs.



Souvent on a besoin de gérer d'autres évènements :
quand l'utilisateur frappe une touche cela déclenche les évènements KeyDown puis KeyUp (pas de keyPress !)

Pour ajouter la gestion de l'évènement KeyUp, cliquer sur le bouton. Dans la fenêtre des propriétés cliquer sur l'onglet « évènement ».

Image non disponible

puis dans la liste double-cliquez sur « KeyUp » vous voyez apparaitre :

 
Sélectionnez
 <TextBox  Name="textBox1" Text="TextBox1"  KeyUp="textBox1_KeyUp" />

Et dans le code C# :

 
Sélectionnez
  private void textBox1_KeyUp(object sender, KeyEventArgs e)
        {
        
             }

On note que la fonction a un paramètre nommé sender (c'est l'objet déclencheur) et un paramètre e de type KeyEventArgs (arguments, paramètres de l'évènement).

Quand on a le curseur sur un objet visuel (comme un bouton), si on clique droit, on a un menu permettant d'aller dans le code XAML du bouton (le button dans le code XAML est sur fond gris) ou dans le code C# :

Image non disponible

On remarque en haut à droite la fenêtre Explorateur de solutions qui contient les différents éléments du projet :

Image non disponible

On voit :
-les propriétés du projet ;
-les références (DLL du projet) ;
-le code XAML de l'application : App.xaml ;
-les icônes et images du projet :Application Icon.png, Background.png, SplashScreenImage.jpg ;
-la page (MainPage ici) ou les pages composées de l'interface (MainPage.xaml) et du code C# (MainPage.xaml.cs).

En bas il y a la liste des erreurs qui sont dans le code et la ligne où se trouve l'erreur (s'il y en a !) :

Image non disponible

Dans les fenêtres, il y a différentes icônes permettant de modifier la taille du texte du design ou des volets :

Image non disponible

On peut cliquer sur le titre de l'onglet d'une fenêtre et en maintenant enfoncé le bouton gauche de la souris, déplacer la fenêtre :

Image non disponible

Quand on déplace, une croix devient visible ; il suffit de lâcher le curseur par exemple sur le haut de la croix pour que la fenêtre soit ancrée en haut.

Par le menu Projet, puis Propriétés de on affiche les propriétés du projet :

Image non disponible

Pour déboguer, il faut choisir l'émulateur en haut au milieu :

Image non disponible


si on clique sur le bouton d'exécution (ou F5) on compile l'application, on charge l'émulateur WP7 et on exécute l'application dans l'émulateur.

Image non disponible

La souris permet de simuler le doigt sur l'écran. Il est en 16 bits par point soit 65635 couleurs.


À sa gauche il y a une barre d'outils qui apparait quand le curseur de la souris s'approche.
Les boutons de haut en bas :

  • fermer l'émulateur.
  • réduire l'émulateur.
  • faire pivoter l'émulateur pour passer en mode paysage.
  • faire pivoter mais dans l'autre sens.
  • ajuster l'émulateur à la taille de l'écran.
  • définir le zoom de l'émulateur.
  • autres.
  • accéléromètre.
  • localisation.
  • copie d'écran.
Image non disponible

Sur la droite de l'écran, il y a des compteurs (voir le chapitre sur la compilation et les tests).


Dans le constructeur de la classe App de votre application (App.xaml.cs), vous pouvez faire disparaitre ces compteurs.

 
Sélectionnez
Application.Current.Host.Settings.EnableFrameRateCounter = false;

On peut mettre dans le code un point d'arrêt en double-cliquant dans la marge à gauche de la ligne ,un rond apparait :
Si on exécute l'application, elle s'arrêtera si elle rencontre un point d'arrêt et surlignera la ligne en jaune.

Image non disponible

Si on met le curseur sur une variable ou un objet du code C#, sa valeur s'affiche dans un petit rectangle (il faut parfois dérouler les détails en cliquant sur les « + »).

On peut ensuite faire exécuter le programme pas à pas (F11) sans sauter dans les routines (F10), on peut même remonter (Maj+F11) :

Image non disponible

On voit qu'on peut aussi ouvrir des fenêtres :

Liste des variables locales et leurs valeurs :

Image non disponible

On peut ouvrir une fenêtre Espion, saisir le nom d'une variable ou d'un objet et voir sa valeur.

Image non disponible

On peut poursuivre l'exécution par F5, arrêter complètement le débogage par Maj-F5.

Dans Visual Studio 2010, pour modifier l'interface, on peut ouvrir le projet dans Expression Blend 4. C'est un logiciel permettant de dessiner des interfaces complexes et très élaborées. Il faut pour cela passer par le menu « Afficher » puis « Ouvrir dans Expression Blend..».

Image non disponible

Où se trouvent les sources des applications ?
Pour mon application qui se nomme CalculTout : dans « C:\Users\Philippe\Documents\Visual Studio 2010\Projects\CalculTout ».
Il y a les fichiers CalculTout.sln et CalculTout.suo.

Image non disponible

Il y a dessous un répertoire nommé aussi CalculTout qui contient les fichiers « .xaml » et « .xaml.cs » et les répertoires Bin et Obj qui contiennent chacun les répertoires Debug et Release.


Conclusion : le XAML permet de décrire l'apparence de l'application, tandis que les évènements sont gérés par un langage .NET classique comme le C# (étudié ici) ou le Visual Basic.

III. Le C# et Framework.NET

Image non disponible

Le C# (prononcer « si sharp ») est un langage de programmation orienté objet et à typage fort, créé par la société Microsoft (Anders Hejlsberg). On l'associe à une boîte à outils que l'on appelle le Framework .NET (prononcez "dotte nette") qui offre une multitude de possibilités : classe d'entiers, de chaîne de caractères… classe d'objets visuels (bouton, textbox, image…), appel à une base de données…

Pourquoi le # (dièse) ? parce que C# est une note au dessus de C !

III-A. Premier programme

Ouvrir l'IDE, créer une nouvelle application (voir chapitre précédent).

Où mettre le code C# à tester ?
Ouvrir MainPage.xaml.cs et placer votre code à tester après « public MainPage() » (c'est le constructeur de la page) sous InitializeComponent. Il sera exécuté dès le démarrage de la page.

 
Sélectionnez
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;

namespace MonApplication
{
    public partial class MainPage : PhoneApplicationPage
    {
        // Constructeur
        public MainPage()
        {
            InitializeComponent();

            //Mettre votre code ici.
            //Il sera exécuté au lancement de l'application
        }
    }
}


On veut afficher « Bonjour ».(Pas très original !)

Voici le code qui fait cela :

 
Sélectionnez
string texte;      //On déclare (on crée) une variable nommée texte
texte= "Bonjour";  //On affecte un texte à la variable texte (on met "Bonjour" dans texte)
MessageBox.Show(texte); //On affiche le texte dans une messageBox


Taper sur F5.
Cela donne :

Image non disponible

Une instruction est une commande élémentaire lue et exécutée par un processeur.

On remarque dans le code C# :
L'exécution se fait séquentiellement, instruction par instruction de haut en bas.
Chaque instruction se termine par « ; ».
Il y a distinction entre majuscules et minuscules.
Les commentaires (parties non exécutées) commencent par //.
On peut aussi utiliser /* commentaires */ qui permet d'écrire des commentaires sur plusieurs lignes.


On peut mettre plusieurs instructions sur une même ligne (séparées par des « ; »).
On peut aussi écrire une instruction sur plusieurs lignes (sans rien ajouter).
Un bloc d'instructions est placé entre accolades {} et peut contenir des blocs imbriqués.

 
Sélectionnez
/* Ceci est encore 
1 commentaire sur 2 lignes */
{
int i = 3;                     //1 instruction sur une ligne
string nom;  nom= "Philippe";  //2 instructions sur cette ligne
string prenom = "Marie"+        //1 instruction sur 2 lignes
"Chantale";
}

Le code se compose de :
Mots clés C# : for, if = …
d'objets et Classes faisant partie du Framework .NET et de leurs propriétés et méthodes : MessageBox, Math.Pow, String…

On verra aussi que les mots clés C# commencent par une minuscule (if , for…).
Par contre les noms de classes ou propriétés du Framework .NET commencent par une majuscule (Math.Pow, String.Equals…)

Ici on a affiché un texte dans une MessageBox ; on peut aussi afficher un texte dans un contrôle TextBlock ou un contrôle TextBox.
Il faut ajouter un TextBlock par exemple dans l'interface, ensuite pour afficher dedans :

 
Sélectionnez
 textBlock1.Text = "Bonjour";

Pour faire des tests en mode Debug, on peut aussi afficher sur la sortie (fenêtre de l'IDE ouverte par CTR+W, O) en écrivant :

 
Sélectionnez
System.Diagnostics.Debug.WriteLine("Bonjour");

On doit être en mode Debug et non Release.

III-B. Objet, Classe, Espace de noms.

On utilise des objets dans les programmes. Des objets visuels comme les boutons, TextBox… ou des objets comme les collections, des objets mathématiques…

Il existe des types d'objet qui définissent les caractéristiques communes des objets. Ces types se nomment les Classes (il y a d'autres types…).

Exemple :
le bouton qui est sur mon écran fait partie de la classe Button. Ainsi il a toutes les caractéristiques de la classe Button.
On rappelle que se sont ces classes que l'on utilise pour instancier (créer) un objet, une instance.
En C# :

 
Sélectionnez
Button MyButton  = new Button();

La ligne déclare un bouton nommé MyButton.

On remarque que :

MyButton est un objet une variable par référence : dans la mémoire de l'ordinateur, à l'endroit de la variable il y a l'adresse où est stockée la valeur de la variable.
Il faut déclarer une référence puis instancier l'objet avec new :

 
Sélectionnez
// en deux lignes 
Button MyButton;           //on déclare, myButton existe mais ne contient rien
MyButton  = new Button(); //on instancie

//ou

// en une ligne
Button MyButton  = new Button();

On le redit : on déclare une référence (avec la première ligne) puis on instancie l'objet avec new (seconde ligne). Parfois on le fait dans deux endroits différents.


Par opposition un int (un type structure numérique entier), permet de créer une variable par valeur, (dans la mémoire de l'ordinateur, à l'endroit où est stockée la variable, il y a directement la valeur de la variable) pas besoin du mot new pour instancier.

On a à notre disposition de multiples classes toutes faites (Button, ListBox, PhoneApplicationPage, int, double…) pour créer des applications. En plus on peut créer de toutes pièces une classe dont on a besoin.


Libération de mémoire : l'objet est libérable quand il cesse d'exister (quand on sort de la fonction où on l'a déclaré et qu'il n'existe plus aucune référence vers lui dans un autre endroit).
Il peut aussi être libérable en lui affectant la valeur null. Mais il existe toujours.
En pratique, on ne s'occupe pas de libération de la mémoire qui s'exécute automatiquement.
Le ramasse miettes (Garbage Collector) libérera ensuite réellement la mémoire quand il en aura besoin.

Un objet a des propriétés. La syntaxe est NomObjet.NomPropriété :

 
Sélectionnez
MyButton.Content = "Bonjour";

Ici on a utilisé la propriété Content du bouton.

Un objet a aussi des méthodes :

 
Sélectionnez
MyButton.Focus()

Cette ligne déclenche une méthode qui donne le focus au bouton.(c'est le bouton qui devient actif).


Une méthode peut avoir un ou des paramètres :

 
Sélectionnez
MessageBox.Show("texte");

Ici la méthode Show a un paramètre ; c'est le texte qui sera affiché dans la MessageBox.

Pour une méthode qui a des paramètres, il peut y avoir surcharge : on peut avoir une même méthode (même nom) avec le nombre ou le type de paramètres différent :
Exemple : la méthodes .Show de MessageBox qui affiche une boite de message accepte 2 signatures différentes ; il y a surcharge de la méthode :

 
Sélectionnez
MessageBox.Show("texte");
MessageBox.Show("texte", "titre", MessageBoxButton.OK);

Dans une classe il peut y avoir des fonctions :

 
Sélectionnez
 public void Affiche()
        {
        }


Un objet peut avoir des évènements.
Pour un bouton par exemple, quand l'utilisateur de l'application clique sur le celui-ci, l'évènement Button.Click se produit.
L'application devra gérer les évènements qui se produisent.

Les classes sont regroupées en bibliothèques sous la dénomination d'Espace de noms (NameSpace). Un framework est un ensemble d'espace de noms et de classes.
On a à notre disposition le Framework.NET qui contient des milliers de classes.

Les espaces de noms sont le moyen de regrouper logiquement des classes afin de mieux s'y retrouver. Cela permet aussi d'utiliser des classes ayant le même nom mais venant d'espaces de noms différents.

Pour pouvoir utiliser une classe il faut inclure l'espace de noms auquel appartient cet objet.
En C#, on le fait grâce à using qui sera placé en haut du code, avant toutes autres instructions :

 
Sélectionnez
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;

Ici, en haut du code, on ajoute les espaces de noms donnant accès aux variables (System), aux contrôles Silverlight (System.Windows.Controls en bas).

using permet d'inclure un espace de noms qui permet d'avoir accès à des classes. Ces classes permettront d'instancier (de créer) des objets.

Dans le code XAML on peut aussi inclure des espaces de noms grâce à xmlns (xml name space) :

 
Sélectionnez
 xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"

Enfin pour utiliser un espace de noms, il faut que l'assembly (la DLL ou librairie dynamique sous forme d'un fichier .dll) contenant cet espace de noms soit incluse dans les références du projet.
Pour utiliser les contrôles (Button, ListBox… qui sont dans l'espace de noms System.Windows.Controls) la référence « System.Window » doit donc être dans les références du projet (ce qui est le cas par défaut) ; voir les références dans la fenêtre de l'explorateur de solution.

On peut ajouter une référence (un assembly sous forme d'une DLL) : pour cela, clic droit sur Références dans l'explorateur de solution puis sur Ajouter une référence.

Image non disponible

Les classes sont regroupées "physiquement" par assembly (DLL) et "logiquement" par namespace (les namespaces et assemblies peuvent se recouper : des classes d'un même namespace peuvent se retrouver dans différents assemblies, et des classes d'un même assembly dans différents namespaces).

On verra qu'une page d'une application Windows Phone est une Classe. Voici les premières lignes de la page d'une application.

 
Sélectionnez
namespace CalculeTout
{
    public partial class MainPage : PhoneApplicationPage
    {

Il y a un espace de noms (qui a le nom de l'application) et une classe partielle (qui a le nom de la page).

Au passage, une classe peut être découpée et composée à différents endroits de plusieurs classes partielles.

Notion de propriété d'instance et de propriété static.

Une propriété d'un objet peut être accessible sur un instance : un objet qui a été crée à partir de la classe ; on appelle cela un membre d'instance.

 
Sélectionnez
string myString= "qsd"; // on déclare une instance de la classe string nommée 'myString'.
myString= myString.Trim(" "); //on utilise .Trim sur l'instance myString

La propriété est celle de l'objet et non du type.
La syntaxe est NomDeLinstance.NomPropriété.

Par contre certaines propriétés ou méthodes sont des propriétés ou des méthodes de la classe, elles sont static :

 
Sélectionnez
bool c= String.Compare (a, b); // on utilise .Compare de la classe String

La propriété est celle du type String et non de l'objet.
La syntaxe est NomDeLaClasse.NomPropriété.


De la même manière il y a des classes static, des méthodes static.

Les classes statiques et les membres de classe sont utilisés pour créer des données et des fonctions auxquelles il est possible d'accéder sans créer d'instance de la classe.

Autre exemple : Si je veux créer un compteur d'instance, je vais ajouter une propriété static qui va s'incrémenter pour chaque instanciation.

Héritage : une classe peut hériter d'UNE autre classe.
La classe héritée possède tous les attributs de la classe mère (ou classe de base) dont elle hérite :

Par exemple, la classe Button hérite successivement des classes :
Object
DependencyObject
UIElement
FrameworkElement
TypesBases
Control

Quand on crée une classe, on peut indiquer de quelle classe elle hérite : par exemple on a dans le code C# une page nommée MainPage, elle hérite de la classe PhoneApplicationPage, elle a donc tous les attributs de la classe de base PhoneApplicationPage qui est une classe du framework. On note bien la syntaxe : après « Class MainPage », « : PhoneApplicationPage » indique la classe mère.

 
Sélectionnez
public partial class MainPage : PhoneApplicationPage

Dans la classe dérivée qu'on a créée, on peut ajouter des champs et des méthodes, on peut aussi appeler les méthodes de la classe de base.

On peut aussi redéfinir une méthode qui remplacera celle de la classe de base (à condition qu'elle soit virtuelle).

Interface :

Il y a deux notions :
- l'interface au sens général du terme, indique "ce que le type expose publiquement", ce que je vois de l'objet, c'est son interface (le nom des propriétés, méthodes…) ; exemple : la méthode Clear fait partie de l'interface d'une ListBox. Par contre le code qui effectue la méthode (celui qui efface physiquement toutes les lignes de la ListBox), ce code se nomme implémentation, lui n'est ni visible ni accessible pour les classes du framework.
- l'interface au sens spécifique de .NET : elle définit un contrat à implémenter (relation "peut faire" ou "a un").

Le nom d'une interface commence par « I ». Ainsi l'interface IEnumerable. Une classe, List générique par exemple, implémente les interfaces ICollection générique (définit des méthodes pour manipuler des collections génériques) et IList (méthodes pour les collections d'objets accessibles séparément par index).

Quand vous créerez une classe, vous écrirez vous même l'implémentation et vous pourrez ajouter à votre classe une interface.

Visibilité :

Quand un objet est créé, il est visible et utilisable, uniquement dans certaines parties de l'application.
Par exemple je peux voir et modifier la couleur d'un bouton créé dans le code XAML uniquement dans le code de la page où il est situé ; dans une autre page il n'est pas accessible.
Pour les variables on parle de portée : la variable peut être locale (private) ou de portée générale ('public') visible partout.

On utilise souvent le mot clé this qui désigne l'objet sur lequel opère la méthode, l'objet où on est. Le mot clé this fait référence à l'instance actuelle de la classe.

 
Sélectionnez
public Employee(string name, string alias) 
{
    this.name = name;
    this.alias = alias;
}

Une fonction est une suite d'instructions regroupées sous un nom qui accepte des paramètres et qui retourne un résultat. Pour l'utiliser on l'appelle par son nom en ajoutant les paramètres, elle retourne le résultat.
On ajoute void si elle ne retourne pas de résultats. On devrait parler de procédure dans ce cas.
En programmation objet on utilise le terme général de méthode.
Voici une fonction qui a deux paramètres (x et y) et qui retourne un int (entier) :

 
Sélectionnez
int Multiplier(int x, int y)
{
    return x*y;
}

III-C. Variable, déclaration, initialisation, affectation, constante

Variable :

C'est le même sens qu'en mathématique.

Une variable sert à stocker un nombre, du texte (chaîne de caractères), une date, un objet…

Une variable a un nom et un type qui indique ce que l'on peut y mettre. Elle a aussi une portée : où et quand sera-t-elle visible et utilisable…?

Le nom d'une variable ne doit contenir que des caractères alphanumériques (abc […] ABC123 […] 0éè…) et « _ », il doit commencer par une lettre et le nom ne doit pas être le nom d'un type, d'une classe, ou d'un mot-clé du langage.

C# différencie majuscules et minuscules, il accepte les caractères accentués.

état, _item, Pos1 sont acceptés
1pos, etat-1, string sont refusés.

Nommage :
La camel casing correspond aux majuscules en chameau : la première lettre de chaque mot est en majuscule.

Pour les variables locales et paramètres de méthode, on utilise lowerCamelCase (première lettre en minuscule, puis première lettre de chaque mot en majuscule).
Exemple : nombreDePoint.

Pour les noms de types et leurs membres publics, on utilise PascalCase (ou UpperCamelCase, chaque "mot" commence par une majuscule).
Exemple : NombreDePoint.

Pour les membres privés, il n'y a pas de convention, mais on utilise habituellement la suivante :
- méthodes et propriétés : PascalCase, comme pour les membres publics.
- variables de classe (champs): lowerCamelCase, comme pour les variables locales, éventuellement précédées d'un préfixe comme "_".
Exemple : _Nom .

On peut aussi utiliser la notation hongroise : on ajoute une lettre, un préfixe indiquant le type :
Exemple : iIndex, sNom. Son utilisation est maintenant déconseillée.

On nomme souvent les variables booléennes (contenant true ou false) avec un nom commençant par « Is ».
Exemple : IsOpen.


Type de variable :
Les variables peuvent être de type numérique, chaîne de caractères, objet…

Exemple.
Si myString est une variable de type chaîne de caractères (ou String), je peux y mettre une chaîne de caractères (« TOTO » par exemple).

Image non disponible

Déclaration :

Avant d'utiliser une variable, il faut la déclarer, la créer et lui allouer un espace de stockage, on dit aussi instancier = créer une instance.
La syntaxe est : type nom;

Déclarer une variable de type int (numérique de type entier) et nommée number :

 
Sélectionnez
int number;

Ainsi la variable number a été créée, elle ne peut contenir qu'un entier.

On peut déclarer plusieurs variables en même temps :

 
Sélectionnez
double number1, number2, number3, result;

Ici on a déclaré quatre variables de type double.


Initialisation :
Il faut donner initialement une valeur à la variable qui vient d'être créée :

 
Sélectionnez
int number;
number= 12;


On peut déclarer et initialiser en même temps :

 
Sélectionnez
int number = 12;


Autre exemple, créer un double et l'initialiser avec la valeur 12 (double) :

 
Sélectionnez
 double conv = 12d;

Il est toujours préférable d'initialiser rapidement une variable. Le compilateur n'est pas content si on tente de lire une variable qui n'a pas été initialisée.

Si on utilise new cela initialise la variable en appellant le constructeur par défaut et donne la valeur par défaut du type.

 
Sélectionnez
int j = new int();  //j=0


On peut aussi déclarer et instancier un objet visuel ou non (ce n'est pas vraiment une variable mais plutôt un objet, mais bon ! c'est pour expliquer).

 
Sélectionnez
 Button MyButton;              //déclaration
 MyButton = new Button();   //instanciation
 
 //ou en 1 lignes
 
 Button MyButton = new Button();

Ici on a instancié un bouton que l'on nomme MyButton.
On remarque qu'on a mis new.


Autre exemple :

 
Sélectionnez
BitmapImage MyBitMap= new BitmapImage();

Ici on a instancié une BitmapImage que l'on a nommée MyBitMap.


Modificateur d'accès :

Une variable peut être accessible de partout (public) ou uniquement dans la classe ou la fonction où elle à été déclarée (private).

 
Sélectionnez
{ public partial class MainPage : PhoneApplicationPage

public int EntierPublic;  //Variable publique, on la verra partout, même dans une autre classe
private int EntierPrivé;  //Variable privée, on la verra uniquement dans la classe MainPage

private void SaveButton_Click(object sender, RoutedEventArgs e)
{

    int Entier privé;// toute variable déclarée dans une fonction est privée à cette fonction
                    // On ne peut pas mettre private ou public
    }

On remarque qu'une classe peut être aussi public ou privée, il en est de même pour une fonction.


Affectation :

C'est l'instruction la plus utilisée en programmation.

On peut aussi utiliser le terme « Assignation » à la place de l'affectation.

'Variable = Expression;' est une affectation, c'est le signe = qui indique l'affectation.

L'affectation transfère le résultat de l'expression située à droite du signe égal dans la variable (ou la propriété) à gauche du signe égal.

 
Sélectionnez
A= B;
Image non disponible

Affecte la valeur de la variable B à la variable A, la valeur de B est mise dans A.

Exemple concret :

 
Sélectionnez
    int number;
    number= 12;

On affecte à la variable de gauche (number), la valeur située à droite (12).

S'il y a une expression à droite, elle est évaluée, calculée et affectée à la variable de gauche :

 
Sélectionnez
    number= num + 12;

Si num est égal à 1, après l'affectation ci-dessus number sera égal à 13.

L'expression de droite peut être complexe : C*12-X+2.

Remarque : regardons l'expression suivante :

 
Sélectionnez
number= number +12;

Cela est valide. Le signe = n'indique pas une égalité mais une affectation de l'expression à droite qui est évaluée dans la variable de gauche. Si number avait la valeur 10, après cette affectation number aura la valeur 22.

L'affection marche pour les objets, leurs propriétés…

 
Sélectionnez
Bouton1.BackColor= Bouton2.BackColor;

Signifie que l'on donne au Bouton1 la même couleur de fond que celle du bouton2 : on affecte la valeur BackColor du Bouton2 au Bouton1.

Attention en C# pour l'égalité on utilise == (contrairement au basic où le symbole pour l'affectation et l'égalité est le même).
Exemple :

 
Sélectionnez
if ( a ==b)  //Si a égale b

typeof permet d' obtenir l'objet System.Type d'un type. sizeof sa taille en octets en mémoire (sizeof ne fonctionne que sur les types valeurs primitifs : int, bool, double…).
GetType permet d'obtenir le type d'un objet.

 
Sélectionnez
int i = 10;

// Quel est le type de int et la place occupée en mémoire par un int
MessageBox.Show("i est de type " + typeof(int) + " et occupe " + sizeof(int) + " octet(s) en mémoire.");

//On veut connaitre la valeur maximum permise avec un int
// (Min existe aussi)
             int max = int.MaxValue;
            MessageBox.Show(max.ToString());
            
//Comment connaitre le type d'une variable
            System.Type type = i.GetType();
            MessageBox.Show(type.ToString());

Une constante, comme son nom l'indique, est constante : elle prend une valeur au départ qui ne peut pas être modifiée.
On met « const » avant le type.

 
Sélectionnez
 public const int months = 12;

Ici on déclare une constante nommée months de type entier qui est public.

L'usage des constantes améliore la lisibilité et facilite les modifications.

III-D. Strings, Char, StringBuilder

Le type String peut contenir une chaîne de caractères (alphanumérique) comme du texte. La longueur de la chaîne n'est pas fixe. Il n'est pas nécessaire de mettre un caractère de fin de chaîne.

Le type char contient un seul caractère.

Pour information Char et String contiennent en interne le code des caractères au format Unicode (dans la variable,chaque caractère est codé sur deux octets) et pas de l'ASCII ou de l'ANSI…(ancien codage ou chaque caractère était codé sur un octet).
La String ocupe en mémoire 20+(n/2)*4 octets (arrondi à la valeur n/2 près), où n est le nombre de caractères. Les premiers caractères ont le même code Unicode et Ascii.

On peut utiliser string de C# ou System.String du Framework NET ; c'est exactement la même chose.

Déclarons une string :

 
Sélectionnez
string MyString;

Après avoir été créée, une String contient null c'est à dire rien (même pas une chaîne vide) ; il faudra l'initialiser pour qu'elle contienne quelque chose.


Il s'agit d'une classe string. Les strings sont de type référence.


Déclarons et initialisons :

 
Sélectionnez
string MyString= "Hello";


Notez bien l'importance des guillemets :
A est la variable A
"A" est une chaîne de caractères contenant le caractère "A"

Le signe « + » permet d'ajouter (de concaténer) des strings.

 
Sélectionnez
MyString= "Hello " + " word";

On peut voir si deux strings sont égales ou différentes (avec == et !=).

 
Sélectionnez
if ( a!= b)
MessageBox.Show (" a différent de b");

Par contre on ne peut pas utiliser supérieur, inférieur, supérieur et égal. Il faut utiliser Compare (voir plus bas).

On peut ajouter des caractères de contrôle dans une chaîne :
\' - simple quote, affiche '.
\" - double quote, affiche ".
\n : à la ligne.
\t : tabulation.
\f : saut de page.
\r : retour chariot.
\' : guillemet simple.
\" : guillemet double.
\\ : affiche \.
\u0066 ajoute le caractère de code Unicode 0066.

Pour éviter les \\ on peut aussi utiliser la notation littérale "verbatim" qui commence par @" :

 
Sélectionnez
string StartDirectory = @"c:\Users\exampleuser\start";

Il existe aussi le type (char) qui ne peut contenir qu'un seul caractère.

 
Sélectionnez
char mycar = 'd';

On note que pour un caractère, on utilise ' et non ".



string est une classe qui a des méthodes :

.ToUpper :

Elle retourne la chaîne de caractères en majuscules.

 
Sélectionnez
str=str.ToUpper();

Si str contenait "abc" il contiendra "ABC"

.ToLower transforme par contre la chaîne en minuscules.

.Trim :

Permet de supprimer des caractères en début et fin de chaîne.

Pour enlever les espaces avant et après la chaîne (cas le plus fréquent) :

 
Sélectionnez
string MyString= "  Informatique  "; //chaîne avec des espaces avant et après

MyString=MyString.Trim();            // enleve les espaces

MessageBox.Show("I"+MyString+"I");   // affiche 'IInformatiqueI'

Pour enlever un caractère :

 
Sélectionnez
string MyString= "/Informatique/";

MyString=MyString.Trim('/'); // enlever le caractère '/'

MessageBox.Show(MyString);

Notez bien qu'un caractère c'est '/' (une string c'est "/").

Pour enlever des caractères, il faut mettre en paramètre de Trim un char ou un tableau de char.

 
Sélectionnez
string MyString= "#@Informatique@#";  // chaîne initiale
char[] b;  // déclaration d'un tableau de Char
b=new char[2]  {'#', '@'};   //b est un tableau de Char contenant les 2 caractères à supprimer.

MyString=MyString.Trim(b);   //elimine les char en début et fin de chaîne

MessageBox.Show(MyString);   //affiche dans une MessageBox la chaîne traitée

Attention : bien utiliser Char[] qui est un tableau de caractères (et pas une string) pour définir les caractères à supprimer.

Il existe aussi TrimStart et TrimEnd pour agir seulement sur le début ou la fin de la chaîne.

Length :

Length : taille d'une chaîne en nombre de caractères.

Afficher la taille de la chaîne « Windows »

 
Sélectionnez
string MyString= "Windows";

MessageBox.Show(MyString.Length.ToString());

On remarque que MyString.Length retourne une valeur numérique qu'il faut convertir en string (à l'aide de ToString() pour pouvoir l'afficher.

Concat :

Concaténation de plusieurs chaînes : mise bout à bout :

 
Sélectionnez
string a="aa";
string b="bb";
string c="";

s=String.Concat(a,b);

Il est plus rapide de faire :

 
Sélectionnez
 s= a + b

Concat est très pratique quand on veut mettre bout à bout tous les éléments d'un tableau :

 
Sélectionnez
string[] b = { "bb", "cc","dd"}; // création d'un tableau de 3 chaînes

string c = string.Concat(b);

MessageBox.Show(c);     // donne bbccdd

Insert :

Insère une chaîne dans une autre.

 
Sélectionnez
string MyString= "Windows";

string c = MyString.Insert(3,"  ");

MessageBox.Show(c);  //Affiche 'Win  dows'

Noter : le premier caractère a la position 0.

Remove :

Enlève des caractères (ici deux caractères) à une certaine position dans une chaîne.
Les paramètres sont (position premier caractère, nombre de caractères à enlever).

 
Sélectionnez
string c = MyString.Remove(3,2);

Replace :

Retourne une nouvelle chaîne, copie de la chaîne de départ, mais où toutes les occurrences d'une chaîne à remplacer ont été substituées par une autre chaîne.

Resultat=ChaîneDépart.Replace(ChaîneARemplacer,ChaîneQuiRemplace);

 
Sélectionnez
string MyString= "02.12.1934";

string c = MyString.Replace(".","/");

MessageBox.Show(c);  //Affiche '02/12/1934'

Replace est pratique pour éliminer un caractère dans une chaîne.

Exemple, éliminer les « / » dans une string :

 
Sélectionnez
 entr = entr.Replace("/", "");

Split :

Découpe en plusieurs sous chaînes une chaîne de départ, cela par rapport à un séparateur.

Je récupère une chaîne de mots ayant pour séparateur « ; », je veux mettre chaque mot dans un tableau.

 
Sélectionnez
string MyString = "Philippe;Jean;Toto";
string[] b; 

b = MyString.Split(';');

Il peut y avoir plusieurs caractères de séparation :

 
Sélectionnez
 string MyString = "Philippe.Jean;Toto";
 string[] b; 

 b = MyString.Split(';', '.');

Remarque : quand on déclare le tableau b, on ne donne pas le nombre d'élément, c'est Split qui crée autant d'élément qu'il faut.

Si le paramètre ne contient pas de caractères, les délimiteurs sont supposés être des espaces blancs.

On peut ajouter deux paramètres permettant d'indiquer le nombre de lignes maximum et forcer l'élimination des lignes vides.

 
Sélectionnez
mots = phrase.Split('/t', 3, StringSplitOptions.RemoveEmptyEntries);

Ici le séparateur est le caractère de tabulation (/t) : retour de trois éléments. Si un élément est vide (séparateur en premier ou dernier caractère par exemple) on l'élimine.

Autre exemple, on a une string contenant des mots, découper les mots et les mettre dans un tableau de string :

 
Sélectionnez
string text  = "Ceci est un cours C# pour les débutants et les autres";

// Conversion de la String en Tableau de mots :.
string[] mots= text.Split(new Char [] {' ', ',', '.', ';', ':'});

.Join :

Concatène tous les éléments d'un tableau et peut ajouter des séparateurs.

Si b[] est un tableau de string, je veux ajouter les éléments bout à bout en les séparant d'un espace :

 
Sélectionnez
 string[] b = {"Bonjour","monsieur"}; 

 string c = String.Join( " ", b);

MessageBox.Show(c);

.IndexOf .LastIndexOf :

Indique le numéro du caractère, la position (la première occurrence) d'une chaîne à chercher dans une autre. LastIndexOf commence par la fin.

 
Sélectionnez
string b = "Bonjour monsieur"; 

 int pos = b.IndexOf( "our");

MessageBox.Show(pos.ToString());

Se souvenir : le premier caractère est en position 0 en NET.
.LastIndexOf retourne la dernière occurrence.
Si la chaîne n'est pas trouvée, renvoie -1.

On peut ajouter un paramètre indiquant que la recherche doit débuter à partir du caractère de position n.

 
Sélectionnez
int pos = b.IndexOf( " ",3 );

La position de départ de recherche peut être ensuite celle de la première occurrence pour retrouver la seconde occurrence.
On peut rajouter un troisième paramètre pour indiquer dans combien de caractères rechercher.

On peut rechercher un caractère :

 
Sélectionnez
int pos = b.IndexOf( 'c',3 );

.IndexOfAny .LastIndexOfAny :

Indique la position (la première occurrence) de tout caractère d'un tableau dans une chaîne avec en plus la possibilité d'indiquer la position de départ. Retourne -1 si les caractères ne sont pas trouvés.

 
Sélectionnez
string b = "Bonjour monsieur,";

 int pos = b.IndexOfAny(new char[] { '@', '.', ',', '!' });

MessageBox.Show(pos.ToString());

.Compare :

Compare deux chaînes :

Il existe == et != qui permet de voir si deux chaînes sont égales ou différentes.
Par contre les autres opérateurs mathématiques de comparaison (supérieur, inférieur…) ne sont pas supportés par les chaînes.
Ainsi on ne peut pas utiliser a > b.
Il faut donc utiliser Compare.
Comparer un caractère veut dire voir s'il est avant ou après dans l'ordre alphabétique ; dans la culture en cours.

 
Sélectionnez
string a = "Hello";
string b = "Bonjour monsieur,";

int c = String.Compare(a, b);

MessageBox.Show(c.ToString()); // retourne '1'

Retourne un entier égal à :
-1 si a inférieur à b.
0 si a=b
1 si a supérieur à b

On peut comparer des sous chaînes et indiquer la sensibilité à la casse :

 
Sélectionnez
int c = String.Compare( a, 2, b, 3, 4, StringComparison.CurrentCultureIgnoreCase);

Paramètres : chaîne a, pos dans a, chaîne b, pos dans b, nombre de car à comparer, option de recherche : la majuscule et la minuscule d'un caractère sont considérées comme égales.

On peut imposer une autre culture (où l'ordre des caractères est différent) et ajouter des options de recherche :

 
Sélectionnez
int c = String.Compare(a, b, new CultureInfo("en-US"), CompareOptions.IgnoreCase)

'String.CompareOrdinal' fait une comparaison sur le code Unicode des caractères ; ici pas de culture ni d'option IgnoreCase, mais c'est plus rapide.

Pour être complet, on peut aussi utiliser CompareTo c'est une méthode d'instance :
Une string est comparée à un objet.

 
Sélectionnez
int cmpVal = str1.CompareTo(MyObject);

Respecte la casse et est spécifique à la culture.

.Equals

Permet de comparer deux chaînes. Retourne true, un bool, si elles sont égales.

 
Sélectionnez
string a = "Hello";
string b = "hello";

bool c = String.Equals(a, b, StringComparison.InvariantCultureIgnoreCase);

MessageBox.Show(c.ToString());

On peut utiliser a==b pour comparer plus simplement deux chaînes mais on n'a pas possibilité de choisir des options de comparaison comme ci-dessus.

.Contains :

Permet de savoir si une chaîne apparaît dans une autre :

 
Sélectionnez
string a = "Hello word";
string b = "hello";
bool c = a.Contains(b);

.Substring :

Extrait une partie d'une chaîne.

Le premier paramètre indique la position de départ ; le second, le nombre de caractères à extraire.

 
Sélectionnez
 string a = "Hello word";
 string c = a.Substring(2,2);

Exercice 1 : comment obtenir les quatre caractères de droite :

 
Sélectionnez
 string a = "Hello word";

string c = a.Substring(a.Length-4);

MessageBox.Show(c);

Ici on omet le second paramètre, la longueur de la sous-chaîne va jusqu'a la fin de la chaîne.

Exercice 2 : comment obtenir les trois caractères de gauche :

 
Sélectionnez
string a = "Hello word";

string c = a.Substring(0,3);

MessageBox.Show(c);

On peut créer des chaînes avec la classe String :

 
Sélectionnez
string d = new string('o', 15);
MessageBox.Show(d);  //affiche 'ooooooooooooooo'

.PadRight :

Aligne les caractères de cette chaîne à gauche et remplit à droite en ajoutant un caractère Unicode spécifié pour une longueur totale spécifiée.

 
Sélectionnez
string a = "Hello word";
         
string c = a.PadRight(14,'.');

MessageBox.Show(c); // affiche 'Hello word....'

PadLeft fait l'inverse.

.StartsWith() et EndsWith() :

Permettent de tester si une string commence ou se termine par une string, retourne true ou false.

Tester si la string s commence par "abc" :

 
Sélectionnez
string a = "Hello word";

bool b = a.StartsWith("He");

MessageBox.Show(b.ToString());

On peut ajouter un argument gérant la culture ou la casse.

Les variables Char contiennent un caractère et un seul, un caractère est stocké sous la forme d'un nombre de 16 bits (deux octets) non signé dont les valeurs sont comprises entre 0 et 65 535. Chaque nombre représente un seul caractère Unicode.

Déclarons une variable c de type Char et initialisons la avec le caractère « 5 » (notez que pour indiquer que c'est un caractère on utilise ' ').

 
Sélectionnez
Char c = '5';
if (Char.IsDigit(c))
    textBlock1.Text = "C'est un chiffre";

Ensuite on a testé si "c" était un chiffre (Char.IsDigit est une méthode static, c'est une méthode de la classe Char).


ToCharArray : Permet de passer une string dans un tableau de Char :

 
Sélectionnez
string maString  = "abcdefghijklmnop";
Char[] maArray = maString.ToCharArray();

La variable maArray contient à présent un tableau composé de Char, chacun représentant un caractère de maString.


Vous pouvez extraire un caractère particulier du tableau de caractères en faisant référence à l'index de ce caractère puis en le transformant en string grâce à ToString() :

 
Sélectionnez
textBlock1.Text = maArray[2].ToString();

On peut aussi très simplement lire un caractère à une certaine position dans une string :

 
Sélectionnez
string maString  = "abcdefghijklmnop";
Char ch = maString[0];

Utilisez les méthodes IsControl, IsDigit, IsHighSurrogate, IsLetter, IsLetterOrDigit, IsLower, IsLowSurrogate, IsNumber, IsPunctuation, IsSeparator, IsSurrogate, IsSurrogatePair, IsSymbol, IsUpper et IsWhiteSpace pour déterminer si un caractère est dans une catégorie Unicode particulière, comme un chiffre, une lettre, un signe de ponctuation, un caractère de contrôle, etc.

 
Sélectionnez
Char monChar='f';
            bool test= Char.IsLetter(monChar);
            bool test1 = Char.IsControl(monChar);
            bool test2 = Char.IsDigit(monChar);
            bool test3 = Char.IsLetterOrDigit(monChar);
            bool test4 = Char.IsLower(monChar);
            bool test5 = Char.IsNumber(monChar);
            bool test6 = Char.IsPunctuation(monChar);
            bool test7 = Char.IsSeparator(monChar);
            bool test8 = Char.IsSymbol(monChar);
            bool test9 = Char.IsUpper(monChar);
            bool test10 = Char.IsWhiteSpace(monChar);

StringBuilder.
Une string est immutable (en anglais) , immuable en vrai français. Cela se dit d'un objet dont les propriétés sont définies à la création de l'objet puis ne peuvent plus changer durant la vie de l'objet.
C'est plus simple pour la gestion de la mémoire et pour le multithreading (comme la chaîne ne change pas tous les threads y ont accès).

Pourtant quand on utilise les méthodes de la Classe string (comme ToUpper) ou la concaténation de chaînes, on a l'impression que la string est modifiée. C'est faux : la chaîne d'entrée restera toujours à la même valeur alors que la valeur de retour sera une nouvelle chaîne traitée à partir de la première mais prenant le nom de la première.
Si j'ajoute 1000 fois un caractère à une chaîne, il y a création de 1000 chaînes ! En terme de performance c'est déplorable.


Dans la programmation courante (une concaténation, un passage en majuscule), il y a peu d'incidence ; mais si on concatène 1000 chaînes, il est préférable d'utiliser un StringBuilder qui lui est mutable.

 
Sélectionnez
using System.Text;

 StringBuilder stringb = new StringBuilder();
        for (int i = 0; i < 1000; i++)
            stringb.Append("T");

Pour concaténer on utilise Append.
On peut utiliser Remove, Replace, Length, Clear, Insert, ToString.

Exemple avec Replace :

 
Sélectionnez
stringb.Replace('!', 'o');


Autre exemple :

 
Sélectionnez
float value = 123458.44f;

//Constructeur initialisant la StringBuilder avec une String
System.Text.StringBuilder stringb = new System.Text.StringBuilder("....");

//Append successif ajoutant une chaîne correspondant à une valeur numérique puis 5 étoiles.
stringb.Append(value).Append('*', 5);

MessageBox.Show ( sb.ToString() );


On peut aussi lire ou mettre un caractère à une position particulière de la StringBuilder :

 
Sélectionnez
char ch = stringb[5];

III-E. Variables numériques

Il y a plusieurs types de variable numérique :
les types en C# et leur équivalent en .NET.

Les entiers ou intégraux

Pour une variable entière il n'y a pas de possibilité de virgule ! Attention, une division de deux entiers donne un entier.
Pour le mathématicien, cela correspondent aux nombres entiers relatifs : entiers positifs ou négatifs et 0 (..-3 -2 -1 0 1 2 3…), mais avec une limite.

int en .NET c'est Int32 (integer (nombre entier)) occupe 4 octets, plage : [-2^31 ; 2^31-1]

uint en .NET c'est UInt32 (unsigned integer (nombre entier non signé)) occupe 4 octets, plage : [0 ; 2^32-1]

long en .NET c'est Int64 (nombre entier long) occupe 8 octets, plage : [-2^63 ; 2^63-1]

ulong en .NET c'est UInt64 (unsigned long (nombre entier long non signé)) occupe 8 octets, plage : [0 ; 2^64-1]

sbyte en .NET c'est SByte (signed byte (octet signé)) occupe 1 octet, plage : [-2^7 ; 2^7-1]

byte en .NET c'est Byte (octet) occupe 1 octet, plage : [0 ; 2^8-1]

short en .NET c'est Int16 (nombre entier court) occupe 2 octets, plage : [-2^15 ; 2^15-1]

ushort en .NET c'est UInt16 (unsigned short) (nombre entier court non signé) occupe 2 octets, plage : [0 ; 2^16-1]



Les nombres en virgule flottante ou réels ou décimaux

Un réel peut avoir une partie fractionnaire : 1,454 est un réel.
Pour le mathématicien, les Single, Double… correspondent aux nombres réels ou fractionnaires, mais avec des limites ( sur la précision et le fait qu'ils ne sont pas infinis).

float en .NET c'est Single (flottant )(nombre réel)occupe 4 octets, plage : ±1,5*10^-45 à ±3,4*10^+38 (7 chiffres de précision).

double en .NET c'est Double (double flottant )(nombre réel)occupe 8 octets, plage : ±5,0*10^-324 à ±1,7*10^+308 (15 à 16 chiffres de précision)



decimal :
Le type decimal prend en charge jusqu'à 29 chiffres significatifs et peut représenter des valeurs jusqu'à 7,9228 x 10^28. Ce type de données est particulièrement adapté aux calculs (par exemple financiers) qui exigent un grand nombre de chiffres, mais qui ne peuvent pas tolérer les erreurs d'arrondi. Par contre les calculs sont plus lents.

decimal en .NET c'est Decimal (nombre décimal) occupe 16 octets, plage : ±1,0*10^-28 à ±7,9*10^28 (28 à 29 chiffres significatifs)

On utilise les entiers quand c'est possible (c'est plus rapide), pour une variable de boucle par exemple.
On utilise les decimal quand il doit y avoir une précision parfaite (calcul financier) mais les calculs sont plus longs.
float et double sont largement utilisés quand on a besoin de nombre réels.

Attention, les variables numériques en informatique ont des limites et ne peuvent pas contenir de nombre infiniment grand ou infiniment petit car ils sont stockées dans un nombre d'octet bien précis. Il y a une limite maximum et une limite minimum : un int est par exemple codé sur 32 bits ce qui fait qu'il peut varier de -2147483648 à 2147483647. Si on utilise une valeur trop grande, une erreur se produit.

On peut déclarer, instancier une variable et donner une valeur a une variable numérique :

 
Sélectionnez
int myInt=12;

Le nombre 12 se nomme un littéral car il donne une valeur immédiatement, en dur.
Par défaut :
12 est un int
1.2 est un double
On remarque que dans un littéral le caractère décimal est toujours le point.

Mais comment forcer le littéral a être d'un type précis ?

Un nombre entier est considéré comme un int s'il tient dans la plage de valeurs d'un int (exemple : -135), et comme un long s'il ne tient pas dans la plage de valeurs d'un int mais dans celle d'un long (exemple : 5452862578).
Un u à la fin symbolise un uint. Exemple : 3u (ou 3U).
Un l à la fin symbolise un long. Exemple : 5l (ou 5L).
Un ul à la fin symbolise un ulong. Exemple : 1ul (ou 1UL).
Un nombre écrit avec une partie décimale, ou avec un d à la fin est un double. Exemple : 2.0 ou 2d (ou 2D).
Un f à la fin symbolise un float. Exemple : 7.0f ou 7f (ou 7F).
Un m à la fin symbolise un décimal. Exemple : 6.0m ou 6m (ou 6M).

Quand on a déclaré une variable, il faut lui assigner une valeur, sinon on ne peut pas l'utiliser :

 
Sélectionnez
int i;
if (i== null)     // n'est pas accepté

Ces types de variable numérique sont des types « valeur » car en fait se sont des structures (et non des classes). On verra cela plus loin.

Voyons les opérateurs :

On peut utiliser +, -, /, * (multiplier).

 
Sélectionnez
int myint=12;
int mysecondint;
mysecondeint= myint+ 3;


'%' donne le reste d'une division (modulo) :

 
Sélectionnez
int myInt = 21 % 10;

MyInt=1

Dans les conditions, on utilise les opérateurs :

égal, différent : ==, != supérieur, inférieur, supérieur ou égal, inférieur ou égal : > , <, >=, <=

Exemple : si A différent de B

 
Sélectionnez
if (A!=B)
{}


Écriture compacte, incrémentation :

 
Sélectionnez
i += 5; //à la place de i = i + 5;.
i -= 5; //à la place de i = i - 5;
i *= 5; //à la place de i = i * 5;
i /= 5; //à la place de i = i / 5;
//Exemple :
i += 5

// Pour ajouter 1 on incrémente, pour retirer 1 on décremente
//On peut post incrémenter ou post décrémenter
i++; //à la place de i = i +1;
i--; //à la place de i = i -1;
//On peut pré incrémenter ou pré décrémenter
++i; //à la place de i = i +1;
--i; //à la place de i = i -1;

En pré incrémentation l'expression ++i représente la valeur de l'opérande après son incrémentation ; la post incrémentation, la valeur d'avant.

 
Sélectionnez
//Exemple :
int i= 2;
int j= i++  //i=3 mais j=2 car l'incrémentation a été effectuée après l'assignation

Sur les intégraux, on peut utiliser des opérations sur les bits :

 
Sélectionnez
                    '>>' décalage à droite.<br/>
                    '<<' décalage à gauche.<br/>
                    '~'  inversion de bits.

Priorité des opérateurs :

S'il y a plusieurs opérateurs, * et / ont la priorité la plus forte (ils sont effectués avant) puis + et -.
Cela veut dire que C# effectue les multiplications et divisions puis les additions et soustractions.
S'il y a plusieurs opérateurs de même priorité, l'ordre des calculs se fait de gauche à droite.
Pour éviter toute faute d'interprétation utiliser des parenthèses :

 
Sélectionnez
double delta= B/(A-C);
//Et pas :
double delta= B/A-C;

Les variables numériques peuvent contenir des valeurs qui ne sont pas infinies, il y a une limite.
On peut connaître la valeur maximum grâce à MaxValue et la valeur minimum avec MinValue.

 
Sélectionnez
 MessageBox.Show(double.MaxValue.ToString());

Affiche la plus grande valeur possible pour un double.

Dépassement de capacité, division par zéro :

Le dépassement de capacité d'entiers (si par exemple une multiplication de deux entiers donne un nombre plus grand que le plus grand des entiers) lève une exception OverflowException ou ignore les bits les plus significatifs du résultat (si pas de vérification).
La division d'entiers par zéro lève toujours une exception DivideByZeroException.
Un dépassement de capacité ou une division par zéro en virgule flottante ne lève jamais d'exception, car les types virgule flottante gèrent la représentation de l'infini et NaN (Not a Number).
Le dépassement de capacité décimal lève toujours une exception OverflowException. La division décimale par zéro lève toujours une exception DivideByZeroException.

Par défaut il n'y a pas de vérification de dépassement de capacité. Pour l'activer, passer par les propriétés du projet (menu Projet puis Propriétés).

Image non disponible

Pour l'activer sur une portion de code seulement, il faut utiliser la directive Checked dans le code.

 
Sélectionnez
checked
{
    int x = a + b;
}


Pour une variable en virgule flottante, il existe donc les valeurs NegativeInfinity et PositiveInfinity.
Pour une variable en virgule flottante, si on la divise par zéro on obtient NaN (Not a Number).


La classe Math : fournit des constantes et des méthodes statiques pour des fonctions trigonométriques, logarithmiques et d'autres fonctions mathématiques courantes. On a bien dit méthodes statiques ; on utilisera donc la syntaxe : Math.Pow() et pas mynumber.Pow.


Max retourne le plus grand des deux nombres :

 
Sélectionnez
double number1=12.3;
double number2=6.7;
  double result = Math.Max(number1, number2);


Min retourne le plus petit des deux nombres :

 
Sélectionnez
double number1=12.3;
double number2=6.7;
  double result = Math.Min(number1, number2);


Abs retourne la valeur absolue :

 
Sélectionnez
double number1=12.3;
  double result = Math.Abs(number1);



Sign : retourne un int indiquant le signe (-1 si négatif, 1 si positif).

Ceiling : retourne la plus petite valeur entière supérieure ou égale.

 
Sélectionnez
double number1=12.6;
  double result = Math.Ceiling(number1); // retourne 13

Floor : retourne le plus grand entier égal ou inférieur, arrondi à l'entier inférieur le plus proche en allant vers l'infini négatif.
(N=1.7 R=1)


Round retourne le nombre entier le plus proche.
' Si N=1.7 Round() retourne R=2
' Si N=1.2 Round() retourne R=1
' Si N=1.5 Round() retourne R=2
On peut rajouter un paramètre qui arrondit à x décimale(s) :

 
Sélectionnez
 result = Math.Round(number, NbDecimal);

NbDecimal est un entier

Convient bien pour arrondir les valeurs financières avec Math.Round (number,2).

Sqrt permet d'avoir la racine carrée :

 
Sélectionnez
 result = Math.Sqrt(number1);

Pow permet d'élever un nombre x à la puissance y :

 
Sélectionnez
 result = Math.Pow(x, y);


PI donne le nombre pi.

 
Sélectionnez
 decimal perimetre= 2* Math.PI * Rayon;


Log permet d'avoir le Log népérien, Log10 permet d'avoir le Log de 10 :

 
Sélectionnez
 result = Math.Log(number1);

Il existe aussi Exp : exponentiel (e à la puissance spécifiée).


Enfin on a tous les méthodes trigonométriques :
Sin Cos Tan ASin ACos ATan (retourne l'angle à partir du Sin, Cos, Tan), Sinh Cosh, Tanh (hyperbolique).
Noter que les angles sont en radians. (Rappel : 2pi=360° ; Angle en radians= (2pi/360)*Angle en degrés). .

 
Sélectionnez
double d= Math.Cos(Math.PI);
            MessageBox.Show(d.ToString()); //affiche -1

Problème de précision :

on a vu qu'avec les variables en virgule flottante, comme les float par exemple, certaines valeurs ou calculs sont représentés avec une certaine approximation, infime mais réelle.
Souvent le calcul est exact mais parfois (rarement en pratique courante) on peut avoir une infime manque de précision. Voici un exemple : on a 0,0001, avec une boucle on l'additionne dix milles fois :

 
Sélectionnez
        int i;
        float k=0;
        float j = 0.0001F;
       
        for (i = 1 ; i <10001; i++)    k = k + j;
       
        
        MessageBox.Show(k.ToString()); //' Affiche 1.000054

        Decimal k1=0;
        Decimal j1  = 0.0001m;
        
        for (i = 1 ; i <10001; i++)    k1 = k1 + j1;
        
        MessageBox.Show(k1.ToString()); // Affiche 1

Avec les decimal le résultat est 1.
Avec les float le résultat est 1.000054

Avec les entiers la précision est parfaite.
Avec les decimal aussi précision parfaite, mais les calculs sont un peu plus lents.
Avec les float, les double la précision parfois n'est pas parfaite.

III-F. Autres types de variable : booléen, objet

Une variable de type bool contient une valeur booléenne :true ou false (vrai ou faux pour les sous doués en anglais !).

Exemple :

 
Sélectionnez
bool myBoolean;

myBoolean = true;

Après déclaration un boolean a la valeur par défaut false.

On peut créer une variable de type object, on peut y mettre ce qu'on veut : un int une string…

 
Sélectionnez
onject o;  //Déclaration
o = new object() //Instanciation

//ou
object o = new object();
            o = 2;
            o = "toto";

Une bonne règle est d'utiliser des variables le plus typées possible plutôt que des objets.
Un exemple : une variable qui doit contenir un objet visuel peut être non typée (Object), moyennement typée (Control) ou très typée (Button par exemple).
Plus la variable sera typée plus le code sera solide, plus on évitera les erreurs de programmation.


Souvent le paramètre sender d'une fonction évènement retourne un objet ; il faut le convertir dans le type de départ.

III-G. Nullable

Comment signaler que le contenu d'une variable ne correspond à rien ?

Les types valeur peuvent être étendus afin d'accepter une valeur normale habituelle et une valeur null. On peut déclarer un type Nullable de plusieurs manières :

 
Sélectionnez
Nullable<int> i=1;    // Classe Nullable
//ou
int? ii=null;         // c'est '?' qui indique que c'est ou nullable

On a déclaré un Nullable entier nommé i et un autre nommé ii.
Un entier nullable (i ou ii) peut prendre toutes les valeurs d'un entier (1, 2, 3…) et aussi la valeur null.
Il a deux propriétés :
HasValue (qui est égal à true quand Value contient un entier, et false quand le nullable= null),
Value contenant un entier. Si on tente d'utiliser Value quand le nullable= null, il y a levée d'une exception.

Il faut toujours bien initialiser une variable même nullable :

 
Sélectionnez
int? i;   // déclaré , non initialisé
i= 2;     // i=2 HasValue=True i.Value= 2
i= null;  // i=null HasValue=false i.Value non lisible

Si on essaie de lire la valeur d'une variable non initialisée, il y a erreur (il ne faut pas penser qu'elle contient null pour la raison qu'elle n'a pas été initialisée).

Autre exemple : déclarons un bool nullable :

 
Sélectionnez
bool? myBool= true;

myBool accepte les valeurs true, false et null.

Cela a de l'intérêt quand on travaille avec les bases de données qui ont des champs qui contiennent un Null et avec Linq.
Cela est aussi intéressant quand on veut indiquer qu'une variable n'a pas encore de valeur. Avant pour une variable indiquant un poids, quand le poids n'était pas renseigné on lui donnait la valeur -1. Maintenant on utilise un nullable et on lui donne la valeur null.

III-H. Tableau

Les tableaux permettent de regrouper des données de même type. Ils vous permettent de faire référence à un ensemble de variables par le même nom et d'utiliser un numéro, appelé index ou indice, pour les distinguer.

Le nombre d'éléments est décidé au départ. Sa longueur est donc fixe. Il n'est plus possible de modifier le nombre d'éléments par la suite.
Les tableaux sont des types référence.


Voici un tableau nommé c contenant trois éléments :
c[0]=2
c[1]=3
c[2]=15

Image non disponible

On note que le premier élément du tableau a l'index 0.

On va déclarer un tableau nommé c (sans donner le nombre d'élément), l'initialiser à trois éléments (avec new) puis donner une valeur à chaque élément :

 
Sélectionnez
int[] c;       //déclaration de c; int[] c'est un tableau d'entiers, non encore alloué
c=new int[3];  //instanciation, allocation en mémoire de 3 éléments
c[0]=2;        //affectation d'une valeur au premier élément
c[1]=3;
c[2]=15;

On voit que pour un tableau à trois éléments, l'index va de 0 à 2 (de 0 à n-1 si n est le nombre d'éléments).

On peut grouper les lignes 1 et 2 :

 
Sélectionnez
int[] c = new int[3];
c[0]=2;
c[1]=3;
c[2]=15;

On peut grouper les lignes 2 et suivantes :

 
Sélectionnez
int[] c ;
c = new int[3] {2, 3, 15};

On peut déclarer et initialiser les valeurs, dans ce cas inutile de donner le nombre d'éléments :

 
Sélectionnez
int[] c = {2, 3, 15};


Exemple avec des tableaux de float et de strings :

 
Sélectionnez
float[] f ={2.5f, 3.8f, 1.5f};
string[] mystring ={"Paul", "Louis", "Jean"};

On remarque qu'on a ajouté un f aux valeurs de la première ligne pour forcer les valeurs à être des floats.


On peut ensuite modifier ou lire un élément :

 
Sélectionnez
int[] c {2, 3, 15};
c[0]=45;                //modification de la valeur du premier élément
int MaVariable= c[2];  //on déclare une variable Mavariable on y affecte la valeur de c[2]

Pour un tableau déclaré et alloué (après int[] c =new int [3];) les éléments contiennent la valeur par défaut du type (null pour un type référence, 0 pour un int par exemple).
Si on utilise un numéro d'index supérieur au plus grand index possible cela déclenche une IndexOutOfRangeException.

Travail sur la référence d'un tableau :

 
Sélectionnez
// premier tableau
string[] MonTableau ={"Paul", "Louis", "Jean"};

// second tableau
string[] AutreTableau;

//copie
AutreTableau= Montableau;

Maintenant AutreTableau contient trois éléments :"Paul", "Louis", "Jean" car les deux tableaux pointent sur la même zone mémoire (par référence oblige !).

Pour parcourir un tableau : on peut utiliser une boucle foreach.

 
Sélectionnez
string[] MonTableau ={"Paul", "Louis", "Jean"};
foreach (string s in Montableau)
{ 
System.Diagnostics.Debug.WriteLine(s);
 }

ou une boucle for.

 
Sélectionnez
string[] MonTableau ={"Paul", "Louis", "Jean"};
for (int i = 0; i < t.Length; i++)
            {
                System.Diagnostics.Debug.WriteLine(t[i]);
            }

On peut créer des tableaux à deux dimensions :

 
Sélectionnez
double[,] myArray = new double[,] { {0.5, 0.2}, {1.4, 1.0} };

myArray[0, 0] vaut 0,5
myArray[1, 1] vaut 1,0

On peut créer des tableaux de tableaux.

 
Sélectionnez
//on crée un grand tableau qui contient deux tableaux
int[][] myTableudeTableau = new int[2][];
//le premier tableau contient 5 éléments
myTableaudeTableau[0] = new int[5];
//on donne une valeur au deuxième élément du premier tableau
myTableaudeTableau[0][1] = 32;

Les tableaux multidimensionnels ne sont pas très bons en terme de performance : il est préférable d'utiliser des tableaux de tableaux.

Les tableaux héritent de la classe Array dont ils peuvent utiliser les méthodes :
La méthode static Clear efface les éléments d'un tableau, les deux derniers paramètres indiquent l'index du premier élément à effacer et le nombre d'éléments à effacer.
Pour effacer tous les éléments.

 
Sélectionnez
Array.Clear(numbers,0, numbers.Length);

Ne supprime pas d'élément du tableau (puisque la longueur est fixe), mais ça remplace chaque élément par la valeur par défaut.

On peut trier :

 
Sélectionnez
Array.Sort(numbers);

Il est possible d'ajouter un paramètre pour la méthode de comparaison.


BinarySearch permet une recherche dans le tableau, il retourne l'index de l'élément trouvé.
Et renvoie une valeur négative si l'élément n'est pas trouvé.
C'est une recherche dichotomique, ça suppose donc que le tableau soit déjà trié ; sinon ça va renvoyer n'importe quoi.

 
Sélectionnez
string[] mots = { "toto", "lili", "tata" };
int c =Array.BinarySearch(mots, "lili");
textBlock1.Text = c.ToString();


Il existe aussi Copy, IndexOf, LastIndexOf, Resize, Reverse.

Sur les tableaux, on peut utiliser les méthodes d'extension de Linq (voir le chapitre sur Linq, il y a des exemples sur des collections mais c'est pareil).

III-I. Conversion de variable, caractère décimal

On a souvent besoin de convertir le contenu d'une variable d'un certain type dans un autre type.
Par exemple : on peut faire des calculs avec les variables numériques mais on ne peut afficher que des string ; aussi il faut convertir souvent.


Il existe des conversions implicites (elles se font automatiquement sans avoir à l'indiquer).
Les conversions implicites, pour les variables numériques, sont celles entre les différentes gammes d'entiers et nombres en virgule flottante vers un type de plage plus grande. Aucune conversion implicite n'a lieu entre booléens et entiers, entre membres d'énumération et entiers.

 
Sélectionnez
int number = 4154613;
long bigNumber = number;

Ici on affecte un int a un long, l'int est transformé en long automatiquement ; cela est accepté par le compilateur car la plage des long est plus grande que celle des int, donc forcement l'int va pouvoir être transformé en long sans perte.


byte, short, int, long peuvent être transformés en long, float, double, decimal. Un double ne peut pas être transformé implicitement en int !

Certaines conversions doivent être indiquée clairement, on parle de conversion explicite :

Il est nécessaire parfois de convertir un type numérique dans un autre type (ayant une plage moins large). On parle de Cast.
Pour effectuer un cast (conversion), il faut spécifier le type désiré entre parenthèses devant la valeur ou la variable à convertir.

On veut caster un double en entier par exemple.

 
Sélectionnez
double Number = 12;
int NumberInt;
NumberInt= (int) Number;

(int) indique dans quel type convertir.

Parse et TryParse sont des méthodes permettant aussi de convertir une chaîne de caractère vers tous type numérique.

int.Parse convertira une chaîne en entier.

On a aussi souvent besoin de convertir une string en nombre : une string à été saisie au clavier, elle est dans un TextBox, je veux la transformer en nombre pour faire des calculs. double.Parse convertira en double.

Le séparateur décimal est celui de la culture en cours (',' pour le français).

Conversion une string en entier :

 
Sélectionnez
int i=int.Parse("12");


On peut être dans une culture française, mais vouloir utiliser le séparateur anglais ; dans ce cas on va ajouter un paramètre : la culture « en-US ».

 
Sélectionnez
string s ="1.2";
Double d = Double.Parse(s, new System.Globalization.CultureInfo("en-US"));



S'il y a impossibilité de convertir, il y a levée d'une exception et l'application s'arrête. Pour éviter cela on utilise TryParse qui retourne un bool qui prend la valeur false si la conversion échoue (et ne lève pas d'exception).
out indique la variable qui recevra la valeur convertie (out indique une variable de sortie qui est déclarée mais peut ne pas être initialisée).

Cast de texte qui est dans le TextBox TextQuantité en double, on retrouve le résultat de la conversion dans le double nommé Quantité ; ConversionIsOk= false si la conversion échoue :

 
Sélectionnez
Double Quantite ;
 bool ConversionIsOk = double.TryParse(TextQuantite.Text, out Quantite);

Texte en date

 
Sélectionnez
DateTime MaDate ;
bool conversionIsOk = DateTime.TryParse(TextDate.Text, out MaDate);

On peut ainsi convertir une string en int, decimal, float, DateTime.

Là aussi on peut ajouter la culture.

 
Sélectionnez
Double Number ;
bool conversionIsOk = Double.TryParse(s, NumberStyle.Number ,new CultureInfo("en-US"), out Number);

On utilise souvent la méthode ToString().

Par exemple, j'ai une variable de type int (entier) ; pour l'afficher, je dois la convertir en string (on peut afficher une chaîne de caractères mais pas une valeur numérique). Pour cela j'ai la méthode ToString :

 
Sélectionnez
int entier=12;
string chaîneaAfficher;
chaîneaAfficher= entier.ToString();

On peut ajouter en paramètre le format de la string.
Ici on va afficher deux caractères après la virgule.

 
Sélectionnez
float Z= 12.4556;
string   mastring =Z.ToString("0.00")
// string= "12,45"


Enfin il y a la classe Convert qui permet de convertir les types primitifs :

 
Sélectionnez
int myint = 12;
double mydouble = Convert.ToDouble(myint);

Convert.ToDouble convertit en double ; il existe ToInt16, ToInt32, ToInt64, ToChar, ToSingle, ToDecimal, ToByte…

Pour convertir une string en Int32 :

 
Sélectionnez
int a = Convert.ToInt32(string);

Exemple pratique :
l'utilisateur saisit "12" dans un TextBox c'est une string, il faut faire un calcul (numérique) puis afficher le résultat dans un TextBlock.

 
Sélectionnez
double number;

//On convertit la string qui est dans Montextbox.text en double          
bool res = double.TryParse(Montextbox.Text, out number);
//On fait le calcul
number= number *number;
// On convertit en string pour l'affecter à Montextblock.Text
Montextblock.Text= number.ToString();

Le séparateur décimal est celui de la culture en cours :
',' pour fr.
'.' pour en.

Où peut-on trouver ce séparateur ?

 
Sélectionnez
string separateur;
separateur=Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator;

ToString, TryParse… utilisent donc le séparateur de la culture en cours.

Par contre pour les littéraux (valeur tapée dans le code), le séparateur, c'est le point :

 
Sélectionnez
float mon float= 1.2;

III-J. Type Valeur et Type Référence

Un type de données est un type valeur s'il contient des données dans l'espace qui lui est alloué en mémoire. Un type référence contient une adresse vers un autre emplacement en mémoire contenant les données.

Type valeur :
La variable valeur contient directement la valeur de la variable.
Sont des variables par valeur :

les types numériques de base (int, double, décimal…) ;
les Booleans, Char, Date ;
les structures ;
les énumérations.


On remarque que les types numériques de base (int, double, decimal…) sont des structures (et non des classes) et donc bien par valeur.
On parle aussi de type primitif directement pris en charge par le compilateur et avec une syntaxe plus ou moins simplifiée.

Type référence :

La variable référence contient l'adresse où est située le contenu de la variable.
Sont des variables référence :

Les classes.
Les tableaux.
Les string.

Que se passe t-il si on affecte une variable à une autre ? Travaillons sur des variables type valeur (structure) puis référence (classe).

 
Sélectionnez
/////Variable par valeur

//Structure Adresse contenant un membre 'Ville'
struct Adresse
        {
           
            public string Ville;

        }
        
// variables de type Adresse
Adresse MonAdresseA= new Adresse();
MonAdresseA.Ville = "Paris";

Adresse MonAdresseB= new Adresse();
MonAdresseB.Ville = "Lyon";

//J'affecte la valeur de MonAdresseB à MonAdresseA
MonAdresseA= MonAdresseB;

MessageBox.Show(MonAdresseA.Ville);//Affiche "Lyon"

// Je modifie un membre de MonAdresseB ; MonAdresseA n'est pas modifiée
//car c'est une copie, et non le même objet
MonAdresseB.Ville = "Lille";
MessageBox.Show(MonAdresseA.Ville);//Affiche "Lyon"



//////Variable par référence
//Classe Personne ayant une propriété Nom
public class Personne
{
 private string _Nom;

 public string Nom
 {
     get
     {
         return _Nom;
     }
     set
     {
         if (value != _Nom)
         {
             _Nom = value;

         }
     }
 }
    }
    
//variables de type Personne, 
Personne A = new Personne();
A.Nom = "Dupont";

Personne B = new Personne();
B.Nom = "Durand";
            
A = B;      // A et B font référence à l'objet dont le nom= Durand
MessageBox.Show(A.Nom); //Affiche "Durand"
            

//je modifie B.Nom
B.Nom = "Jean";
//A.Nom et B.Nom ont même valeur .
MessageBox.Show(A.Nom); // Affiche "'Jean"

Les variables par valeur ou par référence ne réagissent pas de la même manière.
Si le type de variable est par valeur (valable pour les entiers, les longs… les structures…), chaque variable ayant sa valeur, si j'assigne la seconde à la première et que je modifie un membre de la seconde, le membre de la première n'est pas modifiée.
Si le type de variable est par référence (valable pour les tableaux, les classes, les string…), chaque variable est définie par sa référence (son lieu physique) ; assigner la seconde à la première fait qu'elles ont même référence : elles pointent sur le même objet. Si on modifie une propriété de la seconde, la propriété de la première est modifiée.

Que se passe t-il si on utilise == ? (Si l'opérateur == n'a pas été redéfini pour le type.)

Pour une variable par valeur, c'est le contenu, la valeur qui va être comparée.

Pour une variable par référence, c'est la réference qui va être comparée. Les objets concernés seront égaux s'ils pointent sur le même objet.

Comportement particulier des string :

Bien que string soit un type référence, les opérateurs d'égalité (== et !=) sont définis pour comparer les valeurs, pas les références. Le test d'égalité des chaînes est ainsi plus intuitif . Par exemple :

 
Sélectionnez
string a = "hello";
string b = "h";
b += "ello";
System.Diagnostics.Debug.WriteLine((a == b).ToString()); //Affiche true (comparaison des valeurs)
System.Diagnostics.Debug.WriteLine( ((object)a == (object)b).ToString());// Affiche false (comparaison des références)

En résumé :
(différence entre == et Equals.)

 
Sélectionnez
//Variable par valeur
            int int1 = 12;
            int int2 = 12;
            MessageBox.Show((int1 == int2).ToString());// true car compare les valeurs
            MessageBox.Show((int1.Equals(int2)).ToString());// true car compare les valeurs

//Variable par référence
            object o1= 12;
            object o2 = 12;
            MessageBox.Show((o1 == o2).ToString());// false car compare les références
            MessageBox.Show((o1.Equals( o2)).ToString());// true car compare les valeurs

//Variable string
            string s1 = "12";
            string s2 = "12";
            MessageBox.Show((s1 == s2).ToString());// true car compare les valeurs
            MessageBox.Show((s1.Equals(s2)).ToString());// true car compare les valeurs
            MessageBox.Show((object)s1 == (object)s2).ToString());// false car compare les références
            
            
//Variable objet contenant une string
            object ob1 = "12";
            object ob2 = "12";
            
            MessageBox.Show((ob1.Equals(ob2)).ToString());// true car compare les valeurs
            MessageBox.Show((ob1 == ob2).ToString());// true car compare les références et
            // des string littérales identiques dans le code font référence à la même instance de string
            
 //mais
 // Par contre, si une au moins des chaines n'est pas littérale mais construite dynamiquement,
 // même en ayant la même valeur, ce n'est plus la même référence
             object ob1 = "12";
            object ob2 = "123".Substring(0, 2); // c'est la même valeur, mais ce n'est plus la même instance de string
            MessageBox.Show((ob1 == ob2).ToString());// false car compare les références
            MessageBox.Show((ob1.Equals(ob2)).ToString());// true car compare les valeurs           
            
//puisque les variables sont de type object, le compilateur ne sait pas que ce sont des strings,
//et utilise l'opérateur == par défaut, qui compare les références

III-K. Bool, if, et , ou, switch, goto

Image non disponible

On a parfois besoin de savoir si une assertion est vraie ou fausse(true ou false).

Pour stocker une information de ce type, on utilise une variable de type booléen. Une variable de ce type ne peut contenir que true ou false.
Éventuellement on peut créer un bool Nullable, il pourra prendre aussi la valeur null.

Le terme booléen vient de l'algèbre de Boole, cette algèbre ne travaille que sur les valeurs 1 ou 0 (true ou false).


Soit myBoolean une variable booléenne :

 
Sélectionnez
bool myBoolean;

On peut écrire :

 
Sélectionnez
myBoolean = true;

On peut aussi tester cette variable :

 
Sélectionnez
if (myBoolean == false)
{ }

L'expression après if est évaluée, si elle est vraie ce qui est entre {} est exécuté.
On peut aussi écrire :

 
Sélectionnez
if (!myBoolean)
{ }


Autre exemple montrant comment le raisonnement informatique est logique :

 
Sélectionnez
if (maValeur==2)
{}

L'expression maValeur==2 est évaluée, si maValeur est effectivement égale à 2, l'expression prend la valeur true (un booléen) ; dans ce cas le programme se poursuit après {.

Condition :

On a vu qu'après un if il y avait une condition qui peut utiliser :

Égale :==

On note bien que si = est l'affectation, == est le signe d'égalité.

Différent :!=

Not :! Négation logique. Il est défini pour bool et retourne true si l'opérande est false et false si l'opérateur est true.

> , <, >=, <=

Exemple, si A différent de B :

 
Sélectionnez
if (A!=B)
{}

Il existe aussi les opérateurs logiques :

& ou && veut dire And (Et)
Les deux opérateurs font la même chose à la différence que l'on n'évalue pas la seconde valeur seulement si la première valeur est égale à false. On utilise toujours &&.

| ou|| veut dire Or (ou)
Les deux opérateurs font la même chose à la différence que l'on n'évalue pas la seconde valeur seulement si la première valeur est égale à true. On utilise toujours ||.

^ veut dire Xor (A ou B mais pas les 2).

Exemple : si A compris entre 100 et 300 : a>99 vrai et a<301 vrai

 
Sélectionnez
if (A>99 && A<301)
{
    B= 8;
}

&& évalue la seconde expression seulementsi la première est vraie

Comment mettre le code après if ?

Avec un bloc d'instruction entre {} :

 
Sélectionnez
if (A>99 && A<301)
{ 
    B=2;
    C= B+3;
}


Les accolades ne sont pas obligatoires, c'est bien pratique quand il y a une seule instruction après la condition  :

 
Sélectionnez
if (A>99 && A<301) B=2;

Mais attention :

 
Sélectionnez
if (condition)
    A= 2;
    B= 3;

L'indentation semble indiquer que B=3 est dans le bloc if, ce qui est faux.
Il est préférable de mettre des accolades.


Avec un else (sinon) exécuté seulement si la condition après if n'est pas égale à true :

 
Sélectionnez
if (A>99 && A<301) 
{ 
    B=2;
    C= B+3;
}
else
{ 
    B=8;
    C= B+9;
}


Avec un else if (sinon si), on peut tester des conditions successives :

 
Sélectionnez
if (A>99 && A<301) 
{ 
    B=2;
    C= B+3;
}
else if (A==1)
{ 
    B=8;
    C= B+9;
}


Il peut y avoir des if imbriqués. S'il n'y a pas d'accolades le else s'applique au if le plus proche ce qui peut entrainer des erreurs ; bien mettre des accolades :

 
Sélectionnez
if (A>99 && A<301) 
{ 
    if (B=2)
        {C= B+3;}
    else
        {C= B+5;}   
}
else
{ 
    B=8;
    C= B+9;
}

Une alternative au if else if else if est le switch.
On parle de commutateur.

 
Sélectionnez
switch (level)
{
    case 1:
    {
        // niveau 1
    }
    break;

    case 2:
    {
        // niveau 2
    }
    break;

    default:
    {
        // autre niveau
    }
    break;
}

Les accolades ne sont pas nécessaires.
Il faut bien mettre un break à la fin de chaque case sinon il y a erreur de compilation.
Default est exécuté si la valeur de la variable du switch ne correspond à aucun des cases.

Attention l'expression de switch (level ici) ou la valeur de case doit être bool, string, char, enum, integral, nullable. Un int ou un long, oui mais pas un float un double ou un decimal.

On peut faire des calculs sur une plage de valeur après case ; ici avec des long :

 
Sélectionnez
 long d = 2;
            switch (d)
            {
                case 3-1:
                    //case 2
                break;
                case 5:

                break;
            }

L'exécution commence à un case qui correspond à la valeur de la variable du switch et continue jusqu'à ce que l'instruction break transfère le contrôle en dehors du corps du switch. Un break un return ou un throw sont nécessaires après chaque bloc case, y compris le dernier bloc (case ou default).
Si plusieurs valeurs doivent exécuter les mêmes instructions faire comme cela (pas de break donc après les 2 premiers case) :

 
Sélectionnez
 int n = 2;
        switch(n) 
        {
            case 1:
            case 2: 
            case 3: 
                System.Diagnostics.Debug.WriteLine("C'est 1, 2, ou 3.");
                break; 
        default: 
            System.Diagnostics.Debug.WriteLine("C'est autre chose.");
            break; 
        }

Cas particulier, ? est un opérateur ternaire :
A ? B : C veut dire si A est vrai => B, si A est faux => C.

 
Sélectionnez
string IsEgale = (2 == 6) ? "les nombres sont égaux" : "les nombres ne sont pas égaux";

IsEgale= "les nombres ne sont pas égaux"

Il existe une instruction qui permet de sauter d'un endroit du code à un autre :c'est goto.
On saute vers un label indiqué par « : » :

 
Sélectionnez
int a= 2;
goto Suite;
a=a +1;
Suite:
a=a+2;

On peut se passer du goto dans la majorité des cas car il casse le code, le rend moins lisible.
Elle ne doit (quasiment) jamais être utilisée.

III-L. Boucle

Image non disponible

Comme son nom l'indique, une boucle permet d'effectuer plusieurs fois le code qui est dans la boucle.

Voyons la boucle for ; la boucle tourne en incrémentant un compteur tant qu'une condition est vraie :
Habituellement, on utilise donc une variable dite variable de boucle ; on lui donne une valeur de départ (initialisation) ; à chaque tour on l'incrémente (instruction de fin de boucle) ; quand elle atteint une valeur (condition), on quitte la boucle.

for (initialisation ; condition ;instruction de fin de boucle)
{instructions effectuées tant que condition=true}

Les trois arguments de for sont à l'intérieur d'une parenthèse et séparés par des points-virgules.
Chaque action du for est terminée par un point-virgule.
L'accolade n'est nécessaire que s'il y a plus d'une action.
L'accolade n'est pas suivie de point-virgule.

Exemple : faire tourner une boucle 10 fois (i, la variable de boucle prendra les valeurs 0, 1, 2 […] 8, 9.

 
Sélectionnez
for (int i=0; i<10; i++)
{
    System.Diagnostics.Debug.WriteLine(i.ToString());
}

On crée une variable de boucle i qu'on initialise à 0 , la boucle tourne tant que i est inférieur à 10, on incrémente i après chaque passage dans la boucle.
Pour chaque tour de boucle, on affiche la valeur de la variable de boucle sur la console.

On peut utiliser la variable de boucle DANS la boucle.
La variable de boucle n'existe que dans la boucle.

L'initialisation, la condition et l'instruction de fin de boucle sont facultatives.
Voici une boucle qui tourne sans arrêts :

 
Sélectionnez
for (; ; )
{
    // 
}

break; permet de sortir de la boucle.
continue; permet d'interrompre immédiatement l'itération en cours et de passer à la suivante.

On peut initialiser plusieurs variables (de même type), et avoir plusieurs instructions de fin de boucle. Le séparateur est l','.

 
Sélectionnez
 for (int i = 0 ,j=1; i < 10; i++ ,j--)           { }

Ici on a initialisé deux int, notez que le mot-clé int n'est présent qu'une fois. Ils n'existent que dans le corps de la boucle.

L'instruction foreach répète un groupe d'instructions pour chaque élément d'un tableau ou d'une collection.

 
Sélectionnez
 string[] arr= new string[] {"Jan", "Feb", "Mar"};

    foreach (string s in arr)
    {
        System.Diagnostics.Debug.WriteLine(s);
    }

On ne peut pas, dans la majorité des collections, ajouter ou retirer un élément dans une boucle foreach (contrairement à la boucle for).

Voyons la boucle while :
On ne connait pas le nombre de répétitions au départ. On boucle tant que la condition est vérifiée. La boucle peut ne jamais être exécutée.

 
Sélectionnez
while (b != 0)
{
x = a%b;
a = b;
b = x;
}

Tant que b différent de 0 on boucle.
On peut mettre un break dans la boucle pour sortir.

On peut aussi utiliser do while qui exécutera au moins une fois le contenu de la boucle car la condition est en fin de boucle.
On boucle jusqu'à ce que la condition devienne fausse.

 
Sélectionnez
do
{
// Code qui boucle tant que la condition est satisfaite.
}
while (condition);

III-M. Les génériques

Un type générique (Generic) permet de créer une classe ou une méthode, ayant des types non définis au départ.
Les génériques répondent au besoin de méthodes qui ne diffèrent que par le type de certains de leurs paramètres.
En d'autres termes, les paramètres et variables n'ont pas de type : ce ne sont pas des strings, des int… Ce sont des génériques. Quand on utilise la classe ou la procédure, on indique le type. Là le compilateur compile la méthode en tant que méthode générique, et c'est à l'exécution que la méthode "réelle" avec un type spécifique va être créée.. Les génériques nous permettent de définir un comportement ou un algorithme commun sur les types ou un sous ensemble de types .NET.

Par exemple, je vais écrire une fonction Swap avec des génériques.
Voir le chapitre pour les fonctions.
Cette fonction existait dans certains langages, elle permettait d'inverser la valeur de deux variables, elle sera utilisable avec des int, des singles.

Swap reçoit 2 paramètres par référence de type TItem (un généric donc). Par convention, les noms des paramètres de type génériques commencent toujours par T.

 
Sélectionnez
void Swap<TItem>( ref TItem v1,  ref TItem v2)
        {
            TItem temp = default(TItem);// instanciation d'une variable temp de type ItemType

            temp = v1;

            v1 = v2;
           
            v2 = temp;
            
            
        }


Comment utiliser la fonction Swap ?

 
Sélectionnez
// avec des entiers
            int var1 = 12;
            int var2 = 2;
            Swap< int>(ref var1, ref var2);
            MessageBox.Show(var1.ToString() + var2.ToString());
            
            
// avec des strings
            string c1 = "aa";
            string c2 = "bb";
            Swap<string>(ref c1, ref c2);
            MessageBox.Show(c1 + "  " + c2);

Depuis c# 3 et donc ici, on peut omettre <int> ou <string> (grâce à l'inférence de type).

 
Sélectionnez
Swap(ref var1, ref var2);

On a à notre disposition des collections génériques,
la collections List par exemple.

Déclarons la collection Col, c'est une collection typée (elle ne peut contenir que des strings) au départ elle est vide :

 
Sélectionnez
  List<string> Col = new List<string>();

Voir le chapitre sur les collections.

Pourquoi ne pas utiliser des types Object à la place des génériques ? Les génériques sont fortement typés. Si on crée une collection générique de long, on ne peut utiliser que des long : c'est mieux, cela évite les erreurs, les conversions de type. Ils sont plus rapides que l'usage des objets. S'il y a erreur, elle se produit probablement à la compilation et pas à l'exécution. Cela permet d'utiliser l'intellisense.

III-N. Les collections

Il existe deux manières de grouper des objets : avec des tableaux d'objets ou des collections d'objets.

Pour certaines collections (comme les listes, par exemple), le nombre d'éléments n'est pas défini au départ ; contrairement aux tableaux.
Les items affichés dans une ListBox donnent une idée concrète de ce qu'est une collection.

III-N-1. Liste générique

Une liste fonctionne comme un groupe d'éléments, une liste d'éléments dans laquelle il est possible d'ajouter ou d'enlever un élément à n'importe quel endroit sans avoir à se préoccuper de la taille de la collection, ni où se trouve l'élément.
Le nombre d'éléments n'est pas défini au départ. Dans une liste, il n'y a aucun élément au départ, puis il n'y a que les éléments que l'on a ajouté.

List<T> représente une liste fortement typée d'objets accessibles par index.

C'est juste une Liste , d'items. C'est une liste de génériques. Elle est de type référence.
On peut décider que ce sera exclusivement des chaînes de caractères, des int, des decimal ou tel type… Il n'y a pas de clé.
Il faut inclure le namespace suivant :

 
Sélectionnez
using System.Collection.Generic;



Déclarons la collection Col, c'est une collection typée (elle ne peut contenir que des strings) au départ elle est vide :

 
Sélectionnez
  List<string> Col = new List<string>();

J'ajoute des éléments (ou items) à cette collection.

 
Sélectionnez
Col.Add ("Toto");

Voici la collection :

Image non disponible

La collection a maintenant un élément (on dit un item en anglais).

Je fais maintenant :

 
Sélectionnez
Col.Add("Lulu");

Col.Add("Titi");

La collection a trois éléments maintenant.

Image non disponible

Il est aussi possible d'insérer un élément à une position particulière :

 
Sélectionnez
Col.Insert(2, "lulu");

On peut enlever un élément avec Remove :

 
Sélectionnez
Col.Remove("Lulu");
Image non disponible

La collection n'a plus que deux éléments maintenant.

On voit que le nombre d'éléments n'est pas connu à l'avance, il varie en fonction des éléments ajoutés (ou retirés).

On peut aussi enlever l'élément d'index 1 grâce à RemoveAt :

 
Sélectionnez
Col.RemoveAt(1);

On peut aussi enlever une plage d'éléments à partir d'index grâce à RemoveRange :

 
Sélectionnez
Col.RemoveRange(1,3); // on enlève 3 éléments à partir du deuxième.

Attention le premier élément est l'élément d'index 0.

Un élément est repéré par son indice ou index.

 
Sélectionnez
string element= Col[1]; //contient "Titi" (le second Item de la collection)

Pour parcourir une collection on peut utiliser foreach :

 
Sélectionnez
List<string> Col = new List<string>();
Col.Add ("Toto");
Col.Add("Lulu");
Col.Add("Titi");
            
            foreach (string prenom in Col)
            {
                MessageBox.Show (prenom);
            }

On se souviendra qu'avec foreach on peut parcourir mais pas modifier les éléments.
Pour modifier il faut faire une boucle for sur les index :

 
Sélectionnez
List<string> Col = new List<string>();
Col.Add ("Toto");
Col.Add("Lulu");
Col.Add("Titi");
            
            for (int index = 0; index < Col.Count; index++ )
            {
                Col[index] = Col[index] + ".";
                MessageBox.Show(Col [index]);
            }

On remarque qu'on a utilisé Count qui retourne le nombre d'éléments de la collection,
l'index allant de 0 à Count -1.

La List générique possède des méthodes propres aux collections génériques.

Contains permet de savoir si la list contient un élément :

 
Sélectionnez
bool ispresent = Col.Contains("toto");

IndexOf permet de chercher la position d'un élément dans une list (qui n'est pas obligatoirement triée, la recherche s'effectuant élément par élément).

 
Sélectionnez
int position= Col.IndexOf("lulu");// 'retourne l'index du premier'élément qui contient "lulu" 

int position2 =Col.IndexOf("lulu", 1,2);// recherche à partir de l'élément 1 et sur 2 éléments.

Si on veut rechercher successivement les "lulu", on utilisera comme deuxième paramètre, la position précédente+1.

Il existe aussi un LastIndexOf qui commence par la fin.

BinarySearch recherche aussi un élément mais sur une liste triée (recherche dichotomique) ; c'est plus rapide.

 
Sélectionnez
Col.Sort(); //Il est nécessaire que le tableau soit trié 

int i = Col.BinarySearch("lulu");


Revenons sur Sort() :
si on travaille sur une List d'int ou des strings, il y a une méthode de comparaison par défaut et le tri (QuickSort) s'effectue avec :

 
Sélectionnez
Col.Sort();


Par contre si on travaille sur une List d'objets de type ElementDeListe ayant trois propriétés nommées Nom, Numero, Texte ; il n'y a pas de comparateur par défaut.
Il faut le créer : ici on va trier sur le champ Nom.

 
Sélectionnez
static int CompareElements(ElementDeListe x, ElementDeListe y)
        {
            return string.Compare(x.Nom, y.Nom);
              
        }

On a créé une fonction de comparaison ayant deux ElementDeListe comme paramètres (ceux à comparer).
La fonction retourne 1, 0 -1 si x supérieur, égal ou inférieur à y.
Pour faire le tri :

 
Sélectionnez
Col.Sort(CompareElements);

RemoveAll :
Permet de supprimer de la collection les éléments correspondant à une condition; retourne le nombre d'éléments supprimés.

 
Sélectionnez
int nombreDElementsSupprimés = nombres.RemoveAll(n => n % 2 != 0);

On peut créer une List à partir d'un tableau grâce à :

 
Sélectionnez
string[] input = { "Toto", 
                    "Lulu", 
                    "Riri" };

List<string> MyFriends = new List<string>(input);

On peut aussi créer un tableau à partir d'une liste :

 
Sélectionnez
string[] output = MyFriends.ToArray();

Il est possible d'avoir un sous-ensemble d'une liste :

 
Sélectionnez
List<string> Listoutput = MyFriends.GetRange(2, 3);

Il existe aussi RemoveRange, AddRange, InsertRange.

Au passage, pour enlever tous les éléments d'une collection :

 
Sélectionnez
MyFriends.Clear();

On peut aussi utiliser les méthodes d'extension. Elles permettent d'ajouter à des types existants, comme les collections, des méthodes (de LINQ, par exemple) : Min, Max, Sum, Average, Last, First, All, Any, Count,Select, Join, OrderBy, GroupBy, Where…

Il faut inclure l'espace de noms System.Linq.

Max
Quel est l'élément le plus grand (Max) d'une liste :

 
Sélectionnez
List<int> nombres = new List<int> { 2, 3, 4, 5, 6, 7, 8, 9, 0 };
int a = nombres.Max();
MessageBox.Show (a.ToString());

Select
Chaque élément est passé individuellement à une fonction qui le transforme en autre chose et les éléments convertis sont enregistrés, grâce à ToList, dans une nouvelle collection.

 
Sélectionnez
List<string> strings = nombres.Select(n => n.ToString()).ToList();

Ici chaque élément de nombres est converti en string.

Any
List contient-il des éléments qui correspondent aux conditions définies par un prédicat (ici n>=10).

 
Sélectionnez
bool trouve = nombres.Any(n => n >= 10);

First, Where
Recherche un élément qui correspond aux conditions définies par le prédicat et retourne la première ou toutes les occurrences.
Ici on cherche les nombres paires (nombre modulo 2 =0).

 
Sélectionnez
int premierElement = nombres.First(n => n % 2 == 0);
List<int> listresult = nombres.Where(n => n % 2 == 0).ToList();

La classe de base pour List et toutes les collections génériques est Collection.

III-N-2. Dictionary

Dictionary est un dictionnaire contenant des couples clé-valeur typés (génériques). Chaque ajout au dictionnaire se compose d'une valeur et de sa clé associée.

Image non disponible

La clé toujours unique (pas de doublon) permet de retrouver la valeur, elle ne doit pas être vide.
La valeur peut être nulle.

Créons un Dictionary avec des clés de type string et des valeurs de type int.

 
Sélectionnez
Dictionary<int, string> days = new Dictionary<int, String> { { "Sunday", 0 }, { "Monday", 1 } };

Ici on a ajouté des éléments dès la déclaration.

Ajout d'éléments :

 
Sélectionnez
days.Add("Tuesday", 2);
days.Add("Tuesday", 2); // déclenche une exception car la clé 'Tuesday' est déjà dans le Dictionary


On peut tester si la clé existe avant de faire Add.
Si elle n'existe pas (days.ContainsKey("Tuesday") est égal à false), ajouter.

 
Sélectionnez
if (!days.ContainsKey("Tuesday))
            { days.Add("Tuesday", 2); }


Pour lire la valeur correspondant à la clé Monday.

 
Sélectionnez
string mavaleur = days["Tuesday"];

Attention, si la clé n'existe pas on a une exception de type KeyNotFoundException et l'application s'arrête.
Il faut mieux utiliser TryGetValue qui retourne true si la clé a été trouvée et met la valeur après out :

 
Sélectionnez
string value;
            if (days.TryGetValue("Monday", out value))
            {
                // value contient la valeur correspondant à la clé 'Monday'
            }
            else
            {
               // pas de valeur pour la clé 'Monday'
            }

Ici on a testé s'il y avait une valeur correspondant à la clé.
TryGetValue permet donc de rechercher une valeur correspondant à une clé, retourne false si la clé n'existe pas (sans déclencher d'erreur).


Parcourir un Dictionary (le type de clé/Value est le type KeyValuePair).

 
Sélectionnez
foreach (KeyValuePair<string, int> day in days)
            {
                string maday = day.Value;
            }

Enlever un élément :

 
Sélectionnez
days.Remove("Monday");

On peut utiliser toutes les valeurs qui sont dans la ValueCollection du Dictionary.

 
Sélectionnez
Dictionary<string, int>.ValueCollection valueColl =
          days.Values;

      // La ValueCollection est typée (ici c'est une collection de int)
      foreach (int s in valueColl)
      {
        System.Diagnostics.Debug.WriteLine( s.ToString());
      }

Il y a aussi keys qui contient toutes les clés.

III-N-3. LinkedList

Ce sont des Listes chaînées de génériques, chaque élément (node ou nœud) comportant une propriété Value (qui contient la valeur de l'élément) et les propriétés Next et Previous permettant d'atteindre les éléments suivant et précédent.


Ici on va créer des nœuds et tester s'il y a un nœud avant ou après le nœud :

 
Sélectionnez
      // Creation d'une LinkedList de string.
      LinkedList<String> ll = new LinkedList<String>();
            
            
      //Creation d'un noeud nommé lln contenant "orange"
      LinkedListNode<String> lln = new LinkedListNode<String>("orange");
            
      // la valeur dans le node est dans lln.Value

      // Ajouter le noeud lln à la liste ll.
      ll.AddLast(lln);
           
      // Ajouter un noeud avant et après le noeud "orange" 
      ll.AddFirst("red");
      ll.AddLast("yellow");
         
      //lln.List retourne la list a laquelle appartient le noeud      
      //lln.List.Count () nombre de noeuds dans la liste où est le noeud lln
       if ( lln.List == null )
         System.Diagnostics.Debug.WriteLine( " Le Noeud n'est pas lié." );
      else
         System.Diagnostics.Debug.WriteLine( " le Noeud appartient à une liste reliée avec {0} éléments.", lln.List.Count );
      
      //Y a t-il un node avant?
      if ( lln.Previous == null )
      {
         //pas de noeud avant
         }
      else
      {
          System.Diagnostics.Debug.WriteLine (lln.Previous.Value.ToString());
        }
        
       //Valeur du noeud courant 
       string valeutConrante = lln.Value; 

      //Y a t-il un noeud après
      if ( lln.Next != null )
       {//Il y a un noeud après
        System.Diagnostics.Debug.WriteLine( "   Valeur du noeud suivant:     {0}", lln.Next.Value );       
       }

On note que pour ajouter un élément on a AddFirst, AddLast, AddBefore, AddAfter.

Pour effacer un élément on a RemoveFirst, RemoveLast, Remove.

On peut chercher un élément avec Find, First, Last, FindLast.

Autre exemple : on initialise la LinkedList avec un tableau puis on travaille sur la liste à partir de l'élément courant.

 
Sélectionnez
// Déclaration d'un tableau de string
string[] words = {"the", "fox", "jumped", "over", "the", "dog"};        
                
//Déclaration d'une LinkedList qu'on 'chargera' avec le tableau
LinkedList<string> ListWords = new LinkedList<String> (words);
    
//Déclaration d'un noeud nommé 'current' correspondant à "the"
LinkedListNode<String> current = ListWords.FindLast("the");

 
//À partir de l'élément current, on peut ajouter un noeud avant ou après.

ListWords.AddAfter(current, "old");  //il y a aussi AddBefore

//À partir de l'élément courant, on peut parcourir le précédent ou le suivant

 LinkedListNode<String> element = current.Previous ; //il y a aussi Next

//On peut déplacer un élément

 ListWords.AddBefore(current, element);


On peut voir la valeur du premier ou du dernier élément : ListWords.First.Value ListWors.Last.Value

Il existe aussi Contains, Count.

III-N-4. Queue

Collection générique de type FIFO (First In, First Out),

premier arrivé premier sorti.

C'est la queue devant un cinéma, le premier arrivé, prend son billet et sort le premier.

Les objets (string, int… ) stockés dans Queue sont insérés à une extrémité et supprimés à l'autre.

Image non disponible

DeQueue retourne et supprime l'élément de début de liste ; il sort l'élément de la liste.

EnQueue ajoute un élément en fin de liste.

Peek retourne l'élément de début sans le supprimer.

 
Sélectionnez
Queue<int> myQ = new Queue<int> ();
myQ.Enqueue("One");
myQ.Enqueue("Two");
myQ.Enqueue("Three");

myQ.Count Affiche le nombre d'éléments.

Dequeue sort l'élément de début de liste :

 
Sélectionnez
MessageBox.Show (myQ.Dequeue());

Affiche le premier sorti en le sortant, "one" dans notre exemple. S'il n'y a plus d'élément cela lève une exception (une erreur) il faut donc gérer l'exception ou contrôler le nombre d'éléments avec la propriété Count.

 
Sélectionnez
int element= myQ.Peek();

Lit le premier élément sans l'enlever de la Queue.

 
Sélectionnez
myQ.Clear();

Efface tous les éléments de la queue.

III-N-5. Stack

Collection générique de type pile (ou stack) LIFO (Last In, First Out), dernier entré, premier sorti.

Ce type de stack (pile) est très utilisé en interne par les programmes informatiques : on stocke dans une stack les adresses de retour des fonctions appelées, au retour on récupère l'adresse du dessus.

Push insère un objet en haut de la pile.

Pop enlève et retourne un objet en haut de la pile.

Image non disponible

On peut utiliser une pile dans un programme pour gérer le déplacement de l'utilisateur dans un arbre, les éléments en cours sont stockés par Push, pour remonter en chemin inverse, on Pop.

Exemple :

 
Sélectionnez
Stack<int> LaPile = new Stack<int> () ;

Ajouter un élément en haut de la pile :

 
Sélectionnez
 LaPile.Push(2);


Enlever un élément en haut de la pile :

 
Sélectionnez
 int num = LaPile.Pop();


On peut aussi lire l'élément en haut sans l'enlever.

 
Sélectionnez
 int a = LaPile.Peek();


LaPile.Clear(); 'Supprime tous les éléments.

On peut utiliser les méthodes d'extension : Min, Max, Sum, Average ; un exemple, somme de tous les éléments de la pile :

 
Sélectionnez
 LaPile.Push(6);
 LaPile.Push(2);
 int a = LaPile.Sum();

III-N-6. BitArray

Crée une collection de booléens (codés sur un bit). La valeur de chaque élément est true ou false.

III-N-7. ObservableCollection

Il arrive souvent de lier (binder) une collection List à une ListBox par exemple, ce qui fait que les éléments de la liste sont affichés dans la ListBox automatiquement.
Si on ajoute un élément dans la liste avec le code suivant :

 
Sélectionnez
 MaListe.Add(new ElementDeListe("kg-gr", "kg", "C"));

La ListBox n'est pas mise à jour car la liste n'implémente pas INotifyCollectionChanged qui permet d'envoyer des évènements sur l'ajout ou la suppression d'un élément dans une liste.

Pour que l'ajout ou la suppression l'élément dans la collection soient pris en compte par la ListBox, il faut utiliser une ObservableCollection.

Une ObservableCollection représente donc une collection de données génériques qui fournit des notifications lorsque des éléments sont ajoutés, supprimés, ou lorsque la liste entière est actualisée.

Il faut inclure l'espace de noms suivant :

 
Sélectionnez
using System.Collections.ObjectModel;

Ensuite on peut déclarer une ObservableCollection.

 
Sélectionnez
ObservableCollection<string> ob =new ObservableCollection<string>();

Attention, si on modifie un élément de la collection, la modification n'est pas reportée sur la ListBox car les éléments eux-même n'implémentent pas INotifyPropertyChanged. Si on veut que la modification de la valeur d'une propriété d'un élément soit reportée dans la ListBox, il faut donc implémentent INotifyPropertyChanged sur les éléments (voir le chapitre sur le binding).
Les propriétés et méthodes sont les mêmes que pour les List. Il n'y a pas Sort, Reverse, BinarySearch, RemoveAll, RemoveRange, AddRange, FindAll, et pas mal d'autres.

III-N-8. Généralisation sur la notion de collection

Certains objets ont une liste de données, d'items de propriétés, le framework les organise en Collections.

Une collection peut donc faire partie des propriétés d'un objet.

Exemple : les contrôles ListBox possèdent une collection Items dans laquelle sont placés tous les éléments contenus dans la liste. Pour ajouter un élément on utilise la méthode Add de la collection Items :

 
Sélectionnez
ListBox.Items.Add( );

Un tas d'objets possèdent des collections.

Sur les collections, on peut utiliser les méthodes d'extension de Linq (voir le chapitre sur Linq).

III-O. Les énumérations

Enumérations simples.

Les énumérations sont utilisées lorsque l'on a un jeu de constantes liées logiquement.
Un bloc enum permet de créer une liste (une énumération) de constantes, le bloc commence par enum.

 
Sélectionnez
enum Saison
{  printemps,
   été,
   automne,
   hiver
}

Cela crée les constantes suivantes :
Saison.printemps ;
Saison.été ;
Saison.automne ;
Saison.hiver.

Il s'agit d'un ensemble de constantes nommées.

Généralement on définit l'énumération au niveau de l'espace de nom ou de la classe, ainsi on peut l'utiliser partout dans l'espace de nom ou la classe.
Le « ; » semble facultatif.

 
Sélectionnez
// Au niveau  espace de nom
namespace compare
{
    enum Mentions { Passable, AssezBien, Bien, TrèsBien, Excellent };
    public partial class MainPage : PhoneApplicationPage
    {

//Au niveau classe
public partial class MainPage : PhoneApplicationPage
    {
        enum Saison { printemps, été, automne, hivers };


Il est ensuite possible d'utiliser une variable de type Saison et d'y mettre été :

 
Sélectionnez
Saison SaisonEnCours;
SaisonEnCours= Saison.été;
if (SaisonEnCours== Saison.hiver)
{
}

Une variable de type enum est une variable par valeur.

En interne à chaque valeur nommée est associée une valeur numérique ; par défaut sont utilisées les int : 0, 1, ,2…
Par défaut :

Saison.printemps = 0
Saison.été = 1
Saison.automne = 2
Saison.hiver =3


Mais on peut imposer une valeur différente :

 
Sélectionnez
 enum Couleurs { Blanc=3, Rouge=8, Vert=45 };


Ensuite on peut afficher la valeur nommée (grâce à ToString), la valeur numérique (on cast en int puis on utilise ToString) ou récupérer la valeur nommée à partir de la valeur numérique(avec GetName) :

 
Sélectionnez
Couleurs maCouleur= Couleurs.Rouge;
           
MessageBox.Show(maCouleur.ToString());  //Affiche 'Rouge'.
MessageBox.Show(( (int) maCouleur).ToString());//Affiche '8'.
MessageBox.Show(Enum.GetName(typeof(Couleurs), 8));//Affiche 'Rouge' qui correspond à la valeur '8'.

Il est possible d'imposer un type différent :
Ici les valeurs seront des longs.

 
Sélectionnez
 enum Couleurs : long { Blanc=3L, Rouge=8L, Vert=45L };

Notez que les valeurs données doivent être des longs (on a ajouté L aux valeurs).

Enumérations d'indicateur.

A utiliser lorsque plusieurs valeurs d'énumération peuvent être spécifiées en même temps.
On utilise d'attribut [Flags].
Les valeurs sont des puissances de 2. Elles peuvent être combinées avec l'opérateur de bit Or.
Voici une énumération Fichier qui permet d'indiquer si un fichier est en lecture, écriture, les deux et s'il est ouvert ou non.

 
Sélectionnez
[Flags]
public enum Fichier
        {
            None=0,
            Read  = 0x01,
            Write = 0x02,
            Open  = 0x04
        }

Le premier bit indique Read, le deuxième Write et le troisième Open.

 
Sélectionnez
// On instancie MonFichier
// On donne 2 valeurs: Read et Write grâce à Or
Fichier MonFichier= Fichier.Read | Fichier.Write;

        
            MessageBox.Show(MonFichier.ToString());//Affiche 'Read,Write'
           
            MessageBox.Show(((int)MonFichier).ToString());//Affiche '3'  1+2 (011 en binaire)


            //Ajouter Open grâce à Or
            //MonFichier = MonFichier | Fichier.Open;

            MessageBox.Show(MonFichier.ToString());//Affiche 'Read,Write,Open'

            MessageBox.Show(((int)MonFichier).ToString());//Affiche '7'  1+2+4 (111 en binaire)

            // Enlever Write grâce à Xor, il force un bit à zéro
            MonFichier = MonFichier ^ Fichier.Write ;

            MessageBox.Show(MonFichier.ToString());//Affiche 'Read,Open'

            MessageBox.Show(((int)MonFichier).ToString());//Affiche '5'  1+4 (101 en binaire)

            // Tester grâce à And
            bool test= (MonFichier & Fichier.Open) == Fichier.Open;
            if (test==true)
            {
                MessageBox.Show("Est ouvert");
            }


On a à notre disposition des énumérations fournies par le framework.


Les touches :

 
Sélectionnez
 Key MyKey = Key.Enter;

III-P. Les structures

En règle générale, une structure est utilisée comme conteneur pour un petit jeu de variables permettant de regrouper des données de type différent.

Les structures sont intéressantes quand vous voulez utiliser des variables contenant plusieurs informations de différents types. Les structures sont une alternative aux classes.

Exemple : vous voulez définir une variable contenant une adresse composée d'un numéro, de la rue, de la ville.

Il faut d'abord définir la structure (au niveau module ou Class, pas dans une procédure).

 
Sélectionnez
struct Adresse
{
   public int  Numero;  

   public string Rue;

   public string Ville;

}


Puis on peut déclarer une variable de type structure :

 
Sélectionnez
Adresse MonAdresse;

On aurait pu utiliser new.

 
Sélectionnez
Adresse MonAdresse = new Adresse();

Si on n'utilise pas new, les champs ne seront pas assignés et l'objet ne pourra pas être utilisé tant que tous les champs n'auront pas été initialisés.

La variable MonAdresse est déclarée comme une Adresse, elle contient donc :

un numéro qui est dans MonAdresse.Numero ;

un nom de rue qui est dans MonAdresse.Rue ;

un nom de ville qui est dans MonAdresse.Ville.


On pourra enfin l'utiliser :

 
Sélectionnez
MonAdresse.Numero=2;

MonAdresse.Rue= "Grande rue";

MonAdresse.Ville= "Lyon";


Il peut y avoir des constructeurs et des fonctions dans une structure.

 
Sélectionnez
struct Adresse
{
   public int  Numero;  

   public string Rue;

   public string Ville;
   
   //Constructeur
   public Adresse(int s1, string s2, string s3)
    {
        Numero = s1;
        Rue = s2;
        Ville = s3;
    }

}

Ce qui permet d'instancier de cette manière :

 
Sélectionnez
Adresse MonAdresse = new Adresse(12, "grande rue", "Lyon");

Quelles différences entre structure et classe ?
- Une variable d'un type structure est de type valeur et contient directement les données de la structure, alors qu'une instance de classe contient une référence aux données.

Cela a de l'importance : si je crée une variable avec une structure, que je copie cette variable dans une seconde, le fait de modifier une propriété de la première variable ne modifie pas la propriété de la seconde.

- Une structure ne peut pas hériter d'une autre structure ou classe ni servir de base à une autre structure.


On utilise une structure quand elle est limitée à quelques octets de données et qu'on n'a pas besoin de l'héritage.

En pratique on met rarement de membres de type référence dans une structure, car cela fait que la structure ne correspond plus vraiment à la sémantique de valeur que les structures ont habituellement. C'est acceptable si le type référence en question est immuable (comme string), sinon il vaut mieux éviter.


Remarquons que les types numériques de base (int, double, decimal…) sont de type structure et non classe. Cela explique que ces types sont des types valeur et qu''on ne peut pas créer une classe qui hérite de ces types.

III-Q. Les fonctions, procédures, méthodes

Une fonction est un ensemble d'instructions réalisant une certaine tâche et retournant un résultat.
Une procédure est une fonction qui ne renvoie pas de résultat. En programmation orientée objet, une fonction membre d'un objet est désignée par le terme de méthode.

Une fonction est une suite d'instructions regroupées sous un nom.
Elle a en entrée des paramètres et retourne un résultat.
Pour l'utiliser on l'appelle par son nom en ajoutant les paramètres, elle retourne un résultat :

 
Sélectionnez
int Multiplier(int x, int y)
{
    return x*y;
}

Ici on a une fonction nommée Multiplier.
On a deux paramètres (ou arguments) de type int.(int x, int y).
Elle retourne un int (le int avant le nom de la fonction l'indique).
Dans le corps de la fonction (ce qui est entre { et }), on calcule x*y. La fonction retourne le produit ( return indique ce que doit retourner la fonction).

Pour utiliser la fonction :

 
Sélectionnez
int produit= Multiplier (2,3);

Après la ligne précédente produit sera égal à 6.

On peut bien sûr avoir des variables en paramètres d'appel :

 
Sélectionnez
int a= 12;
int b= 2;
int produit= Multiplier (a,b);

Les paramètres d'appel et ceux de la fonction doivent être de même type (on appelle la fonction avec 2 int, la fonction doit attendre 2 int).
Ou bien, il doit exister une conversion implicite du type de l'argument (i.e. la valeur passée à la fonction) vers le type du paramètre. Par exemple on peut passer un int à une méthode qui attend un double, car il y a une conversion implicite ; de même, une variable qui attend un object peut recevoir une valeur de n'importe quel type, puisque tous les types héritent de object.
Par contre les noms des paramètres peuvent être différents (a et b pour l'appel ; x et y dans la fonction).
La valeur de a est passée dans x, celle de b dans y.
On parle de passage de paramètre par valeur, c'est le passage par défaut.


Il existe un autre mode de passage de paramètres : par référence indiqué par ref.
Là ce n'est pas la valeur mais la référence (l'adresse en mémoire) qui est passée ; si la fonction modifie la valeur du paramètre, la variable d'appel est aussi modifiée.

 
Sélectionnez
void Doubler(ref int x,ref int y)
{
    x= x*x;
    y=y*y;
}

Pour l'appel il faut aussi utiliser ref :

 
Sélectionnez
int a= 12;
int b= 2;
Doubler (ref a, ref b);

Il peut arriver qu'un paramètre serve uniquement à récupérer une valeur initialisée par la fonction, dans ce cas on utilise out.
'out' indique une variable de sortie qui est déclarée mais peut ne pas être initialisée avant l'appel.

 
Sélectionnez
void initialise(out int y)
{
    y=5;
}

Comment l'utiliser ?

 
Sélectionnez
int a; //Variable juste déclarée, n'ayant pas de valeur
initialise ( out a);  // a sera égal à 5

Si la fonction ne retourne rien, on utilise void (vide) avant le nom de la fonction, on devra l'appeler procédure.

 
Sélectionnez
void Affiche(int x)
{
    MessageBox.Show ( x.ToString());
}

On peut surcharger une fonction :

La surcharge permet de créer des fonctions qui ont le même nom mais des paramètres différents (on parle de signatures différentes).
Pour avoir une signature différente il faut modifier le nombre de paramètres ou type d'un paramètre :

 
Sélectionnez
int Multiplier(int x, int y)
{
    return x*y;
}

double Multiplier(double x, double y)
{
    return x*y;
}

Ici on a une fonction nommée Multiplier qui a deux signatures, elle accepte des int ou des double.
C'est transparent ; quand on appelle la fonction avec des paramètres double c'est la seconde signature qui fonctionne.

Même si la fonction ou procédure n'a pas de paramètre, il faut mettre « () » pour l'appel.

Pourquoi utiliser des fonctions et des procédures ?
Pour découper un code complexe en fonction plus simple.
Si un code est utilisé plusieurs fois, on le met dans une fonction qu'il suffit d'appeler.

III-R. Erreurs et interception des erreurs

Dans une application, des erreurs peuvent survenir (on dit des exceptions car théoriquement elles surviennent exceptionnellement !) : division par zéro d'un entier, utilisation d'un index de tableau qui n'existe pas…
L'application plante et s'arrête !


Si on lance l'exécution avec le débogueur (F5) en mode Debug ou Release, lorsque survient une exception, l'émulateur s'arrête et le débogueur affiche un message indiquant l'exception.

Image non disponible

On peut modifier le comportement en passant par le menu Déboguer puis Options et paramètres :

Image non disponible

'Activer l'Assistant Exception' ouvre, en cas d'exception, une nouvelle fenêtre plus détaillée (c'est l'option par défaut) à la place de l'ancienne boîte de dialogue.

Vous pouvez modifier le comportement du débogueur vis à vis d'une exception en passant par le menu Déboguer puis Exceptions.

Image non disponible

Si on coche "levé", le débogueur s'arrête dès que l'exception est levée, même si elle est interceptée plus tard. Si on coche seulement "non gérée par l'utilisateur", le débogueur s'arrêtera quand l'exception est levée, sauf s'il y a un bloc catch dans le code qui la gère.

Vous pouvez lancer l'exécution sans le débogueur (Ctrl+F5) :

Image non disponible


Dans ce cas dans l'émulateur, s'il y a une exception, l'exécution s'arrête et l'application en cours s'arrête, on la quitte.
Ce comportement sera le même dans votre Windows Phone.

Il faut éviter cela.
Notons que WP est tolérant : si le code indique comme source d'une image un fichier .jpg qui n'existe pas, il n'y a pas de levée d'exception et l'application se poursuit.


En théorie, on peut parfois prévoir l'exception et l'éviter : tester par exemple le dénominateur d'une division et effectuer la division seulement si ce dénominateur n'est pas égal à 0.
Mais souvent cela n'est pas possible ; l'autre solution est de capter l'erreur.

Pour capter une erreur qui déclenche une exception et provoque un arrêt du programme, on utilise try catch.
Le bloc try ('essayer') contient le code protégé (entre { et }) risquant de provoquer l'exception. Y compris le code dans les méthodes appelées.
La clause catch intercepte l'exception si elle se produit.

Autrement dit, s'il y a une exception dans le bloc try, c'est le bloc catch qui est exécuté avec comme paramètre, si nécessaire, un objet de type Exception ou dérivé. Notons qu'il doit y avoir des accolades dans le bloc catch.

Catch peut contenir :
- rien : l'erreur est ignorée (c'est à éviter) ;
- du code avertissant l'utilisateur qu'il y a un problème ;
- du code résolvant le problème.

Catch peut être utilisé sans arguments ; dans ce cas, il intercepte tout type d'exception et est appelé la clause catch générale (c'est généralement à éviter. Autant que possible, il faut indiquer le type précis d'exception qu'on s'attend à recevoir). Il peut également prendre un argument de type System.Exception ou dérivé. Dans ce cas, il gère une exception spécifique.


Exemple : on tente de lire un élément de tableau avec un index trop grand.

 
Sélectionnez
monElement= array[index];

Si le tableau contient trois éléments et que index=6, il y a levée de l'exception IndexOutOfRangeException et le programme s'arrête.

Comment éviter cela : on peut faire simple avec un catch sans argument qui s'exécutera pour tous les types d'erreur:

 
Sélectionnez
try
        {
            monElement= array[index];
        }
catch 
        {
            MessageBox.Show ("Erreur index");
        }


On peut donner un argument à catch :

 
Sélectionnez
try
        {
            monElement= array[index];
        }
catch (System.IndexOutOfRangeException e) 
        {
            MessageBox.Show (e.Message);
        }

On remarque qu'après chaque catch, on a le paramètre « e » de type Exception ou d'un type plus spécifique :
ArithmeticException ;
IndexOutOfRangeException ;
DivideByZeroException.

« e » possède des propriétés comme e.Message qui contient le message de l'erreur.


On peut mettre plusieurs catch spécifiques pour chaque type d'erreurs. On peut terminer par un catch général qui captera toutes les autres exceptions.

 
Sélectionnez
 try
            {
                string s = null;
               
            }
catch (ArgumentNullException ex )
            {
                MessageBox.Show( ex.Message);
            }
catch (Exception ex )
            {
                MessageBox.Show( ex.Message);
            }


Attention : dès qu'une erreur est traitée par un catch, les autres catch sont ignorés. On note donc l'importance de l'ordre des catch : le dernier catch est le plus général et ne sera exécuté que si les autres ne le sont pas.
Autant que possible, il faut indiquer le type précis d'exception qu'on s'attend à recevoir et eviter les catch vides.


Parfois on a besoin d'intercepter l'erreur puis de la déclencher à nouveau ; dans ce cas on utilise throw qui force la levée d'une exception :

 
Sélectionnez
 try
        {
            return array[index];
        }
catch (System.IndexOutOfRangeException e) 
        {
            MessageBox.Show (e.Message);
         //
          throw   new System.ArgumentOuthrowtOfRangeException("index parameter is out of range.", e);
        }
finally
        {
        }

Le second argument (e) permet de transmettre l'instance de l'exception qui avait déclenché le catch. Ainsi dans la nouvelle exception InnerException indique l'erreur d'origine.
Ce second argument est facultatif.

"throw" tout court, permet de relancer la même exception après avoir fait un traitement.


On peut ajouter finally qui sera toujours exécutée.

Il y a souvent des erreurs dans les conversions (string vers int par exemple).
Pour éviter cela, sans utiliser de try catch, on peut utiliser tryparse qui fait la conversion mais ne déclenche pas d'exception si la conversion est impossible.

 
Sélectionnez
string a="12";
int b;
           bool res =  int.TryParse(a, out b);
           // si la conversion echoue res=false
           //C'est une méthode de classe int.tryparse, double.trypars..
           //retour du resultat dans b

III-S. Linq

C'est un langage de requêtes (permettant d'interroger une source de données) utilisable directement dans le code C# et à l'aide de mots clés familiers (issus du SQL, le langage d'interrogation des bases de données).
Cette source de données peut être une base de données, un fichier XML (Linq To XML) mais aussi une collection, un tableau, une chaîne de caractères (Linq To Objet).
Si un objet prend en charge l'interface IEnumerable, le fournisseur LINQ to Object vous permet de l'interroger.

LINQ (dixit Microsoft) offre trois principaux avantages par rapport aux boucles for Each traditionnelles.
Les requêtes:
- sont plus concises et lisibles, surtout lors du filtrage de plusieurs conditions ;
- fournissent des fonctions puissantes de filtrage, de classement et de regroupement avec un minimum de code ;
- peuvent être appliquées à d'autres sources de données avec peu ou pas de changement.

Il faut ajouter l'espace de noms System.Linq :

 
Sélectionnez
using System.Linq;

Principe :
À titre d'exemple simpliste, on a des données dans MyData et chaque donnée a les champs Nom, Prenom, Ville… Comment chercher les enregistrements ayant comme nom "toto" ?

 
Sélectionnez
var Resultat = from Element in MyData 

where Element.Nom ==n "Toto" 

select Element ;

On crée une variable de requête (ici var Resultat) qui sera chargée de contenir la requête (et pas les résultats), on laisse le soin au compilateur de choisir le type de variable (var qui prend implicitement le type générique IEnumerable de l'expression qu'on lui affecte, il ne s'agit pas de type dynamique, mais seulement de typage implicite).


Puis l'expression de requête composée de from : dans quoi chercher ? Dans quel élément ?
Ici on laisse aussi le soin au compilateur de choisir le type de la variable (il est également possible de préciser le type).


in : quelle source de données ? Dans MyData.


where : précise les conditions à appliquer, c'est le filtre.


select : précise les éléments à extraire qui vont apparaître dans Resultat


Remarquons donc qu'initialement on connaît MyData et on sait que chaque élément de MyData a un champ Nom, c'est tout ! On utilise dans la requête les nouvelles variables Resultat et Element sans avoir à déclarer leur type (on aurait pu le faire). Element est une variable de portée réduite comme élément de MyData.

Et pour afficher les noms dans une ListBox :

 
Sélectionnez
foreach (P in Resultat)
   ListBox.Items.Add(P.Nom);

Ici la requête contenue dans la variable de requête Resultat est exécutée pour alimenter la boucle For Each. On remarque que l'exécution est différée. La requête n'est évaluée (exécutée) que quand on commence à parcourir le résultat.

La clause Where peut contenir des conditions complexes avec des et des ou.

Linq et les tableaux d'int :
un tableau peut être interrogé par Linq.
Exemple, rechercher les nombres pairs dans un tableau d'int :

 
Sélectionnez
// La Data source: c'est un tableau d'Integer 

int[] numbers =  {0, 1, 2, 3, 4, 5, 6};

 

// Création de la requête.

//Pour chaque élément num de la source

//Si l'élément num est tel que num % 2=0 (num modulo 2, condition pour qu'il soit pair)

//Selectionner num et le mettre dans réponses

 var réponses = from num in numbers 

                 where num % 2 == 0 

                 select num ;

 

// Exécution de la requête. 

// On utilise les réponses

foreach (var number in réponses) 

    listBox1.Items.Add(number);

Cela affiche dans la ListBox : 0 2 4 6

On peut vouloir compter uniquement les nombres pairs :

 
Sélectionnez
 var réponse = from num in numbers 

                 where num % 2 == 0 

                 select num ;
                 
                 
 textBlock1.Text = réponse.Count().ToString();

Affiche 4.
Ici on exécute la méthode Count() qui retourne le nombre d'élément.

On aurait pu utiliser .Min() , .Max(), .Average() pour obtenir l'élément le plus petit, le plus grand ou la moyenne des éléments .

Linq et les caractères d'une string : une string peut être interrogée par Linq.
Exemple, rechercher les caractères qui sont des chiffres dans une string :

 
Sélectionnez
string MyString = "ABCjkjhkhs666KMOOP";

//Select les caractères qui sont des chiffres
var Query = from char ch in MyString 
where char.IsDigit(ch) ==true
select ch;


// Exécution de la requête
foreach ( char  c in Query)
  textBlock1.Text= textBlock1.Text+c;

// Combien y a t-il de nombres?
int count  = Query.Count();

Linq et les mots d'une string : une string peut être interrogée par Linq.
Exemple, rechercher un mot dans une string. On va découper tous les mots de la string et les mettre dans un tableau de string puis faire une requête.

 
Sélectionnez
string text  = "Ceci est un cours C# pour les débutants et les autres";

string searchTerm  = "cours";

// Conversion de la String en Tableau de mots:.
string[] dataSource= text.Split(new Char [] {' ', ',', '.', ';', ':'});

// Création et exécution de la requête
// Utiliser ToLower pour  trouver "cours" et "Cours" 
var Query = from word in dataSource 
where word.ToLower() == "cours" 
select word ;

//Compter les 'cours'.
int count = Query.Count();
textBlock1.Text= count.ToString();

Méthodes d'extension de Linq : dès que le type d'une variable a l'interface IEnumérable (tableau, collection…) on peut utiliser les méthodes d'extension de Linq et là, c'est vraiment simple et puissant.

Il y a une relation entre la syntaxe de requête et les méthodes d'extension :

 
Sélectionnez
var query =
    from i in values
    where i % 2 == 0
    select i * i;

est équivalent (en utilisant les méthodes d'extension) à :

 
Sélectionnez
var query = values.Where(i => i % 2 == 0).Select(i => i * i);

Ici on utilise une expression lambda avec l'opérateur = >, qui se lit « conduit à ». A gauche de l'opérateur lambda il y a les paramètres d'entrée et à droite le bloc d'expression ou d'instructions. L'expression lambda i => i * i se lit « i conduit à i fois i ».


Calculons une moyenne sur un tableau de Single :

 
Sélectionnez
Single [] numbers =  {0, 1, 2, 3, 4, 5, 6};

Single moyenne = numbers.Average();
textBlock1.Text = moyenne.ToString();

Pour une List d'int, Average retourne un double :

 
Sélectionnez
List<int> grades = new List<int> { 78, 92, 100, 37, 81 };

double average = grades.Average();


Calculons une somme sur un tableau de Single :

 
Sélectionnez
decimal [] numbers =  {0, 1, 2, 3, 4, 5, 6};

decimal MaSomme = numbers.Sum();


On peut aussi utiliser : First, Last, All, Any, Concat, Contains, Distinct, ElementAr, ElementAtOrDefault, Except, Intersect, Max, Min, Reserve, Skip, SkipeWhile, Take, TakeWhile, ThenBy, ToArray, ToDictionary, Where.

Voir le chapitre binding avec tri, filtre et image qui donne de beaux exemples.

III-T. Date et heure

Image non disponible

Il existe un type de variable DateTime pour gérer les dates et heures, comment l'utiliser ?
Une variable DateTime contient une date plus l'heure (un moment précis). Elle occupe 8 octets (64 bits). Elle peut contenir une date comprise entre le 1er janvier de l'année 01 et le 31 décembre 9999 et une heure comprise entre 0:00:00 (minuit) et 23:59:59.9999999… En fait ce qui est codé dans la variable DateTime est le nombre de graduations ou "tick" (une graduation= 100 nanosecondes) écoulées à compter de minuit, le 1er janvier de l'année 1 jusqu'à la date et l'heure codée.

 
Sélectionnez
//Déclarons un DateTime
DateTime ddn;
//Donnons la valeur 1 janvier 2011
ddn = new DateTime(2011, 5, 1);// année mois, jour.


//Déclarons date1 et donnons une valeur an, mois, jours, heure, minute, seconde
DateTime date1 = new DateTime(2011, 5, 1, 8, 30, 52);
            
//Propriétés en lecture seule
            int d = date1.Day;
            int m = date1.Month;
            int y = date1.Year;
            int h = date1.Hour;
            int mn = date1.Minute;
            int s = date1.Second;

//Convertir un DateTime en string avec To..String()
            MessageBox.Show(date1.ToLongTimeString());   //Affiche 08:30:52
            MessageBox.Show(date1.ToShortDateString());  //Affiche 01/05/2011
            MessageBox.Show(date1.ToShortTimeString());  //Affiche 08:30
            MessageBox.Show(date1.ToLongDateString());   //Affiche dimanche 1 mai 2011
//Voir aussi le chapitre "Afficher" pour utiliser les formats :
             MessageBox.Show(date1.ToString("hh:mm"));

// Convertir une string (saisie par l'utilisateur) en DateTime avec Parse          
string dateString = "5/1/2011 8:32:50 AM";
DateTime dateIn = DateTime.Parse(dateString, 
        System.Globalization.CultureInfo.InvariantCulture);

//Convertir mais avec TryParse      
DateTime DDR;
bool res = DateTime.TryParse("5/1/2011", out DDR);
//si conversion impossible retourne false sans planter


// DateTime particuliers
DateTime date2 = DateTime.Now; // Date et heure actuelles locales
DateTime date3 = DateTime.UtcNow; // en utc
DateTime date4 = DateTime.Today; // Date actuelle

Pour définir un intervalle de temps (une durée) on utilise un TimeSpan (c'est une structure).

 
Sélectionnez
TimeSpan Gr = new TimeSpan(288,0,0,0);// si 3 paramètres=h,m,s 
                                      // si 4 paramètres= j,h,m,s
                                      
//ou 

TimeSpan Gr = new TimeSpan.FromDays(288);

Il existe aussi FromMonths, FromHours, FromMinutes, FromSeconds, FromMilliSeconds.

On peut ajouter un TimeSpan à un DateTime (une durée à un moment).

Quelle sera la date dans 100 jours : il faut utiliser la date du jour et ajouter un TimeSpan de 100 jours.

 
Sélectionnez
DateTime MyDate;
MyDate = DateTime.Now;
TimeSpan Gr = new TimeSpan(100, 0, 0, 0);
MyDate = MyDate + Gr;
MessageBox.Show(MyDate.ToShortDateString());

On aurait pu utiliser Add.


On peut soustraire un TimeSpan. Ici enlevons 10 jours :

 
Sélectionnez
MyDate=MyDate.Subtract(new TimeSpan(10,0,0,0));
MessageBox.Show(MyDate.ToShortDateString());


Enfin, il y a des méthodes pour ajouter des mois, jours, minutes, secondes (AddMonths, AddDays, AddMinutes, AddSecondes).

 
Sélectionnez
MyDate=MyDate.AddMonths(2);
MessageBox.Show(MyDate.ToShortDateString());

MyDate.DayOfWeek retourne le jour de la semaine en anglais sous forme d'un enum de type DayOfWeek (ex. Sunday..)
MyDate.ToString("dddd") retourne le jour de la semaine dans la culture en cours ; en français pour nous (ex. samedi).

La méthode Now retourne la date et l'heure actuelle.

 
Sélectionnez
DateTime d;
d = DateTime.Now;

Il y a aussi Today (aujourd'hui).

Les DateTime utilisent l'heure locale (.Now) par défaut mais on peut utiliser l'heure universelle (utc) comme dans .UtcNow. La méthode ToUniversalTime convertit en temps universel.

Il existe aussi DateTimeOffset : la structure DateTimeOffset stocke un DateTime et un TimeSpan qui indique la différence entre le DateTime et l'UTC (dans un champ Int16).
Cela permet à une valeur DateTimeOffset de répercuter l'heure dans un fuseau horaire quelconque, alors qu'un DateTime ne peut refléter que le fuseau horaire UTC et le fuseau horaire local.

III-U. Nombres aléatoires

On peut avoir besoin de nombres aléatoires pour simuler le hasard dans un programme.

La classe Random fournit des nombres pseudo-aléatoires.

La génération commence à partir d'une valeur initiale qu'on appele souvent "seed" (graine). Ensuite les nombres suivants sont sélectionnés à l'aide d'un algorithme mathématique, mais ils sont néanmoins suffisamment aléatoires pour des fins pratiques.
Lorsque la même valeur initiale est utilisée, la même série de nombres pseudo-aléatoires est générée.
Pour générer des séquences différentes, il faut que la valeur initiale soit différente.

Si on instancie un Random sans paramètres, la valeur initiale est fournie par l'horloge interne (qui change de valeur tous les ticks ou 100ns), elle est donc au hasard :

 
Sélectionnez
 Random rand = new Random();

Ici une série différente est obtenue pour chaque nouvelle instance de Random (à condition qu'il se soit écoulé un peu de temps -au moins un tick- entre 2 instanciations de Random).
Si on utilise le code précédent, à chaque utilisation de l'application, la valeur initiale sera différente et donc la série de nombres sera différente.


Attention : si on instancie deux random à la suite, il y a de grandes chances pour que l'horloge interne garde la même valeur ; la valeur initiale sera la même et la série la même :

 
Sélectionnez
 Random rand1 = new Random();  // même series
 Random rand2 = new Random();

Mais, si l'horloge interne a changé entre les 2 instructions, ça donnera bien des valeurs différentes. C'est imprévisible…


Si on sépare les deux random d'un temps assez long , les séries ne seront pas les mêmes :

 
Sélectionnez
 Random rand1 = new Random();  
 MessageBox.Show ("Deux séries");
 Random rand2 = new Random();  //2 séries différentes

On peut forcer la valeur initiale à une valeur précise en donnant un paramètre (un int) lors de l'instanciation.

 
Sélectionnez
 Random rand1 = new Random(50);  // On aura toujours la même série
 
Sélectionnez
 Random rand1 = new Random(200);  // 2 series différentes mais toujours les mêmes
 Random rand2 = new Random(3845);

Ensuite on peut générer une séries d'octets : valeur 0 (compris) à 255 (compris) hexadécimal= 0xFF.

 
Sélectionnez
Random rand = new Random();  

// Génère  4 random octets dans un tableau.
byte[] bytes = new byte[4];
rand.NextBytes(bytes);

Générons un octet :

 
Sélectionnez
Random rand = new Random();
byte b = (byte)rand.Next(256); //Next retourne un int qu'il faut caster en byte

On peut générer des entiers :

 
Sélectionnez
Random rand = new Random();  

// on génère succesivement 3 entiers
int r1= rand.Next();
int r2= rand.Next();
int r3= rand.Next();

// on génère 1 entier inférieur ou égal à 100
//strictement inférieur à 101
int r4= rand.Next(101);

// on génère 1 entier entre 1 (compris) et 6 (compris)
int r5= rand.Next(1 , 7);

// on génère 1 entier entre -10 (compris) et 6 (compris)
int r6= rand.Next(-10 , 7);

On remarque que, comme la valeur supérieure n'est pas incluse, il faut ajouter 1 à la valeur du paramètre pour obtenir le maximum désirée.
Dans Next(min, max) min est inclus, max est non inclus.
Par ailleurs, on peut avoir des nombres négatifs uniquement quand on utilise deux paramètres.

On peut générer des doubles : de valeur allant de 0.0 (compris) à 1.0 (non compris) soit : supérieur ou égal à 0 et inférieur à 1. Pour obtenir une plage de valeur( de min à max)il faut donc multiplier par max-min puis ajouter min.

 
Sélectionnez
Random rand = new Random();  

// on génère succesivement 3 doubles (valeur entre 0 et 1 non compris)
double r1= rand.NextDouble();
double r2= rand.NextDouble();
double r3= rand.NextDouble();

// on génère 1 double entre 0 et 5
double r4= rand.NextDouble() *5 ;

// on génère 1 double entre 1 et 6 (non compris)
double r4= (rand.NextDouble() *5 )+1;

Pour les forts ! On peut créer une classe qui hérite de Random, en surchargeant Sample on peut modifier la distribution des valeurs.

 
Sélectionnez
public class RandomProportional : Random
{
    // 
    protected override double Sample( )
    {    // On modifie la distribution des valeurs.
        //Ici la distribution est proportionnelle à la valeur:
        //Les nombres se rapprochant de 1 (les plus grands) sont plus fréquents.
        return Math.Sqrt( base.Sample( ) );
    }

    public override int Next()
    {
       return (int) (Sample() * int.MaxValue);
    }   
}

Ensuite on peut utiliser cette classe :

 
Sélectionnez
RandomProportional rand = new RandomProportional();

En cryptographie (pour générer des mots de passe par exemple), par sécurité, utilisez une classe dérivée de System.Security.Cryptography.RandomNumberGenerator telle que System.Security.Cryptography.RNGCryptoServiceProvider, par exemple.

III-V. Culture, globalisation

Image non disponible

Une Culture concerne la langue d'affichage mais aussi les paramètres régionaux : format de date, d'heure, monnaie, séparateur décimal, calendrier.
Pour gérer la culture on utilise la classe CultureInfo (de System.Globalization) et la propriété CurrentCulture de thread (de System.Threading). Il y a également CurrentUICulture (langue d'affichage, c'est la culture utilisée pour récupérer les ressources localisées).

On peut voir et modifier la culture en cours dans les paramètres du téléphone :

Image non disponible

Par défaut la CultureInfo est celle définie dans les paramètres du téléphone, probablement « fr-FR » sur votre téléphone.
fr signifie langue française et FR signifie « région France » ; il existe fr-CH (Suisse), fr-BE (Belgique).
Dans « en-US », « en » signifie anglais et « US » signifie USA.
On se rend compte que l'affichage est dépendant de la CurrentCulture du Thread en cours.


Exemple : si la CurrentCulture est la Culture Us et que j'affiche avec le format monétaire cela affiche un $ avant, si je suis en culture Française cela affiche le symbole de l'euro après.


Ajoutons :

 
Sélectionnez
using System.Globalization;
using System.Threading;

La culture en cours est dans CurrentCulture de System.Threading.

 
Sélectionnez
//Où peut-on trouver le  séparateur décimal?
string separateur;
separateur=Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencyDecimalSeparator;// separateur=","
separateur=Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator); separateur=","

// et le symbole monnetaire?
string symbole;
symbole = Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencySymbol;//symbole= le symbole euros.

//Le nom de la culture
MessageBox.Show(Thread.CurrentThread.CurrentCulture.Name);// Affiche fr-FR
MessageBox.Show(Thread.CurrentThread.CurrentCulture.NativeName);// Affiche français(France)
MessageBox.Show(Thread.CurrentThread.CurrentCulture.EnglishName);// Affiche french (France)

On peut modifier la CurrentCulture par code.

 
Sélectionnez
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
MessageBox.Show(Thread.CurrentThread.CurrentCulture.DisplayName);


On peut aussi modifier le CultureInfo uniquement sur une instruction ToString TryParse ou Format :

 
Sélectionnez
Double d = 12.35;
MessageBox.Show (d.ToString()); //Affiche 12,35
MessageBox.Show( d.ToString( new CultureInfo("en-US")));//Affiche 12.35

On a donc vu que par défaut ToString utilise le CurrentCulture ; mais c'est aussi le cas pour toutes les instructions de conversion. On se souvient que par contre, pour un littéral, le séparateur décimal est toujours le point.

 
Sélectionnez
double d= 3.5;
MessageBox.Show (d.ToString()); //Affiche '3,5' chez nous

III-W. Afficher

On se souvient qu'on peut afficher dans un TextBlock, un TextBox, une ListBox… mais il faut que ce soit du texte et parfois il faut le formater, le mettre en forme.

Quand on a une variable numérique ou DateTime, pour la transformer en chaîne de caractères on utilise souvent la méthode ToString().

Par exemple, j'ai une variable de type int (entier) ; pour l'afficher, je dois la convertir en string (on peut afficher une chaîne de caractères mais pas une valeur numérique) :

 
Sélectionnez
int entier=12;
string chaîneaAfficher;
chaîneaAfficher= entier.ToString();

On peut ajouter en paramètre le format de la string.
Ici on va afficher deux caractères après la virgule.

 
Sélectionnez
float Z= 12.4556;
string   mastring =Z.ToString("0.00")
// string= "12,45"


A - Formats prédéfinis. On peut utiliser un caractère indiquant un format prédéfini.

"C" indique le format monétaire.

 
Sélectionnez
int MaVariable = 12;
TextBlock1.Text = MaVariable.ToString("C");

Affiche $12.00' (en culture anglaise ou "en-US").


On peut modifier la culture et afficher en Fr par exemple.

 
Sélectionnez
using System.Globalization;
//..
int MaVariable = 12;
TextBlock.Text = MaVariable.ToString("C", new CultureInfo("fr-FR"));//Affiche '12,00E'


"D" indique le format décimal.
Pour les intégraux uniquement (int, long… ). Le chiffre après D indique le nombre de chiffres.

 
Sélectionnez
int MaVariable = 12;
TextBlock1.Text = MaVariable.ToString("D"); // Affiche '12' 
TextBlock1.Text = MaVariable.ToString("D6"); // Affiche '000012'

Dans la seconde ligne on a indiqué d'afficher 6 caractères.


"E" indique le format exponentiel. Aussi appelé "notation scientifique".

 
Sélectionnez
int MaVariable = 12;
TextBlock1.Text = MaVariable.ToString("E"); // Affiche '1,200000E+001'


"X" indique le format hexadécimal.

 
Sélectionnez
int MaVariable = 12;
TextBlock1.Text = MaVariable.ToString("X");  // Affiche 'C' 12 en hexadécimal
TextBlock1.Text = MaVariable.ToString("X2"); // Affiche '0C'


"F" indique le format en virgule fixe.

 
Sélectionnez
double MaVariable = 12.576;
TextBlock1.Text = MaVariable.ToString("F");  // Affiche '12,58' 
TextBlock1.Text = MaVariable.ToString("F1"); // Affiche '12,6'

Par défaut F affiche deux chiffres après la virgule ; il arrondit au chiffre supérieur si le 3èm chiffre est supérieur à 5.
On peut spécifier le nombre de décimales à afficher.


"G" indique le format général (par défaut).

 
Sélectionnez
double MaVariable = 12.576;
TextBlock1.Text = MaVariable.ToString("G");  // Affiche '12,576' 
TextBlock1.Text = MaVariable.ToString("G2"); // Affiche '13' 

 double MaVariable2 =121212.576 ;
TextBlock.Text = MaVariable2.ToString("G"); //Affiche '121212,576' (pas de séparateur des milliers)

Par défaut G affiche la notation la plus compacte entre "F" et "E".

 
Sélectionnez
double d1 = 0.003;
string s1 = d1.ToString("G"); // Affiche "0,003"

double d2 = 0.000003;
string s2 = d2.ToString("G"); // Affiche "3E-06"

On peut spécifier le nombre de chiffre à afficher ('G2' affiche deux chiffres). Il arrondit au chiffre supérieur si le 3e chiffre est supérieur à 5.


"N" indique le format numérique.
Il utilise le séparateur décimal et celui des milliers de la culture.

 
Sélectionnez
double MaVariable = 121212.576;
TextBlock1.Text = MaVariable.ToString("N");  // Affiche '121 212,576' 
TextBlock1.Text = MaVariable.ToString("N1"); // Affiche '121 212,6'

Par défaut N affiche tous les chiffres.
On peut spécifier le nombre de chiffre après le séparateur décimal. Il arrondit le dernier chiffre affiché au chiffre supérieur si le précédent chiffre est supérieur à 5.


"P" indique le format pourcentage.
Le pourcentage doit être entre 0 et 1 pour afficher 0% à 100% ( mais rien n'empêche d'utiliser 42 qui en format pourcentage donnera 4200%). La valeur sera multipliée par 100. Le symbole % sera ajouté.

 
Sélectionnez
double MaVariable = 0.5;
TextBlock1.Text = MaVariable.ToString("P");  // Affiche '50,00%' 
TextBlock1.Text = MaVariable.ToString("P0");  // Affiche '50%'

"R" indique le format aller-retour.
Il retourne une chaîne de caractères qui, si on la reconvertit en nombre, redonnera la valeur de départ.



Cette valeur de format ('C', 'X'… ) peut être fournie comme paramètre formatString dans un élément de mise en forme utilisé avec des méthodes telles que String.Format, System.Diagnostics.Debug.WriteLine et StringBuilder.AppendFormat.

 
Sélectionnez
 outputBlock.Text = String.Format("La valeur de la variable est {0:C2}", MaVariable) + "\n";

Affiche "La valeur de la variable est 12" ; il faut que le TextBlock soit suffisamment large pour afficher la totalité du texte ; c'est bête !


Si la valeur d'une variable à virgule flottante Single ou Double est l'infini positif, l'infini négatif ou une valeur non numérique (NaN), la chaîne mise en forme est la valeur de la propriété PositiveInfinitySymbol, NegativeInfinitySymbol ou NaNSymbol qui est spécifiée par l'objet NumberFormatInfo.

"B" - Formats personnalisés

On peut créer de toute pièce un format, on utilise pour cela des caractères réservés.

"0" indique une espace réservé de 0

Chaque 0 est réservé à un chiffre. Affiche un chiffre ou un zéro. Si le nombre contient moins de chiffres que de zéros, affiche des zéros non significatifs. Si le nombre contient davantage de chiffres à droite du séparateur décimal qu'il n'y a de zéros à droite du séparateur décimal dans l'expression de format, arrondit le nombre à autant de positions décimales qu'il y a de zéros. Si le nombre contient davantage de chiffres à gauche du séparateur décimal qu'il n'y a de zéros à gauche du séparateur décimal dans l'expression de format, affiche les chiffres supplémentaires sans modification.

"#" indique un espace réservé de chiffre.

Chaque # est réservé à un chiffre. Affiche un chiffre ou rien. Affiche un chiffre si l'expression a un chiffre dans la position où le caractère # apparaît dans la chaîne de format ; sinon, n'affiche rien dans cette position.

Ce symbole fonctionne comme l'espace réservé au 0, sauf que les zéros non significatifs et à droite ne s'affichent pas si le nombre contient moins de chiffres qu'il n'y a de caractères # de part et d'autre du séparateur décimal dans l'expression de format.

"." (point) indique l'emplacement du séparateur décimal (celui affiché sera celui du pays).

Vous devriez donc utiliser le point comme espace réservé à la décimale, même si vos paramètres régionaux utilisent la virgule à cette fin. La chaîne mise en forme apparaîtra dans le format correct pour les paramètres régionaux.

"," (virgule) indique l'emplacement du séparateur de milliers.

Le séparateur de milliers sépare les milliers des centaines dans un nombre de quatre chiffres ou plus à gauche du séparateur décimal.

"Littéral" la chaîne sera affichée telle quelle.

"%" affiche en pour cent.

Multiplie l'expression par 100. Le caractère du pourcentage (%) est inséré.

"E0" affiche en notation scientifique.

":" et /" sont les séparateurs d'heure et de date.

";" est le séparateur de section : on peut donner trois formats (un pour les positifs, un pour les négatifs, un pour zéro) séparés par ";".

Exemple :

Chaîne de format '0000', avec le nombre 145 cela affiche '0145'.

Chaîne de format '####', avec le nombre 145 cela affiche '145'.

Chaîne de format '000.00', avec le nombre 45.2 cela affiche '045.20'.

Chaîne de format '#,#', avec le nombre 12345678 cela affiche '12,345,678'.

Chaîne de format '#,,' avec le nombre 12345678 cela affiche '12'.

La chaîne de formatage' #,##0.00 ' veut dire obligatoirement deux chiffres après le séparateur décimal et un avant.

Si on affiche avec ce format :

1.1 cela donne 1,10.

.5 cela donne 0,50.

4563 cela donne 4 563,00.


Exemple pratique :

 
Sélectionnez
double MaVariable = 0.5;
TextBox.Text = MaVariable.ToString("$#,##0.00;($#,##0.00);Zero");// Affiche '$0,50'
 
Sélectionnez
double MaVariable = 0;
TextBox.Text = MaVariable.ToString("$#,##0.00;($#,##0.00);Zero");// Affiche 'Zero'

C - String.Format

Permet de combiner des informations littérales à afficher sans modification et des zones formatées.

Les arguments de String.Format se décomposent en deux parties séparées d'une virgule.

- Chaîne de formatage entre guillemets avec du texte si nécessaire et les éléments à inclure avec un numéro d'ordre entre {}. Exemple "{0} + {1} = {2}", les numéros indiquent l'ordre des valeurs.

- Variables ou valeurs à afficher dans l'ordre, la première étant d'indice zéro, séparées par des virgules. Exemple= A, B, A+B.

Exemple :

 
Sélectionnez
int A=3;
int B=5;
MessageBox.Show(String.Format("L'addition {0} + {1} = {2}", A, B, A + B));//affiche'L'addition 3+5=8'


Autre exemple:

 
Sélectionnez
string MonNom = "Philippe";
MessageBox.Show(String.Format("{0} il est {1:hhmm}", MonNom, DateTime.Now));//affiche'Philippe il est 0705''

On voit qu'on peut utiliser après le numéro d'ordre et « : » une chaîne de formatage prédéfinie ou personnalisée.

Ici on a utilisé un format de date.

Pour afficher les DateTime, il y a des formats prédéfinis et les formats personnalisés.

 
Sélectionnez
DateTime thisDate = new DateTime(2012, 3, 3, 14, 9, 20, 45);

//Les formats prédéfinis.

System.Diagnostics.Debug.WriteLine(thisDate.ToString("d"));//Date courte  "03/03/2012"
System.Diagnostics.Debug.WriteLine(thisDate.ToString("D"));//Date longue  "samedi 3 mars 2012"
System.Diagnostics.Debug.WriteLine(thisDate.ToString("t"));//Heure courte  "14:09"
System.Diagnostics.Debug.WriteLine(thisDate.ToString("T"));//Heure longue  "14:09:20"
System.Diagnostics.Debug.WriteLine(thisDate.ToString("f"));//Pleine date, Heure courte "samedi 3 mars 2012 14:09"
System.Diagnostics.Debug.WriteLine(thisDate.ToString("F"));//Pleine date, Heure longue "samedi 3 mars 2012 14:09:20"
System.Diagnostics.Debug.WriteLine(thisDate.ToString("g"));//Date Heure courte "03/03/2012 14:09"
System.Diagnostics.Debug.WriteLine(thisDate.ToString("G"));//Date Heure longue "03/03/2012 14:09:20"
System.Diagnostics.Debug.WriteLine(thisDate.ToString("M"));//Mois  "3 mars"
System.Diagnostics.Debug.WriteLine(thisDate.ToString("Y"));//Année  "mars 2012"

//Formats personnalisés

//Jour : d jour du mois(1 à 31), dd (01 à 31), ddd (jour abrégé de la semaine), dddd (jour complet de la semaine)
System.Diagnostics.Debug.WriteLine(thisDate.ToString("dddd dd")); // "samedi 03"

//Heure : h(1 à 12), hh (01 à 12), H (1 à 23), HH (01 à 23)
System.Diagnostics.Debug.WriteLine(thisDate.ToString("HH"));// "14"

//Minute : m (0 à 59) mm (00 à 59) //Seconde : s, ss
System.Diagnostics.Debug.WriteLine(thisDate.ToString("mm,ss"));// "09,20"

//Mois : M, MM, MMM, MMMM
System.Diagnostics.Debug.WriteLine(thisDate.ToString("MMMM"));// "mars"

//Année : y, yy, yyy, yyyy
System.Diagnostics.Debug.WriteLine(thisDate.ToString("yyyy"));// "2012"

//Exemples (notez le séparateur des heures ":" et celui des dates "/")
System.Diagnostics.Debug.WriteLine(thisDate.ToString("hh:mm:ss"));// "02:09:20"
System.Diagnostics.Debug.WriteLine(thisDate.ToString("d MMMM"));//"3/mars"

On se souvient qu'on peut aussi utiliser :

 
Sélectionnez
DateTime date1 = new DateTime(2011, 5, 1, 8, 30, 52);

            MessageBox.Show(date1.ToLongTimeString());   //Affiche 08:30:52
            MessageBox.Show(date1.ToShortDateString());  //Affiche 01/05/2011
            MessageBox.Show(date1.ToShortTimeString());  //Affiche 08:30
            MessageBox.Show(date1.ToLongDateString());   //Affiche dimanche 1 mai 2011

Bien sur la conversion se fait dans la culture du thread en cours.

On peut ajouter a ToString un paramétre indiquant la culture à utiliser :

 
Sélectionnez
CultureInfo cultureUS = new CultureInfo("en-US");
     
DateTime date1 = new DateTime(1890, 9, 10);

MessageBox.Show(date1.ToString("d", cultureUS));

III-X. Le Framework.NET

Le code se compose de mots clés C# et de classes faisant partie du Framework .NET de Silverlight.

Les classes sont regroupées sous la dénomination d'espace de noms (NameSpace).

Un framework est un ensemble d'espace de noms et de classes.

On a à notre disposition le Framework.NET qui contient des milliers de classes.

Pour pouvoir utiliser une classe il faut inclure l'espace de noms auquel appartient cet objet.
En C#, on le fait grâce à using qui sera placé en haut du code, avant toutes autres instructions :

 
Sélectionnez
using System.Collection.Generic;

Il faut aussi que l'assembly correspondant (sous forme de DLL par exemple) soit référencé dans le projet (fenêtre Explorateur de solution, Références). Par défaut les références du Framework (les DLL) sont déjà dans le projet ; les principaux espaces de noms aussi.
Dans la suite du cours s'il faut inclure un assembly, ou un espace de noms, je le signale.

Voici quelques espaces de noms du Framework du Windows Phone (7.1)

Microsoft.Devices, Microsoft.Devices.Radio, Microsoft.Devices.Sensors, Microsoft.Phone, Microsoft.Phone.Controls, Microsoft.Phone.Info, Microsoft.Phone.Marketplace, Microsoft.Phone.Shell, Microsoft.Phone.Tasks, System, System.Data, System.Windows.Controls.

Voyons le détail de l'espace de noms System : il contient les types (classes , structures) suivants (et d'autres).

System
System.String
System.Objet
Systen.Array
System.Collections
System.Enum
System.Exception
System.Math
System.Random
System.Boolean
System.Byte
System.Char
System.Int16
System.Int32
System.Int64
System.Single
System.DateTime
System.Double
System.Decimal


On connaît bien ces classes qu'on utilise dans le code C#.

On peut retrouver les espaces de noms de Silverlight sur MSDN. À partir de là, on clique sur un espace de noms (System par exemple) puis une classe (Enum par exemple), on voit les différents membres.

On retrouve des icônes (les mêmes que dans Visual Studio) :

Image non disponible


Pour une méthode :

Image non disponible
  • le petit carré à gauche indique que c'est une méthode (s'il y a une petite clé, la méthode est protected sinon elle est public) ;
  • "s" indique si elle est static ;
  • le téléphone indique qu'elle existe sur Windows Phone ;
  • la boule indique qu'elle existe sur Xbox.

Cliquez sur le nom d'une méthode pour voir les détails et des exemples.

Revoir le chapitre Objet, classe, Espace de noms.

III-Y. Création de classe

On est DANS la boîte.

Jusqu'à présent on avait utilisé des classes déjà présentes dans le Framework Silverlight pour créer des objets.
Ici on va créer notre propre classe (nommée ElementDeListe, possédant trois propriétés), menu Projet, Ajouter une classe. On donne un nom puis Ok.
Ensuite on crée la classe ElementDeListe qui a trois propriétés : Titre, Formule, Mode.

 
Sélectionnez
public class ElementDeListe  // la classe se nomme 'ElementDeListe'
{
}


On veut un champ (une variable) nommé Titre pour cette classe ; pour cela on va créer une variable de type string privée nommée _titre.

 
Sélectionnez
private string _titre;

Ce champ (cette variable), n'est pas accessible à l'extérieur de la classe (private).
Un champ est habituellement private ou protected (on pourra l'utiliser dans la classe ou une classe dérivée).
Noter que le nom du champ commence par "_" ; c'est une bonne pratique pour les champs privés.
Le nom est normalement en camelCase (première lettre en minuscule, puis première lettre de chaque "mot" en majuscule).


Noter qu'on peut (ce n'est pas recommandé) créer des champs public :

 
Sélectionnez
public string Titre;

Les noms de classe et les membres publics sont en PascalCase (première lettre en majuscule, puis première lettre de chaque "mot" en majuscule).
Les paramètres et variables locales sont en camelCase, sans le préfixe "_".

Il existe aussi internal (classe ou membre accessible que dans des fichiers figurant dans le même assembly).
On remarque que les variables sont instanciées comme habituellement avec un ";" à la fin de la ligne.

On peut aussi ajouter const (champ constante qui ne peut être modifié que lors de la déclaration) ou readonly (ne peut être modifié que lors de la déclaration ou dans un constructeur) pour que le champ soit une constante (de compilation) ou en lecture seule (constante d'exécution).


Pour un accès public il vaut mieux utiliser une propriété.
On va donc créer une propriété nommée Titre, elle est public, et il faut écrire les Get et Set permettant de lire et modifier cette propriété.
La variable _titre sert à stocker la valeur de Titre :

 
Sélectionnez
public string Titre     // Variable public avec son get et son set
            {
                get
                {
                    return _titre;
                }
                set
                {
                    if (value != _titre)
                    {
                        _titre = value;
                       
                    }
                }
            }

Quand on lira la propriété Titre, on aura la valeur de _titre ; quand on écrira dans Titre, on donnera une valeur à _titre.
Il vaut mieux utiliser une propriété plutôt qu'un champ public car cela permet d'utiliser du code dans Get et Set (contrôle de type, de validité, calcul…).


On remarque que pour une propriété, il n'y a pas de ";" après « public string Titre » mais des accolades.


Voici le code complet de la classe avec ses trois propriétés:
(menu Projet, Ajouter une classe pour créer la classe).

 
Sélectionnez
using System;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;

    namespace CalculeTout // Espace de noms = nom du programme
    {
        public class ElementDeListe  // la classe se nomme 'ElementDeListe'
        {
            private string _titre;  // Variable privée
            
            public string Titre     // Variable public avec son get et son set
            {
                get
                {
                    return _titre;
                }
                set
                {
                    if (value != _titre)
                    {
                        _titre = value;
                       
                    }
                }
            }
            private string _formule;

            public string Formule
            {
                get
                {
                    return _formule;
                }
                set
                {
                    if (value != _formule)
                    {
                        _formule = value;

                    }
                }
            }

            private string _mode;

            public string Mode
            {
                get
                {
                    return _mode;
                }
                set
                {
                    if (value != _mode)
                    {
                        _mode = value;

                    }
                }
            }
          
            public ElementDeListe(string t, string n, string m) { _titre = t; _formule = n; _mode = m; }  
        }
  
  }

La dernière ligne est un constructeur avec 3 paramètres, on lui donne le nom de la classe, on indique des 3 paramètres du constructeur et dans quelles variables il faut passer ces paramètres.

Comment utiliser cette classe ?

On va créer une instance (un objet) nommée element à partir de la classe ElementDeListe :

 
Sélectionnez
public ElementDeListe Element;
Element = new ElementDeListe ();

Maintenant on peut donner une valeur aux propriétés :

 
Sélectionnez
Element.Titre = "Multiplication";
Element.Formule = "*";
Element.Mode = "sans";

On peut aussi utiliser le constructeur avec trois paramètres :

 
Sélectionnez
public ElementDeListe Element= new ElementDeListe("Multiplication", "*", "sans");

On peut aussi créer une collection d'objets.
Dans le code de la page C#, on va instancier une collection nommée ListeMath contenant des objets de type ElementDeListe.

 
Sélectionnez
 public partial class PanoramaPage : PhoneApplicationPage
    {
        public List<ElementDeListe> ListeMath { get; set; } //la collection sera public
       
        public PanoramaPage()
        {
            InitializeComponent();
            
            ListeMath = new List<ElementDeListe>();
            
            //On va créer de nouveaux ElementDeliste en utilisant le constructeur à 3 paramètres.
            ListeMath.Add(new ElementDeListe("Multiplication", "*", "sans"));
            ListeMath.Add(new ElementDeListe("Division", "/", "sans"));
            ListeMath.Add(new ElementDeListe("Addition", "+", "sans"));
            
            ListBoxConversion.DataContext = ListeMath;// en prime on ajoute un binding (une liaison) sur une listbox

Notez bien qu'on instancie ListeMath en haut de la page et avec public afin que cette List soit accessible partout dans la page.
On voit qu'on instancie trois ElementDeListe et qu'on donne une valeur aux trois champs grâce au constructeur.

On a vu qu'a la fin de la classe on avait ajouté un constructeur, il peut y en avoir plusieurs :

 
Sélectionnez
 public class ElementDeListe  // la classe se nomme 'ElementDeListe'
{
private string _titre;  // Variable privée

// constructeur par défaut: sans paramètres
public ElementDeListe() 
    {
        _titre = "sans";
    }

// Second Constructeur avec un paramètre:
public ElementDeListe(string titre)
    {
        this._titre = titre;
    }

}

Si vous ne fournissez pas de constructeur pour votre classe, C# en créera un par défaut qui instanciera l'objet et affectera à toutes les variables membres les valeurs par défaut.

On peut ajouter des méthodes à la classe :

 
Sélectionnez
 public class ElementDeListe  // la classe se nomme 'ElementDeListe'
{
 // Affiche titre :
    public void AfficheTitre()
    {
        MessageBox.Show ( this._titre);
    }
}

En résumé :

Un objet peut être vu comme un regroupement de données (champs et propriétés). La différence entre un champ et une propriété est qu'alors que le champ est une simple variable, la propriété est contrôlée par du code interne qui peut modifier le comportement de la lecture ou de l'écriture de la propriété.


Mais un objet va plus loin : il peut contenir un comportement sous forme de procédures et fonctions contenant du code. Ce sont les méthodes de l'objet, elles représentent le comportement commun de tous les objets appartenant à la classe.

Tous les éléments de la classe peuvent être public (visibles par l'utilisateur de la classe ou des classes dérivées) ou private (non accessibles à l'utilisateur de la classe et utilisés en interne dans la classe mais pas dans les classes dérivées).
Ils peuvent aussi être protected (non accessible à l'utilisateur mais accessible dans la classe et les classes dérivées).

Les champs sont habituellement private et des propriétés public permettent de modifier ces champs.

Héritage :


À partir d'une classe existante, la classe de base (ou classe mère), on peut créer une nouvelle classe, la classe dérivée (ou classe fille) qui hérite des membres de la classe de base. Ici on va créer une classe ElementDeListe2 qui hérite de ElementDeListe (on ajoute au nom de la classe deux points puis le nom de la classe mére) :

 
Sélectionnez
 public class ElementDeListe2  : ElementDeListe
{
}

ElementDeListe2 hérite des membres (champs, propriétés et méthodes) de ElementDeListe (si ils sont protected ou public, pas si ils sont private): ainsi dans la classe ElementDeListe2 on a la méthode AfficheTitre.
On peut ajouter dans ElementDeListe2 d'autres champs, propriétés et méthodes.

 
Sélectionnez
 public class ElementDeListe2  : ElementDeListe
{
private _titre2;
}

On peut utiliser sur une instance de la classe fille une méthode de la classe mère (si elle est accéssible).
On peut aussi redéfinir (substituer, remplacer) les méthodes de la classe mère grâce à override à condition que la méthode de la classe de base soit virtual. Dans ElementDeListe2, si on ne veut pas utiliser la méthode AfficheTitre de la classe mère ; on va remplacer cette méthode par une méthode de même nom dans la classe ElementDeListe2 :

La methode de la classe mère doit donc être public virtual ou protected virtual (ou abstract) :

 
Sélectionnez
 public class ElementDeListe  // la classe se nomme 'ElementDeListe'
{
 // Affiche titre :
    public virtual void AfficheTitre()
    {
        MessageBox.Show ( this._Titre);
    }
}

Dans la classe fille on peut maintenant redéfinir la méthode AfficheTitre (comme la méthode de base est public, la méthode dans la classe fille doit aussi être public) :

 
Sélectionnez
 public class ElementDeListe2  : ElementDeListe
{

public override void AfficheTitre()
{
MessageBox.Show ( this._Mode);
base.AfficheTitre();

}
}

Ici on crée donc une nouvelle méthode. Notez qu'on peut appeler la méthode de la classe mère avec base.AfficheTitre();.

Écriture rapide de classe :
On peut écrire rapidement une classe et ses propriétés avec la syntaxe suivante :

 
Sélectionnez
 public class Personne
    {
        public String Prenom { get; set; }
        public String Nom { get; set; }
        public String Addresse { get; set; }
        public String Sexe { get; set; }
        public String Image { get; set; }
        
        public Personne(String prenom, String nom, String addresse, string sexe, string image)
        {
            this.Prenom = prenom;
            this.Nom = nom;
            this.Addresse = addresse;
            this.Sexe = sexe;
            this.Image = image;
        }

    }

Les get et set sont compact, pas de rajout de code possible.

Classes ou méthodes abstraites :
Elles ne peuvent pas être instanciées. On ne peut donc pas utiliser new. Elles servent à quoi alors ?
La classe abstraite sert de classe de base pour une autre classe qui va en hériter.

 
Sélectionnez
public abstract class MaClasseAbstraite
{
    public abstract void MaMethodeAbstraite();
    //Cette méthode abstract ne doit pas avoir de corps, d'accolades {}
}

Voici la classe dérivée :

 
Sélectionnez
 public  class MaClasse : MaClasseAbstraite 
    {
        public override void MaMethodeAbstraite()
    {


    }
        
    }

La méthode abstraite est redéfinie.

À l'inverse, pour qu'une classe ne puisse pas être héritée, il faut ajouter sealed (scellée) avec le mot Class.
On remarque qu'il y a beaucoup de classe sealed dans le Framework.NET : string par exemple.

On résume les modificateurs d'accès autorisés pour un membre ou un type :
public : L'accès n'est pas limité dans la classe et pour l'utilisateur de la classe.
protected : L'accès est restreint à la classe conteneur ou aux types dérivés de la classe conteneur (pas pour l'utilisateur de la classe ou l'utilisateur de la classe dérivée).
internal : L'accès est autorisé dans la classe et pour l'utilisateur de la classe mais restreint à l'assembly en cours.
protected internal : L'accès est restreint à l'assembly en cours ou aux types dérivés de la classe conteneur.
private : L'accès est restreint au type conteneur. L'utilisateur de la classe n'y a pas accès.

III-Z. Exercices en C#

1 - Exercice sur les voyelle.

Soit une string c contenant un caractère, si c'est une voyelle, afficher "C'est une voyelle".

2 - Exercice sur les et et les ou.

Sont bissextiles les années divisibles par 4 mais non divisibles par 100 et les années divisibles par 400.
Soit une variable nommée annee', écrire le code affichant dans la TextBox resultat si l'année est bissextile.

3 - Exercice sur les majuscules.

Soit une string Nom, si nécéssaire mettre le premier caractère en majuscule.

4 - Exercice sur les fonctions :

Sachant qu'un angle en radian = (angle en degrés/180)x Pi, écrire les fonctions convertissant les degrés en radians et radians en degrés.

Réponses aux exercices.

1 - Réponse à l'exercice sur les voyelles.

Soit une string c, si c'est une voyelle, afficher « C'est une voyelle ».

On va créer une string nommée voyelle, l'initialiser avec une chaine contenant toutes les voyelles.
Puis on verra si voyelle contient la string c.

 
Sélectionnez
string Voyelle = "aeiouy";

string c = "e";


if (Voyelle.Contains(c) )
{
MessageBox.Show("c'est une voyelle");

}

2 - Réponse à l'exercice sur les et et les ou.

Sont bissextiles les années divisibles par 4 mais non divisibles par 100 et les années divisibles par 400.
Soit une variable nommée annee, écrire le code affichant dans la TextBox nommé resultat si l'année est bissextile. Si le reste de la division par 4 égale zéro est vrai ET si le reste de la division par 100 égale zéro est faux OU si le reste de la division par 400 égale zéro est vrai l'année est bissextile.

 
Sélectionnez
int annee= 2012;
if (((annee % 4 == 0) & !(annee % 100 == 0)) | (annee % 400 == 0))
                        {
                            resultat.Text = "Année bissextille";
                        }
                        else
                        {
                            resultat.Text = "Année non bissextille";
                        }

Notez bien la position des parenthèses: (… et… ) ou (… ).

3 - Réponse à l'exercice sur les majuscules.

Soit une string Nom, si nécéssaire mettre le premier caractère en majuscule.

 
Sélectionnez
string Nom  = "philippe";

//Le premier caractère est minuscule?
if (Char.IsLower(Nom[0]))
{
    //Le transformer en majuscule et afficher
    MessageBox.Show(Char.ToUpper(Nom[0]) + Nom.Substring(1, Nom.Length - 1));

}

4 - Réponse à l'exercice sur les fonctions :

Sachant qu'un angle en radian = (angle en degrés/180)x Pi, écrire les fonctions convertissant les degrés en radians et radians en degrés.

 
Sélectionnez
static double DegreeToRadian(double angle)
        {
            return (angle / 180.0) * Math.PI;
        }
     
static double RadianToDegree(double radian)
        {
            return (radian / Math.PI) * 180.0;
        }

précédentsommairesuivant

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 © 2011 Lasserre Philippe. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.