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#▲
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.
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é).
Lancez le logiciel, vous voyez la page d'ouverture :
Cliquez sur « Nouveau Projet » en haut à gauche, cela ouvre la fenêtre suivante :
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 :
Gardez la version 7.1.
L'espace de travail s'ouvre :
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 :
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.
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.
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 :
<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.
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 ».
puis dans la liste double-cliquez sur « KeyUp » vous voyez apparaitre :
<TextBox
Name
=
"textBox1"
Text
=
"TextBox1"
KeyUp
=
"textBox1_KeyUp"
/>
Et dans le code C# :
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# :
On remarque en haut à droite la fenêtre Explorateur de solutions qui contient les différents éléments du projet :
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 !) :
Dans les fenêtres, il y a différentes icônes permettant de modifier la taille du texte du design ou des volets :
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 :
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 :
Pour déboguer, il faut choisir l'émulateur en haut au milieu :
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.
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.
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.
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.
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) :
On voit qu'on peut aussi ouvrir des fenêtres :
Liste des variables locales et leurs valeurs :
On peut ouvrir une fenêtre Espion, saisir le nom d'une variable ou d'un objet et voir sa valeur.
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..».
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.
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▲
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.
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 :
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 :
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.
/* 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 :
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 :
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# :
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 :
// 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é :
MyButton.
Content =
"Bonjour"
;
Ici on a utilisé la propriété Content du bouton.
Un objet a aussi des méthodes :
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 :
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 :
MessageBox.
Show
(
"texte"
);
MessageBox.
Show
(
"texte"
,
"titre"
,
MessageBoxButton.
OK);
Dans une classe il peut y avoir des fonctions :
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 :
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) :
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.
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.
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.
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 :
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.
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.
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) :
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).
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 :
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 :
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 :
int
number;
number=
12
;
On peut déclarer et initialiser en même temps :
int
number =
12
;
Autre exemple, créer un double et l'initialiser avec la valeur 12 (double) :
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.
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).
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 :
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).
{
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.
A=
B;
Affecte la valeur de la variable B à la variable A, la valeur de B est mise dans A.
Exemple concret :
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 :
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 :
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…
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 :
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.
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.
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 :
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 :
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.
MyString=
"Hello "
+
" word"
;
On peut voir si deux strings sont égales ou différentes (avec == et !=).
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 @" :
string
StartDirectory =
@"c:\Users\exampleuser\start"
;
Il existe aussi le type (char) qui ne peut contenir qu'un seul caractère.
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.
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) :
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 :
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.
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 »
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 :
string
a=
"aa"
;
string
b=
"bb"
;
string
c=
""
;
s=
String.
Concat
(
a,
b);
Il est plus rapide de faire :
s=
a +
b
Concat est très pratique quand on veut mettre bout à bout tous les éléments d'un tableau :
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.
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).
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);
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 :
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.
string
MyString =
"Philippe;Jean;Toto"
;
string
[]
b;
b =
MyString.
Split
(
';'
);
Il peut y avoir plusieurs caractères de séparation :
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.
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 :
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 :
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.
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.
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 :
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.
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.
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 :
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 :
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.
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.
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 :
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.
string
a =
"Hello word"
;
string
c =
a.
Substring
(
2
,
2
);
Exercice 1 : comment obtenir les quatre caractères de droite :
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 :
string
a =
"Hello word"
;
string
c =
a.
Substring
(
0
,
3
);
MessageBox.
Show
(
c);
On peut créer des chaînes avec la classe String :
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.
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" :
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 ' ').
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 :
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() :
textBlock1.
Text =
maArray[
2
].
ToString
(
);
On peut aussi très simplement lire un caractère à une certaine position dans une string :
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.
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.
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 :
stringb.
Replace
(
'!'
,
'o'
);
Autre exemple :
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 :
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 :
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 :
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).
int
myint=
12
;
int
mysecondint;
mysecondeint=
myint+
3
;
'%' donne le reste d'une division (modulo) :
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
if
(
A!=
B)
{}
Écriture compacte, incrémentation :
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.
//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 :
'>>' 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 :
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.
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).
Pour l'activer sur une portion de code seulement, il faut utiliser la directive Checked dans le code.
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 :
double
number1=
12
.
3
;
double
number2=
6
.
7
;
double
result =
Math.
Max
(
number1,
number2);
Min retourne le plus petit des deux nombres :
double
number1=
12
.
3
;
double
number2=
6
.
7
;
double
result =
Math.
Min
(
number1,
number2);
Abs retourne la valeur absolue :
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.
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) :
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 :
result =
Math.
Sqrt
(
number1);
Pow permet d'élever un nombre x à la puissance y :
result =
Math.
Pow
(
x,
y);
PI donne le nombre pi.
decimal
perimetre=
2
*
Math.
PI *
Rayon;
Log permet d'avoir le Log népérien, Log10 permet d'avoir le Log de 10 :
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). .
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 :
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 :
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…
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 :
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 :
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 :
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
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 :
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 :
int
[]
c =
new
int
[
3
];
c[
0
]=
2
;
c[
1
]=
3
;
c[
2
]=
15
;
On peut grouper les lignes 2 et suivantes :
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 :
int
[]
c =
{
2
,
3
,
15
};
Exemple avec des tableaux de float et de strings :
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 :
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 :
// 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.
string
[]
MonTableau ={
"Paul"
,
"Louis"
,
"Jean"
};
foreach
(
string
s in
Montableau)
{
System.
Diagnostics.
Debug.
WriteLine
(
s);
}
ou une boucle for.
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 :
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.
//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.
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 :
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.
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.
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.
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 :
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 ».
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 :
Double Quantite ;
bool
ConversionIsOk =
double
.
TryParse
(
TextQuantite.
Text,
out
Quantite);
Texte en date
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.
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 :
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.
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 :
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 :
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.
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 ?
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 :
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).
/////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 :
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.)
//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▲
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 :
bool
myBoolean;
On peut écrire :
myBoolean =
true
;
On peut aussi tester cette variable :
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 :
if
(!
myBoolean)
{
}
Autre exemple montrant comment le raisonnement informatique est logique :
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 :
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
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 {} :
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 :
if
(
A>
99
&&
A<
301
) B=
2
;
Mais attention :
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 :
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 :
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 :
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.
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 :
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) :
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.
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 « : » :
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▲
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.
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 :
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','.
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.
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.
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.
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.
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 ?
// 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).
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 :
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 :
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 :
List<
string
>
Col =
new
List<
string
>(
);
J'ajoute des éléments (ou items) à cette collection.
Col.
Add (
"Toto"
);
Voici la collection :
La collection a maintenant un élément (on dit un item en anglais).
Je fais maintenant :
Col.
Add
(
"Lulu"
);
Col.
Add
(
"Titi"
);
La collection a trois éléments maintenant.
Il est aussi possible d'insérer un élément à une position particulière :
Col.
Insert
(
2
,
"lulu"
);
On peut enlever un élément avec Remove :
Col.
Remove
(
"Lulu"
);
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 :
Col.
RemoveAt
(
1
);
On peut aussi enlever une plage d'éléments à partir d'index grâce à RemoveRange :
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.
string
element=
Col[
1
];
//contient "Titi" (le second Item de la collection)
Pour parcourir une collection on peut utiliser foreach :
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 :
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 :
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).
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.
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 :
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.
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 :
Col.
Sort
(
CompareElements);
RemoveAll :
Permet de supprimer de la collection les éléments correspondant à une condition; retourne le nombre d'éléments supprimés.
int
nombreDElementsSupprimés =
nombres.
RemoveAll
(
n =>
n %
2
!=
0
);
On peut créer une List à partir d'un tableau grâce à :
string
[]
input =
{
"Toto"
,
"Lulu"
,
"Riri"
};
List<
string
>
MyFriends =
new
List<
string
>(
input);
On peut aussi créer un tableau à partir d'une liste :
string
[]
output =
MyFriends.
ToArray
(
);
Il est possible d'avoir un sous-ensemble d'une liste :
List<
string
>
Listoutput =
MyFriends.
GetRange
(
2
,
3
);
Il existe aussi RemoveRange, AddRange, InsertRange.
Au passage, pour enlever tous les éléments d'une collection :
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 :
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.
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).
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).
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.
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.
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 :
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.
if
(!
days.
ContainsKey
(
"Tuesday))
{
days.
Add
(
"Tuesday"
,
2
);
}
Pour lire la valeur correspondant à la clé Monday.
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 :
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).
foreach
(
KeyValuePair<
string
,
int
>
day in
days)
{
string
maday =
day.
Value;
}
Enlever un élément :
days.
Remove
(
"Monday"
);
On peut utiliser toutes les valeurs qui sont dans la ValueCollection du Dictionary.
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 :
// 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.
// 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.
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.
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 :
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.
int
element=
myQ.
Peek
(
);
Lit le premier élément sans l'enlever de la Queue.
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.
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 :
Stack<
int
>
LaPile =
new
Stack<
int
>
(
) ;
Ajouter un élément en haut de la pile :
LaPile.
Push
(
2
);
Enlever un élément en haut de la pile :
int
num =
LaPile.
Pop
(
);
On peut aussi lire l'élément en haut sans l'enlever.
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 :
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 :
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 :
using
System.
Collections.
ObjectModel;
Ensuite on peut déclarer une ObservableCollection.
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 :
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.
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.
// 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é :
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 :
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) :
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.
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.
[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.
// 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 :
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).
struct
Adresse
{
public
int
Numero;
public
string
Rue;
public
string
Ville;
}
Puis on peut déclarer une variable de type structure :
Adresse MonAdresse;
On aurait pu utiliser new.
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 :
MonAdresse.
Numero=
2
;
MonAdresse.
Rue=
"Grande rue"
;
MonAdresse.
Ville=
"Lyon"
;
Il peut y avoir des constructeurs et des fonctions dans une structure.
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 :
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 :
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 :
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 :
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.
void
Doubler
(
ref
int
x,
ref
int
y)
{
x=
x*
x;
y=
y*
y;
}
Pour l'appel il faut aussi utiliser ref :
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.
void
initialise
(
out
int
y)
{
y=
5
;
}
Comment l'utiliser ?
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.
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 :
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.
On peut modifier le comportement en passant par le menu Déboguer puis Options et paramètres :
'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.
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) :
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.
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:
try
{
monElement=
array[
index];
}
catch
{
MessageBox.
Show (
"Erreur index"
);
}
On peut donner un argument à catch :
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.
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 :
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.
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 :
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" ?
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 :
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 :
// 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 :
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 :
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.
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 :
var
query =
from
i in
values
where
i %
2
==
0
select
i *
i;
est équivalent (en utilisant les méthodes d'extension) à :
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 :
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 :
List<
int
>
grades =
new
List<
int
>
{
78
,
92
,
100
,
37
,
81
};
double
average =
grades.
Average
(
);
Calculons une somme sur un tableau de Single :
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▲
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.
//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).
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.
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 :
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).
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.
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 :
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 :
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 :
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.
Random rand1 =
new
Random
(
50
);
// On aura toujours la même série
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.
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 :
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 :
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.
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.
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 :
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▲
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 :
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 :
using
System.
Globalization;
using
System.
Threading;
La culture en cours est dans CurrentCulture de System.Threading.
//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.
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 :
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.
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) :
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.
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.
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.
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.
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".
int
MaVariable =
12
;
TextBlock1.
Text =
MaVariable.
ToString
(
"E"
);
// Affiche '1,200000E+001'
"X" indique le format hexadécimal.
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.
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).
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".
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.
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é.
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.
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 :
double
MaVariable =
0
.
5
;
TextBox.
Text =
MaVariable.
ToString
(
"$#,##0.00;($#,##0.00);Zero"
);
// Affiche '$0,50'
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 :
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:
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.
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 :
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 :
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 :
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) :
Pour une méthode :
- 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.
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.
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 :
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 :
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).
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 :
public
ElementDeListe Element;
Element =
new
ElementDeListe (
);
Maintenant on peut donner une valeur aux propriétés :
Element.
Titre =
"Multiplication"
;
Element.
Formule =
"*"
;
Element.
Mode =
"sans"
;
On peut aussi utiliser le constructeur avec trois paramètres :
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.
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 :
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 :
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) :
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.
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) :
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) :
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 :
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.
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 :
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.
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.
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.
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.
static
double
DegreeToRadian
(
double
angle)
{
return
(
angle /
180
.
0
) *
Math.
PI;
}
static
double
RadianToDegree
(
double
radian)
{
return
(
radian /
Math.
PI) *
180
.
0
;
}