Recherche…


Introduction

Les mots - clés sont des identificateurs réservés et prédéfinis ayant une signification particulière pour le compilateur. Ils ne peuvent pas être utilisés comme identificateurs dans votre programme sans le préfixe @ . Par exemple, @if est un identifiant légal mais pas le mot if clé if .

Remarques

C # possède une collection prédéfinie de "mots clés" (ou mots réservés) qui ont chacun une fonction spéciale. Ces mots ne peuvent pas être utilisés comme identificateurs (noms pour les variables, les méthodes, les classes, etc.) à moins qu'ils ne soient préfixés par @ .

En dehors de cela, C # utilise également certains mots-clés pour fournir une signification spécifique au code. Ils sont appelés mots-clés contextuels. Les mots-clés contextuels peuvent être utilisés comme identificateurs et n'ont pas besoin d'être préfixés par @ lorsqu'ils sont utilisés comme identificateurs.

pilealloc

Le mot-clé stackalloc crée une région de mémoire sur la pile et renvoie un pointeur sur le début de cette mémoire. La mémoire allouée par pile est automatiquement supprimée lorsque la portée dans laquelle elle a été créée est fermée.

//Allocate 1024 bytes. This returns a pointer to the first byte.
byte* ptr = stackalloc byte[1024];

//Assign some values...
ptr[0] = 109;
ptr[1] = 13;
ptr[2] = 232;
...

Utilisé dans un contexte dangereux.

Comme avec tous les pointeurs en C #, il n'y a pas de limites à vérifier les lectures et les affectations. La lecture au-delà des limites de la mémoire allouée aura des résultats imprévisibles - elle peut accéder à un emplacement arbitraire dans la mémoire ou provoquer une exception de violation d'accès.

//Allocate 1 byte
byte* ptr = stackalloc byte[1];

//Unpredictable results...
ptr[10] = 1;
ptr[-1] = 2;

La mémoire allouée par pile est automatiquement supprimée lorsque la portée dans laquelle elle a été créée est fermée. Cela signifie que vous ne devez jamais renvoyer la mémoire créée avec stackalloc ou la stocker au-delà de la durée de vie de l'étendue.

unsafe IntPtr Leak() {
    //Allocate some memory on the stack
    var ptr = stackalloc byte[1024];

    //Return a pointer to that memory (this exits the scope of "Leak")
    return new IntPtr(ptr);
}

unsafe void Bad() {
     //ptr is now an invalid pointer, using it in any way will have
     //unpredictable results. This is exactly the same as accessing beyond
     //the bounds of the pointer.
     var ptr = Leak();
}

stackalloc ne peut être utilisé que lors de la déclaration et de l' initialisation de variables. Ce qui suit n'est pas valide:

byte* ptr;
...
ptr = stackalloc byte[1024];

Remarques:

stackalloc ne doit être utilisé que pour des optimisations de performances (pour le calcul ou l'interopérabilité). Cela est dû au fait que:

  • Le ramasse-miettes n'est pas nécessaire car la mémoire est allouée sur la pile plutôt que sur le tas - la mémoire est libérée dès que la variable est hors de portée
  • Il est plus rapide d'allouer de la mémoire sur la pile plutôt que sur le tas
  • Augmenter les chances de succès du cache sur le processeur en raison de la localisation des données

volatil

L'ajout du mot-clé volatile à un champ indique au compilateur que la valeur du champ peut être modifiée par plusieurs threads distincts. Le principal objectif du mot-clé volatile est d'empêcher les optimisations du compilateur qui supposent uniquement un accès par thread unique. L'utilisation de volatile garantit que la valeur du champ est la valeur la plus récente disponible et que la valeur n'est pas soumise à la mise en cache des valeurs non volatiles.

Il est recommandé de marquer chaque variable pouvant être utilisée par plusieurs threads comme volatile pour éviter tout comportement inattendu dû à des optimisations en arrière-plan. Considérons le bloc de code suivant:

public class Example
{
    public int x;

    public void DoStuff()
    {
        x = 5;

        // the compiler will optimize this to y = 15
        var y = x + 10;

        /* the value of x will always be the current value, but y will always be "15" */
        Debug.WriteLine("x = " + x + ", y = " + y);
    }    
}

Dans le bloc de code ci-dessus, le compilateur lit les instructions x = 5 et y = x + 10 et détermine que la valeur de y finira toujours par 15. Ainsi, il optimisera la dernière instruction comme y = 15 . Cependant, la variable x est en fait un champ public et la valeur de x peut être modifiée à l'exécution via un thread différent agissant séparément sur ce champ. Considérons maintenant ce bloc de code modifié. Notez que le champ x est maintenant déclaré comme volatile .

public class Example
{
    public volatile int x;

    public void DoStuff()
    {
        x = 5;

        // the compiler no longer optimizes this statement
        var y = x + 10;

        /* the value of x and y will always be the correct values */
        Debug.WriteLine("x = " + x + ", y = " + y);
    }    
}

Maintenant, le compilateur recherche les utilisations en lecture du champ x et s'assure que la valeur actuelle du champ est toujours extraite. Cela garantit que même si plusieurs threads lisent et écrivent dans ce champ, la valeur actuelle de x est toujours extraite.

volatile ne peut être utilisé que sur les champs de la class es ou de la struct s. Ce qui suit n'est pas valide :

public void MyMethod()
{
    volatile int x;
}

volatile ne peut être appliqué qu'aux champs des types suivants:

  • types de référence ou paramètres de type génériques connus pour être des types de référence
  • types primitifs tels que sbyte , byte , short , ushort , int , uint , char , float et bool
  • types d' sbyte basés sur byte , sbyte , short , ushort , int ou uint
  • IntPtr et UIntPtr

Remarques:

  • Le modificateur volatile est généralement utilisé pour un champ accessible par plusieurs threads sans utiliser l'instruction de verrouillage pour sérialiser l'accès.
  • Le mot clé volatile peut être appliqué aux types de référence
  • Le mot-clé volatile ne fonctionnera pas sur les primitives 64 bits sur une plate-forme 32 bits atomique. Les opérations verrouillées telles que Interlocked.Read et Interlocked.Exchange doivent toujours être utilisées pour un accès multithread sécurisé sur ces plates-formes.

fixé

L'instruction fixe corrige la mémoire dans un emplacement. Les objets en mémoire sont généralement déplacés, ce qui rend le ramassage des ordures possible. Mais lorsque nous utilisons des pointeurs non sécurisés vers des adresses de mémoire, cette mémoire ne doit pas être déplacée.

  • Nous utilisons l'instruction fixed pour garantir que le garbage collector ne déplace pas les données de chaîne.

Variables fixes

var myStr = "Hello world!";

fixed (char* ptr = myStr)
{
    // myStr is now fixed (won't be [re]moved by the Garbage Collector).
    // We can now do something with ptr.
}

Utilisé dans un contexte dangereux.

Taille de tableau fixe

unsafe struct Example
{
    public fixed byte SomeField[8];
    public fixed char AnotherField[64];
}

fixed ne peut être utilisé que sur les champs d'une struct (doit également être utilisé dans un contexte non sécurisé).

défaut

Pour les classes, interfaces, délégués, tableaux, nullables (tels que int?) Et les types de pointeurs, la default(TheType) renvoie null :

class MyClass {}
Debug.Assert(default(MyClass) == null);
Debug.Assert(default(string) == null);

Pour structs et enums, default(TheType) renvoie la même chose que new TheType() :

struct Coordinates
{
    public int X { get; set; }
    public int Y { get; set; }
}

struct MyStruct
{
    public string Name { get; set; }
    public Coordinates Location { get; set; }
    public Coordinates? SecondLocation { get; set; }
    public TimeSpan Duration { get; set; }
}

var defaultStruct = default(MyStruct);
Debug.Assert(defaultStruct.Equals(new MyStruct()));
Debug.Assert(defaultStruct.Location.Equals(new Coordinates()));
Debug.Assert(defaultStruct.Location.X == 0);
Debug.Assert(defaultStruct.Location.Y == 0);
Debug.Assert(defaultStruct.SecondLocation == null);
Debug.Assert(defaultStruct.Name == null);
Debug.Assert(defaultStruct.Duration == TimeSpan.Zero);

default(T) peut être particulièrement utile lorsque T est un paramètre générique pour lequel aucune contrainte n'est présente pour décider si T est un type de référence ou un type de valeur, par exemple:

public T GetResourceOrDefault<T>(string resourceName)
{
   if (ResourceExists(resourceName))
   {
      return (T)GetResource(resourceName);
   }
   else
   {
      return default(T);
   }
}

lecture seulement

Le mot clé readonly est un modificateur de champ. Lorsqu'une déclaration de champ inclut un modificateur readonly , les affectations à ce champ ne peuvent avoir lieu que dans le cadre de la déclaration ou dans un constructeur de la même classe.

Le mot clé readonly est différent du mot clé const . Un champ const ne peut être initialisé qu'à la déclaration du champ. Un champ readonly peut être initialisé à la déclaration ou dans un constructeur. Par conséquent, les champs en readonly peuvent avoir des valeurs différentes selon le constructeur utilisé.

Le mot-clé readonly est souvent utilisé lors de l'injection de dépendances.

class Person
{
    readonly string _name;
    readonly string _surname = "Surname";

    Person(string name)
    {
        _name = name;
    }
    void ChangeName()
    {
        _name = "another name"; // Compile error
        _surname = "another surname"; // Compile error
    }
}

Note: La déclaration d'un champ readonly n'implique pas l' immuabilité . Si le champ est un type de référence, le contenu de l'objet peut être modifié. Readonly est généralement utilisé pour empêcher que l'objet soit écrasé et assigné uniquement lors de l' instanciation de cet objet.

Remarque: Dans le constructeur, un champ en lecture seule peut être réaffecté

public class Car
{
    public double Speed {get; set;}
}

//In code

private readonly Car car = new Car();

private void SomeMethod()
{
    car.Speed = 100;
}

comme

Le mot clé as est un opérateur similaire à une distribution . Si un transtypage n'est pas possible, utilisez as produit null plutôt que de provoquer une InvalidCastException .

expression as type est équivalent à expression is type ? (type)expression : (type)null avec la mise en garde as est valable uniquement sur les conversions de référence, les conversions nullables et les conversions de boxe. Les conversions définies par l'utilisateur ne sont pas prises en charge. un casting régulier doit être utilisé à la place.

Pour l'expansion ci-dessus, le compilateur génère du code tel que l' expression ne sera évaluée qu'une seule fois et utilisera une vérification de type dynamique unique (contrairement aux deux dans l'exemple ci-dessus).

as peut être utile lorsque vous attendez un argument pour faciliter plusieurs types. Plus précisément, il accorde à l'utilisateur plusieurs options - plutôt que de vérifier chaque possibilité avec is avant de lancer, ou simplement de jeter et de rattraper les exceptions. Il est recommandé d'utiliser «as» lors de la conversion / vérification d'un objet, ce qui entraînera une seule pénalité de déballage. Utiliser is pour vérifier, alors le lancer entraînera deux pénalités de unboxing.

Si un argument est censé être une instance d'un type spécifique, une distribution régulière est préférable car son objectif est plus clair pour le lecteur.

Comme un appel à as peut produire une valeur null , vérifiez toujours le résultat pour éviter une NullReferenceException .

Exemple d'utilisation

object something = "Hello";
Console.WriteLine(something as string);        //Hello
Console.Writeline(something as Nullable<int>); //null
Console.WriteLine(something as int?);          //null

//This does NOT compile:
//destination type must be a reference type (or a nullable value type)
Console.WriteLine(something as int);

Démo en direct sur .NET Fiddle

Exemple équivalent sans utiliser as :

Console.WriteLine(something is string ? (string)something : (string)null);

Ceci est utile lors de la Equals fonction Equals dans les classes personnalisées.

class MyCustomClass
{

    public override bool Equals(object obj)
    {
        MyCustomClass customObject = obj as MyCustomClass;

        // if it is null it may be really null
        // or it may be of a different type
        if (Object.ReferenceEquals(null, customObject))
        {
            // If it is null then it is not equal to this instance.
            return false;
        }

        // Other equality controls specific to class
    }

}

est

Vérifie si un objet est compatible avec un type donné, par exemple si un objet est une instance du type BaseInterface ou un type dérivant de BaseInterface :

interface BaseInterface {}
class BaseClass : BaseInterface {}
class DerivedClass : BaseClass {}

var d = new DerivedClass();
Console.WriteLine(d is DerivedClass);  // True
Console.WriteLine(d is BaseClass);     // True
Console.WriteLine(d is BaseInterface); // True
Console.WriteLine(d is object);        // True
Console.WriteLine(d is string);        // False

var b = new BaseClass();
Console.WriteLine(b is DerivedClass);  // False
Console.WriteLine(b is BaseClass);     // True
Console.WriteLine(b is BaseInterface); // True
Console.WriteLine(b is object);        // True
Console.WriteLine(b is string);        // False

Si l'intention de la distribution est d'utiliser l'objet, il est préférable d'utiliser le as mot - clé »

interface BaseInterface {}
class BaseClass : BaseInterface {}
class DerivedClass : BaseClass {}

var d = new DerivedClass();
Console.WriteLine(d is DerivedClass);  // True - valid use of 'is'
Console.WriteLine(d is BaseClass);     // True - valid use of 'is'

if(d is BaseClass){
    var castedD = (BaseClass)d;
    castedD.Method(); // valid, but not best practice
}

var asD = d as BaseClass;

if(asD!=null){
    asD.Method(); //prefered method since you incur only one unboxing penalty
}

Mais, à partir de C # 7 pattern matching fonctionnalité de pattern matching étend l'opérateur is pour rechercher un type et déclarer une nouvelle variable en même temps. Même partie de code avec C # 7:

7.0
if(d is BaseClass asD ){
    asD.Method();
}

Type de

Renvoie le Type d'un objet sans qu'il soit nécessaire de l'instancier.

Type type = typeof(string);
Console.WriteLine(type.FullName); //System.String
Console.WriteLine("Hello".GetType() == type); //True
Console.WriteLine("Hello".GetType() == typeof(string)); //True

const

const est utilisé pour représenter des valeurs qui ne changeront jamais pendant la durée de vie du programme. Sa valeur est constante à la compilation , par opposition au mot clé readonly , dont la valeur est constante à l'exécution.

Par exemple, comme la vitesse de la lumière ne changera jamais, nous pouvons la stocker dans une constante.

const double c = 299792458;  // Speed of light

double CalculateEnergy(double mass)
{
    return mass * c * c;
}

C'est essentiellement la même chose que d'avoir une return mass * 299792458 * 299792458 , car le compilateur remplacera directement c par sa valeur constante.

Par conséquent, c ne peut pas être modifié une fois déclaré. Ce qui suit produira une erreur de compilation:

const double c = 299792458;  // Speed of light 

c = 500;  //compile-time error

Une constante peut être préfixée avec les mêmes modificateurs d’accès que les méthodes:

private const double c = 299792458;
public const double c = 299792458;
internal const double c = 299792458;

const membres const sont static par nature. Cependant, l'utilisation de static explicitement n'est pas autorisée.

Vous pouvez également définir des constantes locales à la méthode:

double CalculateEnergy(double mass)
{
    const c = 299792458;
    return mass * c * c;
}

Celles-ci ne peuvent pas être précédées d'un mot-clé private ou public , car elles sont implicitement locales à la méthode dans laquelle elles sont définies.


Tous les types ne peuvent pas être utilisés dans une déclaration const . Les types de valeur autorisés sont les types prédéfinis sbyte , byte , short , ushort , int , uint , long , ulong , char , float , double , decimal , bool et tous les types enum . Essayer de déclarer des membres const avec d'autres types de valeur (tels que TimeSpan ou Guid ) échouera à la compilation.

Pour la string type de référence prédéfinie spéciale, les constantes peuvent être déclarées avec n'importe quelle valeur. Pour tous les autres types de référence, les constantes peuvent être déclarées mais doivent toujours avoir la valeur null .


Comme les valeurs const sont connues à la compilation, elles sont autorisées en tant qu'étiquettes de case dans une instruction switch , en tant qu'arguments standard pour les paramètres facultatifs, en tant qu'arguments pour attribuer des spécifications, etc.


Si les valeurs const sont utilisées dans différents assemblys, il faut faire attention au contrôle de version. Par exemple, si l'assembly A définit un public const int MaxRetries = 3; , et l'assembly B utilise cette constante, puis si la valeur de MaxRetries est modifiée ultérieurement à 5 dans l'assembly A (qui est ensuite MaxRetries ), cette modification ne sera pas effective dans l'assembly B, sauf si l' assembly B est également recompilé (avec une référence à la nouvelle version de A).

Pour cette raison, si une valeur peut changer dans les révisions futures du programme et si la valeur doit être visible publiquement, ne déclarez pas cette valeur const sauf si vous savez que tous les assemblys dépendants seront recompilés chaque fois que quelque chose est modifié. L'alternative consiste à utiliser static readonly au lieu de const , qui est résolu à l'exécution.

espace de noms

Le mot-clé namespace est une structure d'organisation qui nous aide à comprendre comment une base de code est organisée. Les espaces de noms en C # sont des espaces virtuels plutôt que dans un dossier physique.

namespace StackOverflow
{
    namespace Documentation
    {
        namespace CSharp.Keywords
        {
            public class Program
            {
                public static void Main()
                {
                    Console.WriteLine(typeof(Program).Namespace);
                    //StackOverflow.Documentation.CSharp.Keywords
                }
            }
        }
    }
}

Les espaces de noms en C # peuvent également être écrits en syntaxe chaînée. Ce qui suit est équivalent à ci-dessus:

namespace StackOverflow.Documentation.CSharp.Keywords
{
    public class Program
    {
        public static void Main()
        {
            Console.WriteLine(typeof(Program).Namespace);
            //StackOverflow.Documentation.CSharp.Keywords
        }
    }
}

essayer, attraper, enfin lancer

try , catch , finally , et throw vous permettent de gérer les exceptions dans votre code.

var processor = new InputProcessor();

// The code within the try block will be executed. If an exception occurs during execution of
// this code, execution will pass to the catch block corresponding to the exception type.
try 
{
    processor.Process(input);
}
// If a FormatException is thrown during the try block, then this catch block
// will be executed.
catch (FormatException ex)
{
    // Throw is a keyword that will manually throw an exception, triggering any catch block that is
    // waiting for that exception type. 
    throw new InvalidOperationException("Invalid input", ex);
}
// catch can be used to catch all or any specific exceptions. This catch block,
// with no type specified, catches any exception that hasn't already been caught
// in a prior catch block.
catch
{
    LogUnexpectedException(); 
    throw; // Re-throws the original exception.
}
// The finally block is executed after all try-catch blocks have been; either after the try has
// succeeded in running all commands or after all exceptions have been caught. 
finally
{
    processor.Dispose();
}

Remarque: Le mot-clé return peut être utilisé dans le bloc try , et le bloc finally sera toujours exécuté (juste avant le retour). Par exemple:

try 
{
    connection.Open();
    return connection.Get(query);
} 
finally 
{
    connection.Close();
}

L'instruction connection.Close() sera exécutée avant que le résultat de connection.Get(query) soit renvoyé.

continuer

Transmettez immédiatement le contrôle à la prochaine itération de la construction de boucle englobante (for, foreach, do, while):

for (var i = 0; i < 10; i++)
{
    if (i < 5)
    {
        continue;
    }
    Console.WriteLine(i);
}

Sortie:

5
6
7
8
9

Démo en direct sur .NET Fiddle

var stuff = new [] {"a", "b", null, "c", "d"};

foreach (var s in stuff)
{
    if (s == null)
    {
        continue;
    }           
    Console.WriteLine(s);
}

Sortie:

une
b
c

Démo en direct sur .NET Fiddle

ref, out

Les mots-clés ref et out provoquent un argument par référence, et non par valeur. Pour les types de valeur, cela signifie que la valeur de la variable peut être modifiée par l'appelé.

int x = 5;
ChangeX(ref x);
// The value of x could be different now

Pour les types de référence, l'instance dans la variable peut non seulement être modifiée (comme c'est le cas sans la ref ), mais elle peut également être complètement remplacée:

Address a = new Address();
ChangeFieldInAddress(a);
// a will be the same instance as before, even if it is modified
CreateANewInstance(ref a);
// a could be an entirely new instance now

La principale différence entre le mot-clé out et ref est que ref exige que la variable soit initialisée par l'appelant, tandis que out passe cette responsabilité à l'appelé.

Pour utiliser un paramètre out , la définition de la méthode et la méthode appelante doivent utiliser explicitement le mot clé out .

int number = 1;
Console.WriteLine("Before AddByRef: " + number); // number = 1
AddOneByRef(ref number);
Console.WriteLine("After AddByRef: " + number);  // number = 2
SetByOut(out number);
Console.WriteLine("After SetByOut: " + number);  // number = 34

void AddOneByRef(ref int value)
{
    value++;
}

void SetByOut(out int value)
{
    value = 34;
}

Démo en direct sur .NET Fiddle

Ce qui suit ne compile pas , car out paramètres out doivent avoir une valeur assignée avant que la méthode ne retourne (elle devrait être compilée avec ref ):

void PrintByOut(out int value)
{
    Console.WriteLine("Hello!");
}

utiliser un mot clé comme modificateur générique

out mot clé out peut également être utilisé dans les paramètres de type générique lors de la définition des interfaces génériques et des délégués. Dans ce cas, le mot-clé out indique que le paramètre type est covariant.

La covariance vous permet d'utiliser un type plus dérivé que celui spécifié par le paramètre générique. Cela permet la conversion implicite des classes implémentant des interfaces variantes et la conversion implicite des types de délégué. La covariance et la contravariance sont prises en charge pour les types de référence, mais elles ne sont pas prises en charge pour les types de valeur. - MSDN

//if we have an interface like this
interface ICovariant<out R> { }

//and two variables like
ICovariant<Object> iobj = new Sample<Object>();
ICovariant<String> istr = new Sample<String>();

// then the following statement is valid
// without the out keyword this would have thrown error
iobj = istr; // implicit conversion occurs here

coché, non coché

Les mots clés checked et unchecked checked définissent la manière dont les opérations gèrent les dépassements mathématiques. Le "dépassement" dans le contexte des mots-clés checked et unchecked se produit lorsqu'une opération arithmétique de type entier génère une valeur dont l'amplitude est supérieure à celle que le type de données cible peut représenter.

Lorsque le débordement se produit dans un bloc checked (ou lorsque le compilateur est configuré pour utiliser l'arithmétique vérifiée globalement), une exception est émise pour avertir d'un comportement indésirable. Pendant ce temps, dans un bloc unchecked , le dépassement de capacité est silencieux: aucune exception n'est levée et la valeur est simplement contournée par la limite opposée. Cela peut conduire à des bogues subtils et difficiles à trouver.

Comme la plupart des opérations arithmétiques sont effectuées sur des valeurs trop petites ou trop grandes pour déborder, la plupart du temps, il n'est pas nécessaire de définir explicitement un bloc comme checked . Des précautions doivent être prises lors du calcul arithmétique sur des entrées non limitées susceptibles de provoquer un débordement, par exemple lors du calcul arithmétique dans des fonctions récursives ou lors de la saisie d’une entrée utilisateur.

Ni checked ni unchecked n'affecte les opérations arithmétiques en virgule flottante.

Lorsqu'un bloc ou une expression est déclaré unchecked , toutes les opérations arithmétiques qu'il contient sont autorisées à déborder sans provoquer d'erreur. Un exemple où ce comportement est souhaité serait le calcul d'une somme de contrôle, où la valeur est autorisée à "boucler" pendant le calcul:

byte Checksum(byte[] data) {
    byte result = 0;
    for (int i = 0; i < data.Length; i++) {
        result = unchecked(result + data[i]); // unchecked expression
    }
    return result;
}

L'une des utilisations les plus courantes de la object.GetHashCode() unchecked est l'implémentation d'une substitution personnalisée pour object.GetHashCode() , un type de somme de contrôle. Vous pouvez voir l'utilisation du mot clé dans les réponses à cette question: Quel est le meilleur algorithme pour un System.Object.GetHashCode surchargé? .

Lorsqu'un bloc ou une expression est déclaré comme checked , toute opération arithmétique qui provoque un dépassement entraîne une OverflowException .

int SafeSum(int x, int y) {
    checked { // checked block
        return x + y; 
    }
}

Les deux cochés et non cochés peuvent être sous forme de bloc et d'expression.

Les blocs cochés et non contrôlés n'affectent pas les méthodes appelées, seuls les opérateurs appelés directement dans la méthode actuelle. Par exemple, les Enum.ToObject() , Convert.ToInt32() et les opérateurs définis par l'utilisateur ne sont pas affectés par les contextes vérifiés / non vérifiés personnalisés.

Remarque : Le comportement par défaut du débordement par défaut (coché ou non) peut être modifié dans les propriétés du projet ou via le commutateur de ligne de commande / checked [+ | -] . Il est courant de vérifier les opérations pour les versions de débogage et de les désactiver pour les versions validées. Les mots clés checked et unchecked checked unchecked seraient alors utilisés que lorsqu'une approche par défaut ne s'applique pas et que vous avez besoin d'un comportement explicite pour garantir l'exactitude.

aller à

goto peut être utilisé pour accéder à une ligne spécifique du code, spécifiée par une étiquette.

goto tant que:

Étiquette:

void InfiniteHello()
{
    sayHello:
    Console.WriteLine("Hello!");
    goto sayHello;
}

Démo en direct sur .NET Fiddle

Déclaration de cas:

enum Permissions { Read, Write };

switch (GetRequestedPermission())
{
    case Permissions.Read:
        GrantReadAccess();
        break;

    case Permissions.Write:
        GrantWriteAccess();
        goto case Permissions.Read; //People with write access also get read
}

Démo en direct sur .NET Fiddle

Ceci est particulièrement utile pour exécuter plusieurs comportements dans une instruction switch, car C # ne prend pas en charge les blocs de cas en cascade .

Réessayer d'exception

var exCount = 0;
retry:
try
{
    //Do work
}
catch (IOException)
{
    exCount++;
    if (exCount < 3)
    {
        Thread.Sleep(100);
        goto retry;
    }
    throw;
}

Démo en direct sur .NET Fiddle

Semblable à beaucoup de langues, l'utilisation du mot-clé goto est déconseillée sauf les cas ci-dessous.

Les utilisations valides de goto qui s'appliquent à C #:

  • Cas de chute dans la déclaration de changement.

  • Pause à plusieurs niveaux. LINQ peut souvent être utilisé à la place, mais ses performances sont généralement moins bonnes.

  • Désallocation de ressources lors de l'utilisation d'objets de bas niveau non emballés. En C #, les objets de bas niveau doivent généralement être regroupés dans des classes distinctes.

  • Machines à états finis, par exemple, analyseurs syntaxiques; utilisé en interne par les machines asynchrones / attendues générées par le compilateur.

enum

Le mot clé enum indique au compilateur que cette classe hérite de la classe abstraite Enum , sans que le programmeur ait à l'hériter explicitement. Enum est un descendant de ValueType , destiné à être utilisé avec un ensemble distinct de constantes nommées.

public enum DaysOfWeek
{
    Monday,
    Tuesday,
}

Vous pouvez éventuellement spécifier une valeur spécifique pour chacun (ou certains d'entre eux):

public enum NotableYear
{
   EndOfWwI = 1918;
   EnfOfWwII = 1945,
}

Dans cet exemple, j'ai omis une valeur pour 0, c'est généralement une mauvaise pratique. Une enum aura toujours une valeur par défaut produite par conversion explicite (YourEnumType) 0 , où YourEnumType est votre type d' enume déclaré. Sans une valeur de 0 définie, un enum n'aura pas de valeur définie à l'initiation.

Le type sous-jacent de enum est int , vous pouvez changer le type sous-jacent en n'importe quel type entier, y compris byte , sbyte , short , ushort , int , uint , long et ulong . Vous trouverez ci-dessous un enum avec le type sous-jacent byte :

enum Days : byte
{
    Sunday = 0,
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday
};

Notez également que vous pouvez convertir vers / à partir du type sous-jacent simplement avec une distribution:

int value = (int)NotableYear.EndOfWwI;

Pour ces raisons, vous devriez toujours vérifier si une enum est valide lorsque vous exposez les fonctions de la bibliothèque:

void PrintNotes(NotableYear year)
{
    if (!Enum.IsDefined(typeof(NotableYear), year))
        throw InvalidEnumArgumentException("year", (int)year, typeof(NotableYear));

    // ...
}

base

Le mot clé de base est utilisé pour accéder aux membres d'une classe de base. Il est couramment utilisé pour appeler les implémentations de base des méthodes virtuelles ou pour spécifier quel constructeur de base doit être appelé.

Choisir un constructeur

public class Child : SomeBaseClass {
    public Child() : base("some string for the base class")
    {
    }
}

public class SomeBaseClass {
    public SomeBaseClass()
    {
        // new Child() will not call this constructor, as it does not have a parameter
    }
    public SomeBaseClass(string message)
    {
        // new Child() will use this base constructor because of the specified parameter in Child's constructor
        Console.WriteLine(message);
    }
}

Implémentation de la base d'appel de la méthode virtuelle

public override void SomeVirtualMethod() {
    // Do something, then call base implementation
    base.SomeVirtualMethod();
}

Il est possible d'utiliser le mot clé base pour appeler une implémentation de base à partir de n'importe quelle méthode. Cela lie l'appel de méthode directement à l'implémentation de base, ce qui signifie que même si les nouvelles classes enfant remplacent une méthode virtuelle, l'implémentation de base sera toujours appelée. Cela doit donc être utilisé avec prudence.

public class Parent
{
    public virtual int VirtualMethod()
    {
        return 1;
    }
}

public class Child : Parent
{
    public override int VirtualMethod() {
        return 11;
    }

    public int NormalMethod()
    {
        return base.VirtualMethod();
    }

    public void CallMethods()
    {
        Assert.AreEqual(11, VirtualMethod());

        Assert.AreEqual(1, NormalMethod());
        Assert.AreEqual(1, base.VirtualMethod());
    }
}

public class GrandChild : Child
{
    public override int VirtualMethod()
    {
        return 21;
    }

    public void CallAgain()
    {
        Assert.AreEqual(21, VirtualMethod());
        Assert.AreEqual(11, base.VirtualMethod());

        // Notice that the call to NormalMethod below still returns the value
        // from the extreme base class even though the method has been overridden
        // in the child class.
        Assert.AreEqual(1, NormalMethod());
    }
}

pour chaque

foreach est utilisé pour parcourir les éléments d'un tableau ou les éléments d'une collection qui implémente IEnumerable ✝.

var lines = new string[] { 
    "Hello world!", 
    "How are you doing today?", 
    "Goodbye"
};

foreach (string line in lines)
{
    Console.WriteLine(line);
}

Cela va sortir

"Bonjour le monde!"
"Comment ça va aujourd'hui?"
"Au revoir"

Démo en direct sur .NET Fiddle

Vous pouvez quitter la boucle foreach à tout moment en utilisant le mot-clé break ou passer à l'itération suivante à l'aide du mot-clé continue .

var numbers = new int[] {1, 2, 3, 4, 5, 6};

foreach (var number in numbers)
{
    // Skip if 2
    if (number == 2)
        continue;

    // Stop iteration if 5
    if (number == 5)
        break;

    Console.Write(number + ", ");
}

// Prints: 1, 3, 4, 

Démo en direct sur .NET Fiddle

Notez que l'ordre d'itération n'est garanti que pour certaines collections telles que les tableaux et la List , mais n'est pas garanti pour de nombreuses autres collections.


✝ Alors que IEnumerable est généralement utilisé pour indiquer des collections énumérables, foreach exige uniquement que la collection expose publiquement la méthode de l' object GetEnumerator() , qui doit renvoyer un objet exposant la méthode bool MoveNext() et l' object Current { get; } propriété.

params

params permet à un paramètre de méthode de recevoir un nombre variable d'arguments, c'est-à-dire zéro, un ou plusieurs arguments sont autorisés pour ce paramètre.

static int AddAll(params int[] numbers)
{
    int total = 0;
    foreach (int number in numbers)
    {
        total += number;
    }
    
    return total;
}

Cette méthode peut maintenant être appelée avec une liste typique d'arguments int , ou un tableau d'ints.

AddAll(5, 10, 15, 20);                // 50
AddAll(new int[] { 5, 10, 15, 20 });  // 50

params doivent apparaître au plus une fois et, s'ils sont utilisés, ils doivent être en dernier dans la liste des arguments, même si le type suivant est différent de celui du tableau.


Soyez prudent lorsque vous surchargez les fonctions lorsque vous utilisez le mot-clé params . C # préfère faire correspondre les surcharges plus spécifiques avant de tenter d'utiliser des surcharges avec des params . Par exemple si vous avez deux méthodes:

static double Add(params double[] numbers)
{
    Console.WriteLine("Add with array of doubles");
    double total = 0.0;
    foreach (double number in numbers)
    {
        total += number;
    }
    
    return total;
}

static int Add(int a, int b)
{
    Console.WriteLine("Add with 2 ints");
    return a + b;
}

Ensuite, la surcharge de 2 arguments spécifique aura la priorité avant d'essayer la surcharge de params .

Add(2, 3);      //prints "Add with 2 ints"
Add(2, 3.0);    //prints "Add with array of doubles" (doubles are not ints)
Add(2, 3, 4);   //prints "Add with array of doubles" (no 3 argument overload)

Pause

Dans une boucle (for, foreach, do, while) l'instruction break interrompt l'exécution de la boucle la plus interne et retourne au code qui la suit. Il peut également être utilisé avec le yield dans lequel il spécifie qu'un itérateur a pris fin.

for (var i = 0; i < 10; i++)
{
    if (i == 5)
    {
        break;
    }
    Console.WriteLine("This will appear only 5 times, as the break will stop the loop.");
}

Démo en direct sur .NET Fiddle

foreach (var stuff in stuffCollection)
{
    if (stuff.SomeStringProp == null)
        break;
    // If stuff.SomeStringProp for any "stuff" is null, the loop is aborted.
    Console.WriteLine(stuff.SomeStringProp);
}

L'instruction break est également utilisée dans les constructions à casse pour sortir d'un cas ou d'un segment par défaut.

switch(a)
{
    case 5:
        Console.WriteLine("a was 5!");
        break;

    default:
        Console.WriteLine("a was something else!");
        break;
}

Dans les instructions switch, le mot clé 'break' est requis à la fin de chaque instruction de cas. Ceci est contraire à certains langages qui permettent de passer à la prochaine déclaration de cas de la série. Des solutions de contournement pour cela incluraient des instructions «goto» ou empiler les instructions «case» de manière séquentielle.

Le code suivant donnera les numéros 0, 1, 2, ..., 9 et la dernière ligne ne sera pas exécutée. yield break signifie la fin de la fonction (pas simplement une boucle).

public static IEnumerable<int> GetNumbers()
{
    int i = 0;
    while (true) {
        if (i < 10) {
            yield return i++;
        } else {
            yield break;
        }
    }
    Console.WriteLine("This line will not be executed");
}

Démo en direct sur .NET Fiddle

Notez que contrairement à d'autres langages, il n'existe aucun moyen d'étiqueter une pause particulière en C #. Cela signifie que dans le cas de boucles imbriquées, seule la boucle la plus interne sera arrêtée:

foreach (var outerItem in outerList)
{
    foreach (var innerItem in innerList)
    {
        if (innerItem.ShoudBreakForWhateverReason)
            // This will only break out of the inner loop, the outer will continue:
            break; 
    }
}

Si vous souhaitez sortir de la boucle externe , vous pouvez utiliser l'une des stratégies suivantes:

  • Une déclaration de goto pour sortir de toute la structure en boucle.
  • Une variable d'indicateur spécifique ( shouldBreak dans l'exemple suivant) qui peut être vérifiée à la fin de chaque itération de la boucle externe.
  • Refactoring le code pour utiliser une instruction return dans le corps de la boucle la plus interne ou éviter toute la structure de la boucle imbriquée.
bool shouldBreak = false;
while(comeCondition)
{
    while(otherCondition)
    {
        if (conditionToBreak)
        {
            // Either tranfer control flow to the label below...
            goto endAllLooping;

            // OR use a flag, which can be checked in the outer loop:
            shouldBreak = true;
        }
    }

    if(shouldBreakNow)
    {
        break; // Break out of outer loop if flag was set to true
    }
}

endAllLooping: // label from where control flow will continue

abstrait

Une classe marquée avec le mot clé abstract ne peut pas être instanciée.

Une classe doit être marquée comme abstraite si elle contient des membres abstraits ou si elle hérite des membres abstraits qu'elle ne met pas en œuvre. Une classe peut être marquée comme abstraite même si aucun membre abstrait n'est impliqué.

Les classes abstraites sont généralement utilisées comme classes de base lorsqu'une partie de l'implémentation doit être spécifiée par un autre composant.

abstract class Animal 
{
    string Name { get; set; }
    public abstract void MakeSound();
}

public class Cat : Animal 
{
    public override void MakeSound()
    {
        Console.WriteLine("Meov meov");
    }
}

public class Dog : Animal 
{   
    public override void MakeSound()
    {
        Console.WriteLine("Bark bark");
    }
}

Animal cat = new Cat();       // Allowed due to Cat deriving from Animal
cat.MakeSound();              // will print out "Meov meov"    

Animal dog = new Dog();       // Allowed due to Dog deriving from Animal
dog.MakeSound();              // will print out "Bark bark"

Animal animal = new Animal(); // Not allowed due to being an abstract class

Une méthode, une propriété ou un événement marqué avec le mot-clé abstract indique que l'implémentation de ce membre doit être fournie dans une sous-classe. Comme mentionné ci-dessus, les membres abstraits ne peuvent apparaître que dans les classes abstraites.

abstract class Animal 
{
   public abstract string Name { get; set; }
}

public class Cat : Animal 
{
    public override string Name { get; set; }
}

public class Dog : Animal 
{
    public override string Name { get; set; }
}

float, double, décimal

flotte

float est un alias du type de données .NET System.Single . Il permet de stocker des nombres à virgule flottante simple précision IEEE 754. Ce type de données est présent dans mscorlib.dll qui est implicitement référencé par chaque projet C # lorsque vous les créez.

Portée approximative: -3,4 × 10 38 à 3,4 × 10 38

Précision décimale: 6-9 chiffres significatifs

Notation :

float f = 0.1259;
var f1 = 0.7895f; // f is literal suffix to represent float values 

Il convient de noter que le type float entraîne souvent des erreurs d’arrondi significatives. Dans les applications où la précision est importante, d'autres types de données doivent être pris en compte.


double

double est un alias du type de données .NET System.Double . Il représente un nombre à virgule flottante de 64 bits à double précision. Ce type de données est présent dans mscorlib.dll qui est implicitement référencé dans tout projet C #.

Gamme: ± 5,0 × 10 −324 à ± 1,7 × 10 308

Précision décimale: 15-16 chiffres significatifs

Notation :

double distance = 200.34; // a double value
double salary = 245; // an integer implicitly type-casted to double value
var marks = 123.764D; // D is literal suffix to represent double values

décimal

decimal est un alias du type de données .NET System.Decimal . Il représente un mot-clé indiquant un type de données 128 bits. Par rapport aux types à virgule flottante, le type décimal a plus de précision et une plage plus petite, ce qui le rend approprié pour les calculs financiers et monétaires. Ce type de données est présent dans mscorlib.dll qui est implicitement référencé dans tout projet C #.

Gamme: -7,9 × 10 28 à 7,9 × 10 28

Précision décimale: 28-29 chiffres significatifs

Notation :

decimal payable = 152.25m; // a decimal value
var marks = 754.24m; // m is literal suffix to represent decimal values

uint

Un entier non signé , ou uint , est un type de données numérique qui ne peut contenir que des entiers positifs. Comme son nom l'indique, il représente un entier non signé de 32 bits. Le mot-clé uint lui-même est un alias pour le type de système de type commun System.UInt32 . Ce type de données est présent dans mscorlib.dll , qui est implicitement référencé par chaque projet C # lorsque vous les créez. Il occupe quatre octets d'espace mémoire.

Les entiers non signés peuvent contenir n'importe quelle valeur de 0 à 4 294 967 295.

Exemples sur comment et maintenant ne pas déclarer les entiers non signés

uint i = 425697; // Valid expression, explicitly stated to compiler
var i1 = 789247U; // Valid expression, suffix allows compiler to determine datatype
uint x = 3.0; // Error, there is no implicit conversion

Remarque: selon Microsoft , il est recommandé d'utiliser le type de données int autant que possible, car le type de données uint n'est pas compatible avec CLS.

ce

Le mot this clé this fait référence à l'instance actuelle de la classe (objet). On peut ainsi distinguer deux variables portant le même nom, l’une au niveau de la classe (un champ) et l’autre un paramètre (ou une variable locale) d’une méthode.

public MyClass {
    int a;

    void set_a(int a)
    {
        //this.a refers to the variable defined outside of the method,
        //while a refers to the passed parameter.
        this.a = a;
    }
}

Les autres utilisations du mot clé sont le chaînage des surcharges de constructeur non statiques :

public MyClass(int arg) : this(arg, null)
{
}

et écrire des indexeurs :

public string this[int idx1, string idx2]
{
    get { /* ... */ }
    set { /* ... */ }
}

et déclarer des méthodes d'extension :

public static int Count<TItem>(this IEnumerable<TItem> source)
{
    // ...
}

S'il n'y a pas de conflit avec une variable locale ou d'un paramètre, il est une question de style que ce soit d'utiliser this ou non, si this.MemberOfType et MemberOfType seraient équivalentes dans ce cas. Voir aussi mot clé de base .

Notez que si une méthode d'extension doit être appelée sur l'instance actuelle, this est nécessaire. Par exemple, si vous êtes dans une méthode non statique d'une classe qui implémente IEnumerable<> et que vous souhaitez appeler l'extension Count avant, vous devez utiliser:

this.Count()  // works like StaticClassForExtensionMethod.Count(this)

et this ne peut pas être omis ici.

pour

Syntaxe: for (initializer; condition; iterator)

  • La boucle for est couramment utilisée lorsque le nombre d'itérations est connu.
  • Les instructions de la section d' initializer ne sont exécutées qu'une seule fois avant d'entrer dans la boucle.
  • La section condition contient une expression booléenne évaluée à la fin de chaque itération de boucle pour déterminer si la boucle doit quitter ou doit être réexécutée.
  • La section iterator définit ce qui se passe après chaque itération du corps de la boucle.

Cet exemple montre comment for peut être utilisé pour itérer sur les caractères d'une chaîne:

string str = "Hello";
for (int i = 0; i < str.Length; i++)
{
    Console.WriteLine(str[i]);                
}

Sortie:

H
e
l
l
o

Démo en direct sur .NET Fiddle

Toutes les expressions qui définissent une instruction for sont facultatives; Par exemple, l'instruction suivante est utilisée pour créer une boucle infinie:

for( ; ; )
{
    // Your code here
}

La section d' initializer peut contenir plusieurs variables, à condition qu'elles soient du même type. La section condition peut être composée de toute expression pouvant être évaluée à une bool . Et la section iterator peut effectuer plusieurs actions séparées par des virgules:

string hello = "hello";
for (int i = 0, j = 1, k = 9; i < 3 && k > 0; i++, hello += i) {
    Console.WriteLine(hello);
}

Sortie:

Bonjour
bonjour1
bonjour12

Démo en direct sur .NET Fiddle

tandis que

L'opérateur while itération sur un bloc de code jusqu'à ce que la requête conditionnelle soit égale à false ou que le code soit interrompu par une goto , return , break ou throw .

Syntaxe pour while mot clé:

while ( condition ) { bloc de code; }

Exemple:

int i = 0;
while (i++ < 5)
{
    Console.WriteLine("While is on loop number {0}.", i);
}

Sortie:

"Tant qu'il est sur la boucle numéro 1."
"Tant qu'il est sur la boucle numéro 2."
"Tant qu'il est sur la boucle numéro 3."
"Tant qu'il est sur la boucle numéro 4."
"Tant qu'il est sur la boucle numéro 5."

Démo en direct sur .NET Fiddle

Une boucle while est contrôlée par une entrée , car la condition est vérifiée avant l'exécution du bloc de code joint. Cela signifie que la boucle while n'exécuterait pas ses instructions si la condition est fausse.

bool a = false;

while (a == true)
{
    Console.WriteLine("This will never be printed.");
}

Donner une condition while sans la configurer pour qu’elle devienne fausse à un moment donné entraînera une boucle infinie ou sans fin. Dans la mesure du possible, cela doit être évité, cependant, il peut y avoir des circonstances exceptionnelles lorsque vous en avez besoin.

Vous pouvez créer une telle boucle comme suit:

while (true)
{
//...
}

Notez que le compilateur C # transformera des boucles telles que

while (true)
{
// ...
}

ou

for(;;)
{
// ...
}

dans

{
:label
// ...
goto label;
}

Notez qu'une boucle while peut avoir n'importe quelle condition, aussi complexe soit-elle, à condition qu'elle évalue (ou retourne) une valeur booléenne (bool). Il peut également contenir une fonction qui renvoie une valeur booléenne (une telle fonction est évaluée par le même type qu'une expression telle que «a == x»). Par exemple,

while (AgriculturalService.MoreCornToPick(myFarm.GetAddress()))
{
    myFarm.PickCorn();
}

revenir

MSDN: L'instruction return termine l'exécution de la méthode dans laquelle elle apparaît et renvoie le contrôle à la méthode appelante. Il peut également renvoyer une valeur facultative. Si la méthode est un type vide, l'instruction return peut être omise.

public int Sum(int valueA, int valueB)
{
    return valueA + valueB;
}


public void Terminate(bool terminateEarly)
{
    if (terminateEarly) return; // method returns to caller if true was passed in
    else Console.WriteLine("Not early"); // prints only if terminateEarly was false
}

dans

Le mot clé in a trois utilisations:

a) Dans le cadre de la syntaxe d'une instruction foreach ou de la syntaxe d'une requête LINQ

foreach (var member in sequence)
{
    // ...
}

b) Dans le contexte des interfaces génériques et des types de délégué génériques, cela signifie la contravariance pour le paramètre de type en question:

public interface IComparer<in T>
{
    // ...
}

c) Dans le contexte de LINQ, la requête fait référence à la collection interrogée

var query = from x in source select new { x.Name, x.ID, };

en utilisant

Il existe deux types d' using de l' using mots clés, à savoir l' using statement et l' using directive :

  1. en utilisant la déclaration :

    Le mot-clé using garantit que les objets qui implémentent l'interface IDisposable sont correctement éliminés après utilisation. Il existe un sujet distinct pour l' instruction using

  2. en utilisant la directive

    La directive using a trois utilisations, consultez la page msdn pour la directive using . Il existe un sujet distinct pour la directive d'utilisation .

scellé

Lorsqu'il est appliqué à une classe, le modificateur sealed empêche les autres classes d'en hériter.

class A { }
sealed class B : A { }
class C : B { } //error : Cannot derive from the sealed class

Appliqué à une méthode virtual (ou à une propriété virtuelle), le modificateur sealed empêche cette méthode (propriété) d'être remplacée dans les classes dérivées.

public class A 
{
    public sealed override string ToString() // Virtual method inherited from class Object
    {
        return "Do not override me!";
    }
}

public class B: A 
{
    public override string ToString() // Compile time error
    { 
        return "An attempt to override"; 
    }
}

taille de

Utilisé pour obtenir la taille en octets pour un type non géré

int byteSize = sizeof(byte) // 1
int sbyteSize = sizeof(sbyte) // 1
int shortSize = sizeof(short) // 2
int ushortSize = sizeof(ushort) // 2
int intSize = sizeof(int) // 4
int uintSize = sizeof(uint) // 4
int longSize = sizeof(long) // 8
int ulongSize = sizeof(ulong) // 8
int charSize = sizeof(char) // 2(Unicode)
int floatSize = sizeof(float) // 4
int doubleSize = sizeof(double) // 8
int decimalSize = sizeof(decimal) // 16
int boolSize = sizeof(bool) // 1

statique

Le modificateur static est utilisé pour déclarer un membre statique, qui n'a pas besoin d'être instancié pour être accédé, mais est simplement accessible via son nom, par exemple DateTime.Now .

static peut être utilisé avec des classes, des champs, des méthodes, des propriétés, des opérateurs, des événements et des constructeurs.

Bien qu'une instance d'une classe contienne une copie séparée de tous les champs d'instance de la classe, il n'y a qu'une seule copie de chaque champ statique.

class A
{
    static public int count = 0;

    public A()
    {
        count++;
    }
}

class Program
{
    static void Main(string[] args)
    {
        A a = new A();
        A b = new A();
        A c = new A();

        Console.WriteLine(A.count); // 3 
    }
}

count est égal au nombre total d'instances d' A classe A

Le modificateur static peut également être utilisé pour déclarer un constructeur statique pour une classe, pour initialiser des données statiques ou exécuter du code qui doit être appelé une seule fois. Les constructeurs statiques sont appelés avant que la classe ne soit référencée pour la première fois.

class A
{
    static public DateTime InitializationTime;

    // Static constructor
    static A()
    {
        InitializationTime = DateTime.Now;
        // Guaranteed to only run once
        Console.WriteLine(InitializationTime.ToString());
    }
}

Une static class est marquée avec le mot-clé static et peut être utilisée comme conteneur utile pour un ensemble de méthodes qui fonctionnent sur des paramètres, mais ne nécessite pas nécessairement d'être lié à une instance. En raison de la nature static de la classe, elle ne peut pas être instanciée, mais peut contenir un static constructor . Certaines caractéristiques d'une static class incluent:

  • Ne peut pas être hérité
  • Ne peut pas hériter d'autre chose que Object
  • Peut contenir un constructeur statique mais pas un constructeur d'instance
  • Ne peut contenir que des membres statiques
  • Est scellé

Le compilateur est également convivial et permettra au développeur de savoir si des membres de l'instance existent dans la classe. Un exemple serait une classe statique qui convertit les métriques américaines et canadiennes:

static class ConversionHelper {
    private static double oneGallonPerLitreRate = 0.264172;

    public static double litreToGallonConversion(int litres) {
        return litres * oneGallonPerLitreRate;
    }
}

Lorsque les classes sont déclarées statiques:

public static class Functions
{
  public static int Double(int value)
  {
    return value + value;
  }
}

toutes les fonctions, propriétés ou membres de la classe doivent également être déclarés statiques. Aucune instance de la classe ne peut être créée. Essentiellement, une classe statique vous permet de créer des groupes de fonctions regroupés de manière logique.

Étant donné que C # 6 static peut également être utilisé parallèlement à l’ using pour importer des membres et des méthodes statiques. Ils peuvent être utilisés sans nom de classe.

Ancienne manière, sans using static :

using System;

public class ConsoleApplication
{
    public static void Main()
    {
         Console.WriteLine("Hello World!"); //Writeline is method belonging to static class Console
    }

}

Exemple d' using static

using static System.Console;

public class ConsoleApplication
{
    public static void Main()
    {
         WriteLine("Hello World!"); //Writeline is method belonging to static class Console
    }

}

Désavantages

Bien que les classes statiques puissent être incroyablement utiles, elles comportent leurs propres avertissements:

  • Une fois que la classe statique a été appelée, la classe est chargée en mémoire et ne peut pas être exécutée via le ramasse-miettes tant que AppDomain ne contient pas la classe statique.

  • Une classe statique ne peut pas implémenter une interface.

int

int est un alias pour System.Int32 , qui est un type de données pour les entiers signés 32 bits. Ce type de données peut être trouvé dans mscorlib.dll qui est implicitement référencé par chaque projet C # lorsque vous les créez.

Gamme: -2 147 483 648 à 2 147 483 647

int int1 = -10007;
var int2 = 2132012521;     

longue

Le mot-clé long est utilisé pour représenter les entiers 64 bits signés. C'est un alias pour le type de données System.Int64 présent dans mscorlib.dll , qui est implicitement référencé par chaque projet C # lorsque vous les créez.

Toute variable longue peut être déclarée explicitement et implicitement:

long long1 = 9223372036854775806;  // explicit declaration, long keyword used
var long2 = -9223372036854775806L; // implicit declaration, 'L' suffix used

Une variable longue peut contenir n'importe quelle valeur comprise entre -9,223,372,036,854,775,808 et 9,223,372,036,854,775,807, et peut être utile dans les situations où une variable doit contenir une valeur supérieure aux limites de ce que peuvent contenir les autres variables (telles que la variable int ).

ulong

Mot-clé utilisé pour les entiers 64 bits non signés. Il représente le type de données System.UInt64 trouvé dans mscorlib.dll qui est implicitement référencé par chaque projet C # lorsque vous les créez.

Gamme: 0 à 18,446,744,073,709,551,615

ulong veryLargeInt = 18446744073609451315;
var anotherVeryLargeInt = 15446744063609451315UL;

dynamique

Le mot-clé dynamic est utilisé avec des objets typés dynamiquement . Les objets déclarés comme dynamic renoncent aux vérifications statiques à la compilation et sont évalués à l'exécution.

using System;
using System.Dynamic;

dynamic info = new ExpandoObject();
info.Id = 123;
info.Another = 456;

Console.WriteLine(info.Another);
// 456

Console.WriteLine(info.DoesntExist);
// Throws RuntimeBinderException

L'exemple suivant utilise la dynamic avec la bibliothèque Json.NET de Newtonsoft, afin de lire facilement les données d'un fichier JSON désérialisé.

try
{
    string json = @"{ x : 10, y : ""ho""}";
    dynamic deserializedJson = JsonConvert.DeserializeObject(json);
    int x = deserializedJson.x;
    string y = deserializedJson.y;
    // int z = deserializedJson.z; // throws RuntimeBinderException
}
catch (RuntimeBinderException e)
{
    // This exception is thrown when a property
    // that wasn't assigned to a dynamic variable is used
}

Il existe certaines limitations associées au mot-clé dynamique. L'un d'eux est l'utilisation de méthodes d'extension. L'exemple suivant ajoute une méthode d'extension pour string: SayHello .

static class StringExtensions
{
    public static string SayHello(this string s) => $"Hello {s}!";
}

La première approche sera de l'appeler comme d'habitude (comme pour une chaîne):

var person = "Person";
Console.WriteLine(person.SayHello());

dynamic manager = "Manager";
Console.WriteLine(manager.SayHello()); // RuntimeBinderException

Aucune erreur de compilation, mais à l'exécution, vous obtenez une RuntimeBinderException . La solution consiste à appeler la méthode d'extension via la classe statique:

var helloManager = StringExtensions.SayHello(manager);
Console.WriteLine(helloManager);

virtuel, remplacement, nouveau

virtuel et remplacement

Le mot-clé virtual permet à une méthode, une propriété, un indexeur ou un événement d'être remplacés par des classes dérivées et présente un comportement polymorphe. (Les membres sont non virtuels par défaut en C #)

public class BaseClass
{
    public virtual void Foo()
    {
        Console.WriteLine("Foo from BaseClass");
    }
}

Afin de remplacer un membre, le mot clé override est utilisé dans les classes dérivées. (Notez que la signature des membres doit être identique)

public class DerivedClass: BaseClass
{
    public override void Foo()
    {
        Console.WriteLine("Foo from DerivedClass");
    }
}

Le comportement polymorphe des membres virtuels signifie que, lorsqu’il est appelé, le membre en cours d’exécution est déterminé lors de l’exécution et non lors de la compilation. Le membre dominant dans la classe la plus dérivée de l'objet particulier est une instance exécutée.

En bref, l'objet peut être déclaré de type BaseClass au moment de la compilation, mais si à l'exécution il s'agit d'une instance de DerivedClass le membre remplacé sera exécuté:

BaseClass obj1 = new BaseClass();
obj1.Foo(); //Outputs "Foo from BaseClass"

obj1 = new DerivedClass();
obj1.Foo(); //Outputs "Foo from DerivedClass"    

La substitution d'une méthode est facultative:

public class SecondDerivedClass: DerivedClass {}

var obj1 = new SecondDerivedClass();
obj1.Foo(); //Outputs "Foo from DerivedClass"    

Nouveau

Étant donné que seuls les membres définis comme virtual sont remplaçables et polymorphes, une classe dérivée qui redéfinit un membre non virtuel peut entraîner des résultats inattendus.

public class BaseClass
{
    public void Foo()
    {
        Console.WriteLine("Foo from BaseClass");
    }
}

public class DerivedClass: BaseClass
{
    public void Foo()
    {
        Console.WriteLine("Foo from DerivedClass");
    }
}

BaseClass obj1 = new BaseClass();
obj1.Foo(); //Outputs "Foo from BaseClass"

obj1 = new DerivedClass();
obj1.Foo(); //Outputs "Foo from BaseClass" too!    

Lorsque cela se produit, le membre exécuté est toujours déterminé au moment de la compilation en fonction du type d'objet.

  • Si l'objet est déclaré de type BaseClass (même si à l'exécution est d'une classe dérivée), alors la méthode de BaseClass est exécutée
  • Si l'objet est déclaré de type DerivedClass alors la méthode de DerivedClass est exécutée.

Il s'agit généralement d'un accident (lorsqu'un membre est ajouté au type de base après qu'un membre identique ait été ajouté au type dérivé) et qu'un avertissement de compilation CS0108 est généré dans ces scénarios.

Si c'était intentionnel, le new mot-clé est utilisé pour supprimer l'avertissement du compilateur (et informez les autres développeurs de vos intentions!). le comportement reste le même, le new mot-clé supprime simplement l'avertissement du compilateur.

public class BaseClass
{
    public void Foo()
    {
        Console.WriteLine("Foo from BaseClass");
    }
}

public class DerivedClass: BaseClass
{
    public new void Foo()
    {
        Console.WriteLine("Foo from DerivedClass");
    }
}

BaseClass obj1 = new BaseClass();
obj1.Foo(); //Outputs "Foo from BaseClass"

obj1 = new DerivedClass();
obj1.Foo(); //Outputs "Foo from BaseClass" too! 

L'utilisation de la substitution n'est pas facultative

Contrairement au C ++, l'utilisation du mot clé override n'est pas facultative:

public class A
{
    public virtual void Foo()
    {
    }
}

public class B : A
{
    public void Foo() // Generates CS0108
    {
    }
}

L'exemple ci-dessus provoque également l'avertissement CS0108 , car B.Foo() ne A.Foo() pas automatiquement A.Foo() . Ajouter une override lorsque l'intention est de remplacer la classe de base et de provoquer un comportement polymorphe, d'ajouter de new lorsque vous souhaitez un comportement non polymorphe et de résoudre l'appel en utilisant le type statique. Ce dernier doit être utilisé avec prudence, car il peut entraîner une grave confusion.

Le code suivant entraîne même une erreur:

public class A
{
    public void Foo()
    {
    }
}

public class B : A
{
    public override void Foo() // Error: Nothing to override
    {
    }
}

Les classes dérivées peuvent introduire un polymorphisme

Le code suivant est parfaitement valide (bien que rare):

    public class A
    {
        public void Foo()
        {
            Console.WriteLine("A");
        }
    }

    public class B : A
    {
        public new virtual void Foo() 
        {
            Console.WriteLine("B");
        }
    }

Maintenant, tous les objets avec une référence statique de B (et ses dérivés) utilisent le polymorphisme pour résoudre Foo() , tandis que les références de A utilisent A.Foo() .

A a = new A();
a.Foo(); // Prints "A";
a = new B();
a.Foo(); // Prints "A";
B b = new B();
b.Foo(); // Prints "B";

Les méthodes virtuelles ne peuvent pas être privées

Le compilateur C # est strict en empêchant les constructions insensées. Les méthodes marquées comme virtual ne peuvent pas être privées. Parce qu'une méthode privée ne peut pas être vue à partir d'un type dérivé, elle ne peut pas non plus être écrasée. Cela ne compile pas:

public class A
{
    private virtual void Foo() // Error: virtual methods cannot be private
    {
    }
}

async, attend

Le mot-clé await été ajouté dans la version C # 5.0 prise en charge à partir de Visual Studio 2012. Il s'appuie sur TPL (Task Parallel Library), ce qui facilite considérablement le multithreading. Les mots-clés async et en await sont utilisés par paire dans la même fonction, comme indiqué ci-dessous. Le mot-clé await est utilisé pour suspendre l'exécution de la méthode asynchrone actuelle jusqu'à ce que la tâche asynchrone attendue soit terminée et / ou que ses résultats soient renvoyés. Pour utiliser le mot-clé await , la méthode qui l'utilise doit être marquée avec le mot-clé async .

L'utilisation d' async avec void est fortement déconseillée. Pour plus d'informations, vous pouvez regarder ici .

Exemple:

public async Task DoSomethingAsync()
{    
    Console.WriteLine("Starting a useless process...");
    Stopwatch stopwatch = Stopwatch.StartNew();
    int delay = await UselessProcessAsync(1000);
    stopwatch.Stop();
    Console.WriteLine("A useless process took {0} milliseconds to execute.", stopwatch.ElapsedMilliseconds);
}

public async Task<int> UselessProcessAsync(int x)
{
    await Task.Delay(x);
    return x;
}

Sortie:

"Lancer un processus inutile ..."

** ... 1 seconde de retard ... **

"Il a fallu 1000 millisecondes pour exécuter un processus inutile."

Les paires de mots-clés async et await peuvent être omises si une méthode de retour de Task ou de Task<T> ne renvoie qu'une seule opération asynchrone.

Plutôt que ceci:

public async Task PrintAndDelayAsync(string message, int delay)
{
    Debug.WriteLine(message);
    await Task.Delay(x);
}

Il est préférable de le faire:

public Task PrintAndDelayAsync(string message, int delay)
{
    Debug.WriteLine(message);
    return Task.Delay(x);
}
5.0

En C # 5.0 await ne peut pas être utilisé dans catch et finally .

6,0

Avec C # 6.0, l’ await peut être utilisée dans catch et finally .

carboniser

Un caractère est une lettre unique stockée dans une variable. C'est un type de valeur intégré qui prend deux octets d'espace mémoire. Il représente le type de données System.Char trouvé dans mscorlib.dll qui est implicitement référencé par chaque projet C # lorsque vous les créez.

Il y a plusieurs façons de faire cela.

  1. char c = 'c';
  2. char c = '\u0063'; //Unicode
  3. char c = '\x0063'; //Hex
  4. char c = (char)99;//Integral

Un caractère peut être implicitement converti en ushort, int, uint, long, ulong, float, double, ou decimal et renverra la valeur entière de ce caractère.

ushort u = c;

renvoie 99 etc.

Cependant, il n'y a pas de conversions implicites d'autres types en char. Au lieu de cela, vous devez les lancer.

ushort u = 99;
 char c = (char)u;

fermer à clé

lock fournit la sécurité des threads pour un bloc de code, de sorte qu'il ne soit accessible que par un seul thread dans le même processus. Exemple:

private static object _lockObj = new object();
static void Main(string[] args)
{
    Task.Run(() => TaskWork());
    Task.Run(() => TaskWork());
    Task.Run(() => TaskWork());

    Console.ReadKey();
}

private static void TaskWork()
{
    lock(_lockObj)
    {
        Console.WriteLine("Entered");

        Task.Delay(3000);
        Console.WriteLine("Done Delaying");

        // Access shared resources safely

        Console.WriteLine("Leaving");
    }   
}

Output:

Entered
Done Delaying
Leaving
Entered
Done Delaying
Leaving
Entered
Done Delaying
Leaving

Cas d'utilisation:

Chaque fois que vous avez un bloc de code qui peut produire des effets secondaires s'il est exécuté par plusieurs threads en même temps. Le mot clé lock ainsi qu'un objet de synchronisation partagé ( _objLock dans l'exemple) peuvent être utilisés pour empêcher cela.

Notez que _objLock ne peut pas être null et que plusieurs threads exécutant le code doivent utiliser la même instance d'objet (en en faisant un champ static ou en utilisant la même instance de classe pour les deux threads)

Du côté du compilateur, le mot clé lock est un sucre syntaxique remplacé par Monitor.Enter(_lockObj); et Monitor.Exit(_lockObj); . Donc, si vous remplacez le verrou en entourant le bloc de code avec ces deux méthodes, vous obtiendrez les mêmes résultats. Vous pouvez voir le code réel dans le sucre syntaxique dans C # - exemple de verrouillage

nul

Une variable d'un type référence peut contenir une référence valide à une instance ou une référence null. La référence null est la valeur par défaut des variables de type référence, ainsi que les types de valeur nullable.

null est le mot clé qui représente une référence null.

En tant qu'expression, il peut être utilisé pour affecter la référence null aux variables des types susmentionnés:

object a = null;
string b = null;
int? c = null;
List<int> d  = null;

Les types de valeur non nullables ne peuvent pas recevoir de référence nulle. Toutes les affectations suivantes sont invalides:

int a = null; 
float b = null;
decimal c = null;

La référence null ne doit pas être confondue avec des instances valides de différents types, telles que:

  • une liste vide ( new List<int>() )
  • une chaîne vide ( "" )
  • le nombre zéro ( 0 , 0f , 0m )
  • le caractère nul ( '\0' )

Parfois, il est utile de vérifier si quelque chose est nul ou un objet vide / par défaut. La méthode System.String.IsNullOrEmpty (String) peut être utilisée pour vérifier cela ou vous pouvez implémenter votre propre méthode équivalente.

private void GreetUser(string userName)
{
    if (String.IsNullOrEmpty(userName))
    {
        //The method that called us either sent in an empty string, or they sent us a null reference. Either way, we need to report the problem.
        throw new InvalidOperationException("userName may not be null or empty.");
    }
    else
    {
        //userName is acceptable.
        Console.WriteLine("Hello, " + userName + "!");
    }
}

interne

Le mot clé internal est un modificateur d'accès pour les types et les membres de type. Les types internes ou les membres sont accessibles uniquement dans les fichiers du même assembly

usage:

public class BaseClass 
{
    // Only accessible within the same assembly
    internal static int x = 0;
}

La différence entre les différents modificateurs d'accès est clarifiée ici

Modificateurs d'accès

Publique

Le type ou le membre est accessible par tout autre code du même assembly ou d'un autre assembly qui le référence.

privé

Le type ou le membre ne peut être accédé que par le code de la même classe ou de la même structure.

protégé

Le type ou le membre est uniquement accessible par code dans la même classe ou structure, ou dans une classe dérivée.

interne

Le type ou le membre est accessible par n'importe quel code du même assembly, mais pas d'un autre assembly.

protégé interne

Le type ou le membre est accessible par n'importe quel code du même assembly ou par toute classe dérivée d'un autre assembly.

Si aucun modificateur d'accès n'est défini, un modificateur d'accès par défaut est utilisé. Il y a donc toujours une forme de modificateur d'accès même si ce n'est pas le cas.

where peut servir à deux fins dans C #: taper la contrainte dans un argument générique et filtrer les requêtes LINQ.

Dans une classe générique, considérons

public class Cup<T>
{
    // ...
}

T s'appelle un paramètre de type. La définition de classe peut imposer des contraintes sur les types réels pouvant être fournis pour T.

Les types de contraintes suivants peuvent être appliqués:

  • type de valeur
  • Type de référence
  • constructeur par défaut
  • héritage et implémentation

type de valeur

Dans ce cas, seules les struct (ceci inclut les types de données «primitifs» tels que int , boolean etc.) peuvent être fournies.

public class Cup<T> where T : struct
{
    // ...
}

Type de référence

Dans ce cas, seuls les types de classe peuvent être fournis

public class Cup<T> where T : class
{
    // ...
}

valeur hybride / type de référence

Il est parfois souhaitable de restreindre les arguments de type à ceux disponibles dans une base de données. Celles-ci sont généralement associées à des types de valeur et à des chaînes. Comme toutes les restrictions de type doivent être respectées, il n'est pas possible de spécifier where T : struct or string (ce n'est pas une syntaxe valide). Une solution consiste à restreindre les arguments de type à IConvertible qui a des types intégrés de "... Booléen, SByte, Octet, Int16, UInt16, Int32, UInt32, Int64, UInt64, Unique, Double, Décimal, DateHeure, Char et Chaîne. " Il est possible que d'autres objets implémentent IConvertible, bien que cela soit rare dans la pratique.

public class Cup<T> where T : IConvertible
{
    // ...
}

constructeur par défaut

Seuls les types contenant un constructeur par défaut seront autorisés. Cela inclut les types de valeur et les classes qui contiennent un constructeur par défaut (sans paramètre)

public class Cup<T> where T : new
{
    // ...
}

héritage et implémentation

Seuls les types qui héritent d'une certaine classe de base ou implémentent une interface donnée peuvent être fournis.

public class Cup<T> where T : Beverage
{
    // ...
}


public class Cup<T> where T : IBeer
{
    // ...
}

La contrainte peut même référencer un autre paramètre de type:

public class Cup<T, U> where U : T
{
    // ...
}

Plusieurs contraintes peuvent être spécifiées pour un argument de type:

public class Cup<T> where T : class, new()
{
    // ...
}

Les exemples précédents montrent des contraintes génériques sur une définition de classe, mais les contraintes peuvent être utilisées partout où un argument de type est fourni: classes, structures, interfaces, méthodes, etc.

where peut aussi être une clause LINQ. Dans ce cas, il est analogue à WHERE en SQL:

int[] nums = { 5, 2, 1, 3, 9, 8, 6, 7, 2, 0 };

var query =
    from num in nums 
    where num < 5
    select num;

    foreach (var n in query)
    {
        Console.Write(n + " ");
    }
    // prints 2 1 3 2 0

externe

Le mot clé extern est utilisé pour déclarer les méthodes implémentées en externe. Cela peut être utilisé conjointement avec l'attribut DllImport pour appeler du code non géré à l'aide des services Interop. qui dans ce cas viendra avec static modificateur static

Par exemple:

using System.Runtime.InteropServices;
public class MyClass
{
    [DllImport("User32.dll")]
    private static extern int SetForegroundWindow(IntPtr point);

    public void ActivateProcessWindow(Process p)
    {
        SetForegroundWindow(p.MainWindowHandle);
    }
}

Cela utilise la méthode SetForegroundWindow importée de la bibliothèque User32.dll

Cela peut également être utilisé pour définir un alias d'assembly externe. ce qui nous permet de référencer différentes versions de mêmes composants à partir d'un seul assemblage.

Pour référencer deux assemblys avec les mêmes noms de type complets, un alias doit être spécifié à l'invite de commandes, comme suit:

/r:GridV1=grid.dll
/r:GridV2=grid20.dll

Cela crée les alias externes GridV1 et GridV2. Pour utiliser ces alias depuis un programme, référencez-les en utilisant le mot-clé extern. Par exemple:

extern alias GridV1;
extern alias GridV2;

bool

Mot-clé pour stocker les valeurs booléennes true et false . bool est un alias de System.Boolean.

La valeur par défaut d'un bool est false.

bool b; // default value is false
b = true; // true
b = ((5 + 2) == 6); // false

Pour qu'un bool autorise des valeurs nulles, il doit être initialisé en tant que booléen?.

La valeur par défaut d'un bool? est nul

bool? a // default value is null

quand

Le when est un mot clé ajouté dans C # 6 et il est utilisé pour le filtrage des exceptions.

Avant l'introduction du mot when clé when , vous auriez pu avoir une clause catch pour chaque type d'exception; avec l'ajout du mot-clé, un contrôle plus fin est désormais possible.

Une expression when est attachée à une branche catch et seulement si la condition when est true , la clause catch sera exécutée. Il est possible d'avoir plusieurs clauses catch avec les mêmes types de classes d'exception, et différentes when conditions when remplies.

private void CatchException(Action action)
{
    try
    {
        action.Invoke();
    }
    
    // exception filter
    catch (Exception ex) when (ex.Message.Contains("when"))
    {
        Console.WriteLine("Caught an exception with when");
    }

    catch (Exception ex)
    {
        Console.WriteLine("Caught an exception without when");
    }
}

private void Method1() { throw new Exception("message for exception with when"); }
private void Method2() { throw new Exception("message for general exception"); }


CatchException(Method1);
CatchException(Method2);

décoché

Le mot clé unchecked empêche le compilateur de rechercher les débordements / sous-flux.

Par exemple:

const int ConstantMax = int.MaxValue;
unchecked
{
    int1 = 2147483647 + 10;
}
int1 = unchecked(ConstantMax + 10);

Sans le mot-clé unchecked , aucune des deux opérations d'addition ne sera compilée.

Quand est-ce utile?

Ceci est utile car cela peut accélérer les calculs qui ne déborderont certainement pas, car la vérification du débordement prend du temps ou lorsqu'un comportement de débordement / débordement est souhaité (par exemple, lors de la génération d'un code de hachage).

vide

Le mot réservé "void" est un alias de type System.Void et a deux utilisations:

  1. Déclarez une méthode qui n'a pas de valeur de retour:
public void DoSomething()
{
    // Do some work, don't return any value to the caller.
}

Une méthode avec un type de retour vide peut toujours avoir le mot-clé de return dans son corps. Ceci est utile lorsque vous souhaitez quitter l'exécution de la méthode et renvoyer le flux à l'appelant:

public void DoSomething()
{
    // Do some work...

    if (condition)
        return;

    // Do some more work if the condition evaluated to false.
}
  1. Déclarez un pointeur sur un type inconnu dans un contexte non sécurisé.

Dans un contexte non sécurisé, un type peut être un type de pointeur, un type de valeur ou un type de référence. Une déclaration de type pointeur est généralement type* identifier , où le type est un type connu - c.-à-d. int* myInt , mais peut également être un void* identifier , où le type est inconnu

Notez que la déclaration d'un type de pointeur vide est déconseillée par Microsoft.

si, si ... sinon, si ... sinon si


L'instruction if est utilisée pour contrôler le flux du programme. Une instruction if identifie l'instruction à exécuter en fonction de la valeur d'une expression Boolean .

Pour une seule instruction, les braces {} sont facultatives mais recommandées.

int a = 4;
if(a % 2 == 0) 
{
     Console.WriteLine("a contains an even number");
}
// output: "a contains an even number"

Le if peut aussi avoir une clause else , qui sera exécutée si la condition est évaluée à false:

int a = 5;
if(a % 2 == 0) 
{
     Console.WriteLine("a contains an even number");
}
else
{
     Console.WriteLine("a contains an odd number");
}
// output: "a contains an odd number"

Le if ... else if construct vous permet de spécifier plusieurs conditions:

int a = 9;
if(a % 2 == 0) 
{
     Console.WriteLine("a contains an even number");
}
else if(a % 3 == 0) 
{
     Console.WriteLine("a contains an odd number that is a multiple of 3"); 
}
else
{
     Console.WriteLine("a contains an odd number");
}
// output: "a contains an odd number that is a multiple of 3"

Important de noter que si une condition est remplie dans l'exemple ci-dessus, le contrôle ignore d'autres tests et saute à la fin de cette construction si sinon.Par conséquent, l' ordre des tests est important si vous utilisez if .. else if construct

Les expressions booléennes C # utilisent une évaluation de court-circuit . Ceci est important dans les cas où l'évaluation des conditions peut avoir des effets secondaires:

if (someBooleanMethodWithSideEffects() && someOtherBooleanMethodWithSideEffects()) {
  //...
}

Il n'y a aucune garantie que someOtherBooleanMethodWithSideEffects sera réellement exécuté.

C'est également important dans les cas où des conditions préalables garantissent qu'il est "sûr" d'évaluer les versions ultérieures. Par exemple:

if (someCollection != null && someCollection.Count > 0) {
   // ..
}

La commande est très importante dans ce cas car, si nous inversons la commande:

if (someCollection.Count > 0 && someCollection != null) {

il va lancer une NullReferenceException si someCollection est null .

faire

L'opérateur do effectue une itération sur un bloc de code jusqu'à ce qu'une requête conditionnelle soit égale à false. La boucle do-while peut également être interrompue par une goto , return , break ou throw .

La syntaxe du mot clé do est la suivante:

faire { bloc de code; } while ( condition );

Exemple:

int i = 0;

do
{
    Console.WriteLine("Do is on loop number {0}.", i);
} while (i++ < 5);

Sortie:

"Do est sur la boucle numéro 1."
"Do est sur la boucle numéro 2."
"Do est sur la boucle numéro 3."
"Do est sur la boucle numéro 4."
"Do est sur la boucle numéro 5."

Contrairement à la while boucle, la boucle do-while est sortie contrôlée. Cela signifie que la boucle do-while exécute ses instructions au moins une fois, même si la condition échoue la première fois.

bool a = false;

do
{
    Console.WriteLine("This will be printed once, even if a is false.");
} while (a == true);

opérateur

La plupart des opérateurs intégrés (y compris les opérateurs de conversion) peuvent être surchargés en utilisant le mot clé d' operator avec les modificateurs public et static .

Les opérateurs se présentent sous trois formes: opérateurs unaires, opérateurs binaires et opérateurs de conversion.

Les opérateurs unaires et binaires nécessitent au moins un paramètre du même type que le type conteneur, et certains nécessitent un opérateur de correspondance complémentaire.

Les opérateurs de conversion doivent convertir vers ou à partir du type englobant.

public struct Vector32
{
    
    public Vector32(int x, int y)
    {
        X = x;
        Y = y;
    }
    
    public int X { get; }
    public int Y { get; }

    public static bool operator ==(Vector32 left, Vector32 right)
        => left.X == right.X && left.Y == right.Y;

    public static bool operator !=(Vector32 left, Vector32 right)
        => !(left == right);

    public static Vector32 operator +(Vector32 left, Vector32 right)
        => new Vector32(left.X + right.X, left.Y + right.Y);

    public static Vector32 operator +(Vector32 left, int right)
        => new Vector32(left.X + right, left.Y + right);

    public static Vector32 operator +(int left, Vector32 right)
        => right + left;

    public static Vector32 operator -(Vector32 left, Vector32 right)
        => new Vector32(left.X - right.X, left.Y - right.Y);

    public static Vector32 operator -(Vector32 left, int right)
        => new Vector32(left.X - right, left.Y - right);

    public static Vector32 operator -(int left, Vector32 right)
        => right - left;

    public static implicit operator Vector64(Vector32 vector)
        => new Vector64(vector.X, vector.Y);

    public override string ToString() => $"{{{X}, {Y}}}";

}

public struct Vector64
{

    public Vector64(long x, long y)
    {
        X = x;
        Y = y;
    }

    public long X { get; }
    public long Y { get; }

    public override string ToString() => $"{{{X}, {Y}}}";

}

Exemple

var vector1 = new Vector32(15, 39);
var vector2 = new Vector32(87, 64);
        
Console.WriteLine(vector1 == vector2); // false
Console.WriteLine(vector1 != vector2); // true
Console.WriteLine(vector1 + vector2);  // {102, 103}
Console.WriteLine(vector1 - vector2);  // {-72, -25}

struct

Un type de struct est un type de valeur généralement utilisé pour encapsuler de petits groupes de variables associées, telles que les coordonnées d'un rectangle ou les caractéristiques d'un élément dans un inventaire.

Les classes sont des types de référence, les structs sont des types de valeur.

using static System.Console;

namespace ConsoleApplication1
{
    struct Point
    {
        public int X;
        public int Y;

        public override string ToString()
        {
            return $"X = {X}, Y = {Y}";
        }

        public void Display(string name)
        {
            WriteLine(name + ": " + ToString());
        }
    }

    class Program
    {
        static void Main()
        {
            var point1 = new Point {X = 10, Y = 20};
            // it's not a reference but value type
            var point2 = point1;
            point2.X = 777;
            point2.Y = 888;
            point1.Display(nameof(point1)); // point1: X = 10, Y = 20
            point2.Display(nameof(point2)); // point2: X = 777, Y = 888

            ReadKey();
        }
    }
}

Les structures peuvent également contenir des constructeurs, des constantes, des champs, des méthodes, des propriétés, des indexeurs, des opérateurs, des événements et des types imbriqués.


Quelques suggestions de MS sur l'utilisation de struct et Quand utiliser la classe:

CONSIDÉRER

définir une structure au lieu d'une classe si les instances de ce type sont petites et généralement de courte durée ou sont généralement incorporées dans d'autres objets.

ÉVITER

définir une structure à moins que le type présente toutes les caractéristiques suivantes:

  • Il représente logiquement une valeur unique, similaire aux types primitifs (int, double, etc.)
  • Il a une taille d'instance inférieure à 16 octets.
  • C'est immuable.
  • Il ne sera pas nécessaire de l'encadrer fréquemment.

commutateur

L'instruction switch est une instruction de contrôle qui sélectionne une section de commutateur à exécuter dans une liste de candidats. Une instruction de commutateur comprend une ou plusieurs sections de commutateur. Chaque section de commutateur contient une ou plusieurs étiquettes de case suivies d'une ou plusieurs instructions. Si aucune étiquette ne contient une valeur correspondante, le contrôle est transféré dans la section default , le cas échéant. La gestion des cas n'est pas prise en charge en C # à proprement parler. Cependant, si une ou plusieurs étiquettes de case sont vides, l'exécution suivra le code du prochain bloc de case contenant du code. Cela permet de regrouper plusieurs étiquettes de case avec la même implémentation. Dans l'exemple suivant, si month est égal à 12, le code dans le case 2 sera exécuté car les étiquettes de case 12 1 et 2 sont regroupées. Si un case bloc est pas vide, une break doit être présent avant le prochain case l' étiquette, sinon le compilateur signaleront une erreur.

int month = DateTime.Now.Month; // this is expected to be 1-12 for Jan-Dec

switch (month)
{
    case 12: 
    case 1: 
    case 2:
        Console.WriteLine("Winter");
        break;
    case 3: 
    case 4: 
    case 5:
        Console.WriteLine("Spring");
        break;
    case 6: 
    case 7: 
    case 8:
        Console.WriteLine("Summer");
        break;
    case 9:     
    case 10: 
    case 11:
        Console.WriteLine("Autumn");
        break;
    default:
        Console.WriteLine("Incorrect month index");
        break;
}

Un case ne peut être étiqueté que par une valeur connue à la compilation (par exemple 1 , "str" , Enum.A ), donc une variable n'est pas une étiquette de case valide, mais une valeur const ou Enum est (ainsi que toute valeur littérale).

interface

Une interface contient les signatures des méthodes, propriétés et événements. Les classes dérivées définissent les membres car l'interface ne contient que la déclaration des membres.

Une interface est déclarée en utilisant le mot-clé interface .

interface IProduct
{
    decimal Price { get; }
}

class Product : IProduct
{
    const decimal vat = 0.2M;
    
    public Product(decimal price)
    {
        _price = price;
    }
    
    private decimal _price;
    public decimal Price { get { return _price * (1 + vat); } }
}

peu sûr

Le mot clé unsafe peut être utilisé dans les déclarations de type ou de méthode ou pour déclarer un bloc en ligne.

Le but de ce mot clé est de permettre l'utilisation du sous - ensemble non sécurisé de C # pour le bloc en question. Le sous-ensemble non sécurisé inclut des fonctionnalités telles que les pointeurs, l'allocation de pile, les tableaux de type C, etc.

Le code dangereux n'est pas vérifiable et c'est pourquoi son utilisation est déconseillée. La compilation de code non sécurisé nécessite de passer un commutateur au compilateur C #. En outre, le CLR exige que l'assembly en cours d'exécution soit totalement sécurisé.

Malgré ces limitations, le code non sécurisé a des utilisations valides pour rendre certaines opérations plus performantes (par exemple, indexation de tableaux) ou plus simples (par exemple, interopérer avec certaines bibliothèques non gérées).

Comme exemple très simple

// compile with /unsafe
class UnsafeTest
{
   unsafe static void SquarePtrParam(int* p)
   {
      *p *= *p; // the '*' dereferences the pointer.
      //Since we passed in "the address of i", this becomes "i *= i"
   }

   unsafe static void Main()
   {
      int i = 5;
      // Unsafe method: uses address-of operator (&):
      SquarePtrParam(&i); // "&i" means "the address of i". The behavior is similar to "ref i"
      Console.WriteLine(i); // Output: 25
   }
}

En travaillant avec des pointeurs, nous pouvons changer les valeurs des emplacements de mémoire directement, plutôt que de devoir les adresser par leur nom. Notez que cela nécessite souvent l'utilisation du mot clé fixed pour éviter toute corruption de mémoire car le ramasse-miettes déplace les objets (sinon, vous risquez d'obtenir l' erreur CS0212 ). Comme une variable qui a été "corrigée" ne peut pas être écrite, nous devons souvent avoir un deuxième pointeur qui pointe vers le même emplacement que le premier.

void Main()
{
    int[] intArray = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    UnsafeSquareArray(intArray);
    foreach(int i in intArray)
        Console.WriteLine(i);
}

unsafe static void UnsafeSquareArray(int[] pArr)
{
    int len = pArr.Length;

    //in C or C++, we could say
    // int* a = &(pArr[0])
    // however, C# requires you to "fix" the variable first 
    fixed(int* fixedPointer = &(pArr[0]))
    {
        //Declare a new int pointer because "fixedPointer" cannot be written to.
        // "p" points to the same address space, but we can modify it
        int* p = fixedPointer;

        for (int i = 0; i < len; i++)
        {
            *p *= *p; //square the value, just like we did in SquarePtrParam, above
            p++;      //move the pointer to the next memory space.
                      // NOTE that the pointer will move 4 bytes since "p" is an
                      // int pointer and an int takes 4 bytes

            //the above 2 lines could be written as one, like this:
            // "*p *= *p++;"
        }
    }
}

Sortie:

1
4
9
16
25
36
49
64
81
100

unsafe permet également l'utilisation de stackalloc qui allouera de la mémoire sur la pile comme _alloca dans la bibliothèque d'exécution C. Nous pouvons modifier l'exemple ci-dessus pour utiliser stackalloc comme suit:

unsafe void Main()
{
    const int len=10;
    int* seedArray = stackalloc int[len];
    
    //We can no longer use the initializer "{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}" as before.
    // We have at least 2 options to populate the array. The end result of either
    // option will be the same (doing both will also be the same here).

    //FIRST OPTION:
    int* p = seedArray; // we don't want to lose where the array starts, so we
                        // create a shadow copy of the pointer
    for(int i=1; i<=len; i++)
        *p++ = i;
    //end of first option

    //SECOND OPTION:
    for(int i=0; i<len; i++)
        seedArray[i] = i+1;
    //end of second option

    UnsafeSquareArray(seedArray, len);
    for(int i=0; i< len; i++)
        Console.WriteLine(seedArray[i]);
}

//Now that we are dealing directly in pointers, we don't need to mess around with
// "fixed", which dramatically simplifies the code
unsafe static void UnsafeSquareArray(int* p, int len)
{
    for (int i = 0; i < len; i++)
        *p *= *p++;
}

(La sortie est la même que ci-dessus)

implicite

Le mot clé implicit est utilisé pour surcharger un opérateur de conversion. Par exemple, vous pouvez déclarer une classe Fraction qui doit automatiquement être convertie en double lorsque cela est nécessaire et qui peut être automatiquement convertie à partir de l' int :

class Fraction(int numerator, int denominator)
{
    public int Numerator { get; } = numerator;
    public int Denominator { get; } = denominator;
    // ...
    public static implicit operator double(Fraction f)
    {
        return f.Numerator / (double) f.Denominator;
    }
    public static implicit operator Fraction(int i)
    {
        return new Fraction(i, 1);
    }
}

vrai faux

Les mots-clés true et false ont deux utilisations:

  1. En tant que valeurs booléennes littérales
var myTrueBool = true;
var myFalseBool = false;
  1. En tant qu'opérateurs pouvant être surchargés
public static bool operator true(MyClass x)
{
    return x.value >= 0;
}

public static bool operator false(MyClass x)
{
    return x.value < 0;
}

La surcharge de l'opérateur false était utile avant C # 2.0, avant l'introduction des types Nullable .
Un type qui surcharge l'opérateur true doit également surcharger l'opérateur false .

chaîne

string est un alias du type de données .NET, System.String , qui permet de stocker du texte (séquences de caractères).

Notation:

string a = "Hello";
var b = "world";
var f = new string(new []{ 'h', 'i', '!' }); // hi!

Chaque caractère de la chaîne est codé en UTF-16, ce qui signifie que chaque caractère nécessite au moins 2 octets d'espace de stockage.

ushort

Type numérique utilisé pour stocker des entiers positifs à 16 bits. ushort est un alias de System.UInt16 et occupe 2 octets de mémoire.

La plage valide est comprise entre 0 et 65535 .

ushort a = 50; // 50
ushort b = 65536; // Error, cannot be converted
ushort c = unchecked((ushort)65536); // Overflows (wraps around to 0)

sbyte

Type numérique utilisé pour stocker des entiers signés à 8 bits. sbyte est un alias pour System.SByte et occupe 1 octet de mémoire. Pour l'équivalent non signé, utilisez l' byte .

La plage valide est comprise entre -127 et 127 (le reste est utilisé pour stocker le signe).

sbyte a = 127; // 127
sbyte b = -127; // -127
sbyte c = 200; // Error, cannot be converted
sbyte d = unchecked((sbyte)129); // -127 (overflows)

var

Une variable locale implicitement typée qui est fortement typée comme si l'utilisateur avait déclaré le type. Contrairement aux autres déclarations de variables, le compilateur détermine le type de variable qu'il représente en fonction de la valeur qui lui est attribuée.

var i = 10; // implicitly typed, the compiler must determine what type of variable this is
int i = 10; // explicitly typed, the type of variable is explicitly stated to the compiler

// Note that these both represent the same type of variable (int) with the same value (10).

Contrairement aux autres types de variables, les définitions de variable avec ce mot-clé doivent être initialisées lors de la déclaration. Cela est dû au mot-clé var représentant une variable implicitement typée.

var i;
i = 10;

// This code will not run as it is not initialized upon declaration.

Le mot-clé var peut également être utilisé pour créer de nouveaux types de données à la volée. Ces nouveaux types de données sont connus sous le nom de types anonymes . Ils sont très utiles, car ils permettent à un utilisateur de définir un ensemble de propriétés sans avoir à déclarer explicitement un type d'objet en premier.

Type anonyme simple

var a = new { number = 1, text = "hi" };

Requête LINQ qui renvoie un type anonyme

public class Dog
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public class DogWithBreed
{
    public Dog Dog { get; set; }
    public string BreedName  { get; set; }
}

public void GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
             join b in db.Breeds on d.BreedId equals b.BreedId
             select new 
                    {
                        DogName = d.Name,
                        BreedName = b.BreedName
                    };

    DoStuff(result);
}

Vous pouvez utiliser le mot-clé var dans l'instruction foreach

public bool hasItemInList(List<String> list, string stringToSearch)
{
    foreach(var item in list)
    {
        if( ( (string)item ).equals(stringToSearch) )
            return true;
    }

    return false;
}

déléguer

Les délégués sont des types qui représentent une référence à une méthode. Ils sont utilisés pour transmettre des méthodes comme arguments à d'autres méthodes.

Les délégués peuvent contenir des méthodes statiques, des méthodes d'instance, des méthodes anonymes ou des expressions lambda.

class DelegateExample
{
    public void Run()
    {
        //using class method
        InvokeDelegate( WriteToConsole ); 
        
        //using anonymous method
        DelegateInvoker di = delegate ( string input ) 
        { 
            Console.WriteLine( string.Format( "di: {0} ", input ) );
            return true; 
        };
        InvokeDelegate( di ); 
        
        //using lambda expression
        InvokeDelegate( input => false ); 
    }

    public delegate bool DelegateInvoker( string input );

    public void InvokeDelegate(DelegateInvoker func)
    {
        var ret = func( "hello world" );
        Console.WriteLine( string.Format( " > delegate returned {0}", ret ) );
    }

    public bool WriteToConsole( string input )
    {
        Console.WriteLine( string.Format( "WriteToConsole: '{0}'", input ) );
        return true;
    }
}

Lors de l'attribution d'une méthode à un délégué, il est important de noter que la méthode doit avoir le même type de retour et les mêmes paramètres. Cela diffère de la surcharge de méthode «normale», où seuls les paramètres définissent la signature de la méthode.

Les événements sont construits au-dessus des délégués.

un événement

Un event permet au développeur d’implémenter un modèle de notification.

Exemple simple

public class Server
{
    // defines the event
    public event EventHandler DataChangeEvent;

    void RaiseEvent()
    {
        var ev = DataChangeEvent;
        if(ev != null)
        {
            ev(this, EventArgs.Empty);
        }
    }
}

public class Client
{
    public void Client(Server server)
    {
        // client subscribes to the server's DataChangeEvent
        server.DataChangeEvent += server_DataChanged;
    }

    private void server_DataChanged(object sender, EventArgs args)
    {
        // notified when the server raises the DataChangeEvent
    }
}

Référence MSDN

partiel

Le mot-clé partial peut être utilisé lors de la définition de type de class, struct ou interface pour permettre de diviser la définition de type en plusieurs fichiers. Ceci est utile pour incorporer de nouvelles fonctionnalités dans le code généré automatiquement.

File1.cs

namespace A
{
    public partial class Test
    {
        public string Var1 {get;set;}
    }
}

File2.cs

namespace A
{
    public partial class Test
    {
        public string Var2 {get;set;}
    }
}

Remarque: une classe peut être divisée en un nombre quelconque de fichiers. Cependant, toute déclaration doit être sous le même espace de noms et le même assembly.

Les méthodes peuvent également être déclarées partielles en utilisant le mot-clé partial . Dans ce cas, un fichier contiendra uniquement la définition de la méthode et un autre fichier contiendra l'implémentation.

Une méthode partielle a sa signature définie dans une partie d'un type partiel et son implémentation est définie dans une autre partie du type. Les méthodes partielles permettent aux concepteurs de classes de fournir des hooks de méthodes, similaires aux gestionnaires d'événements, que les développeurs peuvent décider d'implémenter ou non. Si le développeur ne fournit pas d'implémentation, le compilateur supprime la signature au moment de la compilation. Les conditions suivantes s'appliquent aux méthodes partielles:

  • Les signatures dans les deux parties du type partiel doivent correspondre.
  • La méthode doit retourner vide.
  • Aucun modificateur d'accès n'est autorisé. Les méthodes partielles sont implicitement privées.

- MSDN

File1.cs

namespace A
{
    public partial class Test
    {
        public string Var1 {get;set;}
        public partial Method1(string str);
    }
}

File2.cs

namespace A
{
    public partial class Test
    {
        public string Var2 {get;set;}
        public partial Method1(string str)
        {
            Console.WriteLine(str);
        }
    }
}

Remarque: Le type contenant la méthode partielle doit également être déclaré partiel.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow