IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
logo
Sommaire > Le langage C# > Classes et membres de classe
        Quelles sont les visibilités de classe en C# ?
        Quelles sont les visibilités des membres de classe en C# ?
        Qu'est-ce qu'une propriété ?
        Comment créer une propriété ?
        Comment déclarer un champ en tant que constante ?
        Quelle est la différence entre les champs const et readonly ?
        Comment appeler un constructeur à partir d'un autre constructeur de la même classe ?
        Comment appeler un constructeur de la classe de base ?
         Qu'est-ce qu'une classe partielle ?
        Comment déclarer une variable de manière globale ?
        Comment passer un nombre variable d'arguments à une fonction ?
        Comment passer un nombre variable d'arguments à une fonction avec des types différents ?
         Qu'est-ce qu'un 'générique' ?
        Comment spécifier des contraintes sur des classes ou méthodes génériques ?
        Comment tester si on a affaire à un type valeur ou référence dans une classe générique ?
        Comment empêcher une classe d'être dérivée ?
        Comment faire pour que sa classe soit énumérable avec foreach ?
         Comment créer un itérateur ?



Quelles sont les visibilités de classe en C# ?
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 : fr Niveaux d'accessibilité (MSDN)

Quelles sont les visibilités des membres de classe en C# ?
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 : fr Niveaux d'accessibilité (MSDN)

Qu'est-ce qu'une propriété ?
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 : fr Propriétés (MSDN)
lien : fr Choisir entre propriété et méthode (MSDN)

Comment créer une propriété ?
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.


Comment déclarer un champ en tant que constante ?
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.


Quelle est la différence entre les champs const et readonly ?
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.
info 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)
      {
      	  // initialisation dans le constructeur
          MonReadOnly = monParam;
      }
    }
      
        

Comment appeler un constructeur à partir d'un autre constructeur de la même classe ?
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)
          { }
      }
      
        

Comment appeler un constructeur de la classe de base ?
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) // passe le paramètre au constructeur de la classe Person
          {
		  }
      }
      
        

Qu'est-ce qu'une classe partielle ?
auteur : dev01
Une classe partielle est tout simplement une classe dont la définition est séparée dans plusieurs fichiers sources distincts. Exemple :

          
    //Fichier MaClasse.1.cs
    public partial class MaClasse
    {
        private List<string> infos;
    }

	//Fichier MaClasse.2.cs
    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

Comment déclarer une variable de manière globale ?
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.


Comment passer un nombre variable d'arguments à une fonction ?
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 : fr Mot-clé params (MSDN)

Comment passer un nombre variable d'arguments à une fonction avec des types différents ?
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);
      }
      
        

Qu'est-ce qu'un "générique" ?
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"; // impossible car Valeur est de type int

        
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"); // ok
stringList.Add(1); // impossible car c'est une liste d'objets de type String

        
lien : L'article de Louis-Guillaume Morand sur les nouveauté du framework 2.0
lien : fr Génériques (Guide de programmation C#)

Comment spécifier des contraintes sur des classes ou méthodes génériques ?
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>(); // OK, string est un type référence
	maClasseReference<int> ref2 = new maClasseReference<int>(); // impossible, int est un type valeur

	maClasseValeur<int> val1 = new maClasseValeur<int>(); // OK, int est un type valeur
	maClasseValeur<string> val2 = new maClasseValeur<string>(); // impossible, string est un type référence

        

Comment tester si on a affaire à un type valeur ou référence dans une classe générique ?
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).


Comment empêcher une classe d'être dérivée ?
auteur : cardi
Il suffit de la qualifier du mot réservé sealed.

          
      sealed class ClassImpossibleAHeriter
      { 
          // ...
      }
      
        

Comment faire pour que sa classe soit énumérable avec foreach ?
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 : fr System.Collections.IEnumerable (MSDN)
lien : fr System.Collections.IEnumerator (MSDN)

Comment créer un itérateur ?
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];
        }
    }

    // IEnumerable<T> hérite de IEnumerable, on doit donc définir aussi la méthode
    // non générique GetEnumerator par implémentation explicite de l'interface
    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 : fr System.Collections.Generic.IEnumerable<T> (MSDN)
lien : fr System.Collections.Generic.IEnumerator<T> (MSDN)
lien : fr Itérateurs (Guide de programmation C#)


Consultez les autres F.A.Q's


Valid XHTML 1.0 TransitionalValid CSS!

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 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.