
|
auteurs : dev01, tomlev |
Les différents niveaux de visibilité pour une classe (ou structure, interface, etc.) en C# sont les suivants :
- public : La classe est accessible à partir de n'importe quel code, y compris dans un autre assembly
- internal : La classe n'est accessible que dans l'assembly ou elle est définie
- protected : La classe n'est accessible qu'à partir des classes qui héritent de celle qui la contient (applicable uniquement aux classes imbriquées)
- protected internal : La classe n'est accessible qu'à partir des classes qui héritent de celle qui la contient, et à partir du même assembly (applicable uniquement aux classes imbriquées)
- private : La classe n'est accessible qu'a partir de la classe qui la contient (applicable uniquement aux classes imbriquées)
Si ce n'est pas précisé, une classe est par défaut :
- interne (internal) si elle est déclarée directement dans un namespace
- privée (private) si elle est déclarée dans une autre classe (classe imbriquée)
|
lien : Niveaux d'accessibilité (MSDN)
|
|
auteur : tomlev |
Les différents niveaux de visibilité pour les membres d'une classe (ou structure) en C# sont les suivants :
- public : Le membre est accessible à partir de n'importe quel code, y compris dans un autre assembly
- internal : Le membre n'est accessible qu'à partir de l'assembly courant
- protected : Le membre n'est accessible qu'à partir des classes dérivées
- protected internal : Le membre n'est accessible qu'à partir des classes dérivées, et à partir de l'assembly courant
- private : Le membre n'est accessible qu'a partir de la classe qui le contient
Si ce n'est pas précisé, un membre d'une classe ou d'une structure est privé par défaut. Dans une interface ou un enum, les membres
sont implicitement publics (il est d'ailleurs illégal de spécifier leur niveau d'accessibilité).
|
lien : Niveaux d'accessibilité (MSDN)
|
|
auteurs : dev01, tomlev |
Une propriété est un membre d'une classe (ou structure, ou interface) qui s'utilise comme un champ,
mais peut implémenter sa propre logique pour accéder à la valeur. Une propriété se compose généralement
de 2 accesseurs, get (pour récupérer la valeur) et set (pour la modifier).
Une propriété peut être en lecture/écriture (accesseurs get et set), en lecture seule (seulement
un accesseur get), ou, plus rarement, en écriture seule (seulement un accesseur set).
Notez que selon les bonnes pratiques de Microsoft, une propriété ne doit pas effectuer de traitement lourd, et
ne doit pas renvoyer un tableau. Dans ces cas-là, on utilise plutôt une méthode
|
lien : Propriétés (MSDN)
lien : Choisir entre propriété et méthode (MSDN)
|
|
auteurs : dev01, tomlev |
Une propriété est définie ainsi :
public class A
{
private string _maVariable;
public string MaVariable
{
get
{
return _maVariable;
}
set
{
_maVariable = value;
}
}
}
|
Cette propriété permet d'accéder en lecture/écriture à la variable.
Pour un accès en lecture seule, il suffit de supprimer le set{ } et inversement pour un accès en écriture seule.
Notez que l'accesseur set prend implicitement un paramètre nommé value, du même type que la propriété.
En C# 3.0, il existe un "raccourci" pour déclarer une propriété qui n'implémente pas de logique particulière dans ses
accesseurs. C'est ce qu'on appelle une propriété "auto-implémentée" :
public string MaVariable { get; set; }
|
Il est également possible de définir séparément le niveau d'accessibilité d'un accesseur :
public string MaVariable { get; private set; }
|
De cette façon, la propriété n'est modifiable qu'à l'intérieur de la classe.
|
|
auteurs : cardi, tomlev |
Il suffit d'utiliser const devant la déclaration du champ. L'initialisation devra se faire en même temps que la déclaration car il ne sera plus possible de modifier la valeur de la variable par la suite :
const int maConstante = 2007 ;
|
Lorsqu'on fait référence à une constante dans le code, le compilateur remplace cette référence par la valeur de la constante.
|
|
auteurs : cardi, tomlev |
Bien que similaires, les constantes et les champs readonly sont 2 choses bien différentes :
-
Une constante (const) permet de définir une valeur qui sera toujours la même, partout dans le programme et à chaque exécution
Si vous faites référence à cette constante dans le code, le compilateur remplacera cette référence par la valeur de la constante.
Notez que le type d'une constante ne peut être qu'un type primitif (int, bool, ulong...) ou de type string. De plus, une constante est toujours statique (c'est implicite, donc inutile de le préciser)
-
Un champ en lecture seule (readonly) est initialisé lors de la création de l'objet (ou du type si le champ est statique), et ne change plus par la suite
On ne peut initialiser ce champ que dans le constructeur ou immédiatement lors de sa déclaration.
Contrairement à une constante, un champ readonly peut être de n'importe quel type, et peut être un membre d'instance ou un membre statique.
 |
Un champ readonly n'a pas forcément la même valeur d'un objet à l'autre, ce n'est pas une constante. Il est simplement constant après avoir été initialisé, mais
on peut l'initialiser avec différentes valeurs.
|
public class MaClasse
{
public const int MaConstante = 42 ;
public readonly string MonReadOnly;
public MaClasse (string monParam)
{
MonReadOnly = monParam;
}
}
|
|
|
auteur : cardi |
Il suffit d'utiliser this dans le header du constructeur (c'est-à-dire juste après la liste des paramètres) :
class Employee
{
private String PrenomNom;
public Employee (String param_PrenomNom)
{ PrenomNom = param_PrenomNom; }
public Employee (String param_Prenom, String param_Nom)
: this (param_Prenom + " " + param_Nom)
{ }
}
|
|
|
auteur : tomlev |
Il suffit d'utiliser base dans le header du constructeur (c'est-à-dire juste après la liste des paramètres) :
class Employee : Person
{
public Employee (String param_PrenomNom)
: base (param_PrenomNom)
{
}
}
|
|
|
auteur : dev01 |
Une classe partielle est tout simplement une classe dont la définition est séparée dans plusieurs fichiers sources distincts.
Exemple :
public partial class MaClasse
{
private List< string > infos;
}
public partial class MaClasse
{
public MaClasse ()
{
this . infos = new List< string > ();
}
}
|
Les classes partielles sont utilisées, par exemple, par Visual Studio 2005 et SharpDevelop 2.0 pour séparer le code généré par le designer graphique du code écrit par le développeur.
|
lien : L'article de Louis-Guillaume MORAND sur les nouveautés du framework 2.0
|
|
auteurs : dev01, tomlev |
Afin d'avoir une variable accessible de n'importe quel endroit de son application il faut la déclarer static.
public class Configuration
{
private static string _connectionString;
public static string ConnectionString
{
get
{
return _connectionString;
}
set
{
_connectionString = value;
}
}
}
|
Un membre statique appartient à un type et non à une instance d'un type, on peut donc y accéder simplement via le nom du type : Configuration.ConnectionString.
|
|
auteur : cardi |
Il suffit de déclarer un paramètre de type tableau, en le précédant du mot réservé params. On accède ensuite aux paramètres via le tableau :
static void Main ()
{
MaFonctionVariable (" Bonjour " );
MaFonctionVariable (" Bonjour " , " Au revoir " );
}
static void MaFonctionVariable (params String[ ] MesParams)
{
foreach (String courantString in MesParams)
Console. WriteLine (" Valeur du paramètre : {0} " , courantString);
}
|
|
lien : Mot-clé params (MSDN)
|
|
auteurs : cardi, tomlev |
Comme indiqué plus haut, on utilise le mot réservé params pour passer un nombre variable d'arguments à une fonction.
Pour pouvoir passer des types différents, il suffit de déclarer le paramètre comme étant de type Object[] : étant donné que tous les types dérivent de Object, le tableau pourra contenir des objets de n'importe quel type
static void Main ()
{
MaFonctionVariableEnType (" Bonjour " );
MaFonctionVariableEnType (" Bonjour " , 2007 );
MaFonctionVariableEnType (" Bonjour " , 2007 , true );
MaFonctionVariableEnType (" Bonjour " , 2007 , true , 10 . 1 );
}
static void MaFonctionVariableEnType (params Object[ ] MesParams)
{
foreach (Object courantObject in MesParams)
Console. WriteLine (" Valeur du paramètre : {0} " , courantObject);
}
|
|
|
auteurs : dev01, tomlev |
Les génériques sont une fonctionnalité apparue en C# 2.0, similaire aux templates de C++ (mais avec des différences notables).
Ils permettent de définir des types ou des méthodes sans connaitre à l'avance le type de certains éléments.
Cela permet d'avoir des classes ou méthodes qui offrent la même fonctionnalité pour différents types, de données, plutôt que
d'avoir à redévelopper la même fonctionnalité pour chaque type.
On déclare un type ou une méthode générique en déclarant les paramètres de type entre '<' et '>'. Exemple pour une classe :
public class MaClasseGenerique< T>
{
public T Valeur { get; set; }
}
|
Exemple pour une méthode :
public T GetValue< T> ()
{
return (T)value;
}
|
Une fois déclaré, le type générique peut être utilisé partout dans la classe ou la méthode comme n'importe quel autre type.
Pour utiliser un type ou une méthode générique, il faut remplacer le paramètre de type par le type réel qu'on souhaite utiliser :
MaClasseGenerique< int > x = new MaClasseGenerique< int > ();
x. Valeur = 3 ;
x. Valeur = " hello " ;
|
L'utilisation la plus fréquente des génériques est la manipulation de collections fortement typées, comme List<T> :
List< string > stringList = new List< string > ();
stringList. Add (" Salut " );
stringList. Add (1 );
|
|
lien : L'article de Louis-Guillaume Morand sur les nouveauté du framework 2.0
lien : Génériques (Guide de programmation C#)
|
|
auteurs : cardi, tomlev |
Lorsque vous développez une classe ou une méthode générique, il est possible que vous écriviez quelque chose de ce genre :
public class maClasse< K>
{
protected K paramK;
public maClasse ()
{
paramK = new K ();
}
}
|
Lors de la compilation, Visual Studio vous dira qu'il est impossible de créer une instance de K. Cela est dû au fait que vous ne pouvez pas être sûr que le type K (qui sera substitué par un vrai type par la suite) possède un constructeur sans aucun paramètre.
C'est là qu'intervient le principe des contraintes sur les classes génériques. Grâce à where, il est possible de spécifier que le type K doit posséder un constructeur sans paramètre :
public class maClasse< K> where K : new ()
{
}
|
Il est également possible de spécifier que le type K doit hériter d'une classe ou implémenter une interface.
Cela permet d'accéder aux membres définis dans la classe de base ou l'interface.
public class maClasse< K> where K : new (), IDisposable
{
protected K paramK;
public maClasse ()
{
paramK = new K ();
paramK. Dispose ();
}
}
|
Notez qu'il n'est pas possible de spécifier comme type de base Enum ou Delegate, c'est une limitation imposée par le compilateur.
Un dernier type de contrainte permet de spécifier que le paramètre de type doit être un type valeur (struct) ou un type référence (class) :
public class maClasseReference< K> where K : class
{
}
public class maClasseValeur< K> where K : struct
{
}
maClasseReference< string > ref1 = new maClasseReference< string > ();
maClasseReference< int > ref2 = new maClasseReference< int > ();
maClasseValeur< int > val1 = new maClasseValeur< int > ();
maClasseValeur< string > val2 = new maClasseValeur< string > ();
|
|
|
auteurs : cardi, tomlev |
Il est possible d'utiliser le mot-clé default afin de récupérer la valeur par défaut d'un type. Pour un type référence, nous récupérerons null alors que pour les types valeurs, cela dépend (0 pour int, float, etc.).
Partant de ce principe, il suffit de tester la nullité de la valeur par défaut récupérée pour savoir si on a affaire à un type référence ou valeur.
public class MaClasseGenerique< K>
{
public void TypeK ()
{
if (default (K) ! = null )
Console. WriteLine (" K est un type valeur ! " );
else
Console. WriteLine (" K est un type référence ! " );
}
}
class Program
{
static void Main (string [ ] args)
{
(new MaClasseGenerique< int > ()). TypeK ();
(new MaClasseGenerique< string > ()). TypeK ();
}
}
|
Une autre possibilité est d'utiliser la réflexion :
public class MaClasseGenerique< K>
{
public void TypeK ()
{
if (typeof (K). IsValueType)
Console. WriteLine (" K est un type valeur ! " );
else
Console. WriteLine (" K est un type référence ! " );
}
}
|
Notez que l'usage de la réflexion peut être coûteux en termes de performance, l'utilisation de default est donc préférable.
Lorsque c'est possible, l'idéal est de spécifier dans les contraintes de type générique s'il s'agit d'un type valeur ou d'un type référence (voir cette question).
|
|
auteur : cardi |
Il suffit de la qualifier du mot réservé sealed.
sealed class ClassImpossibleAHeriter
{
}
|
|
|
auteurs : nico-pyright(c), tomlev |
L'instruction foreach permet de parcourir facilement n'importe quelle collection d'objets.
Pour rendre une classe énumérable avec foreach, il suffit d'implémenter l'interface IEnumerable.
Cette interface définit une seule méthode, nommée GetEnumerator, qui renvoie un objet implémentant l'interface IEnumerator.
Voici un exemple d'une classe qui peut être parcourue avec foreach et renvoie une série de nombres :
class LostNumbers : IEnumerable
{
private int [ ] _numbers = new [ ] { 4 , 8 , 15 , 16 , 23 , 42 } ;
public IEnumerator GetEnumerator ()
{
return new LostNumbersEnumerator (_numbers);
}
private class LostNumbersEnumerator : IEnumerator
{
private int _index;
private int [ ] _numbers;
public LostNumbersEnumerator (int [ ] numbers)
{
_numbers = numbers;
_index = - 1 ;
}
public void Reset ()
{
_index = - 1 ;
}
public bool MoveNext ()
{
_index+ + ;
return _index < _numbers. Length;
}
public object Current
{
get { return _numbers[ _index] ; }
}
}
}
|
On peut maintenant utiliser la classe de la façon suivante :
LostNumbers numbers = new LostNumbers ();
foreach (int n in numbers)
{
Console. WriteLine (n);
}
|
|
lien : System.Collections.IEnumerable (MSDN)
lien : System.Collections.IEnumerator (MSDN)
|
|
auteur : tomlev |
C# 2.0 introduit des nouveautés importantes en ce qui concerne l'énumération de collections :
d'une part, les interfaces génériques IEnumerable<T> et IEnumerator<T> qui permettent d'énumérer une collection de façon fortement typée,
et d'autre part les itérateurs qui permettent d'implémenter des énumerateurs beaucoup plus facilement.
On crée un bloc itérateur en utilisant le mot-clé yield.
Si on reprend l'exemple précédent en utilisant les fonctionnalités de C# 2.0, le code devient :
class LostNumbers : IEnumerable< int >
{
private int [ ] _numbers = new [ ] { 4 , 8 , 15 , 16 , 23 , 42 } ;
public IEnumerator< int > GetEnumerator ()
{
for (int i = 0 ; i < _numbers. Length)
{
yield return _numbers[ i] ;
}
}
IEnumerator IEnumerable. GetEnumerator ()
{
return this . GetEnumerator ();
}
}
|
Le compilateur refactorise le code ci-dessus en créant une classe qui implémente IEnumerator<int> de façon à obtenir le comportement voulu.
Notez qu'il est aussi possible d'utiliser les itérateurs pour n'importe quelle méthode
qui doit renvoyer une séquence d'objets :
public IEnumerable< int > GetLostNumbers ()
{
yield return 4 ;
yield return 8 ;
yield return 15 ;
yield return 16 ;
yield return 23 ;
yield return 42 ;
}
|
|
lien : System.Collections.Generic.IEnumerable<T> (MSDN)
lien : System.Collections.Generic.IEnumerator<T> (MSDN)
lien : Itérateurs (Guide de programmation C#)
|
Consultez les autres F.A.Q's


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