C# Language
Mots clés
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 @
.
-
abstract
-
as
-
base
-
bool
-
break
-
byte
-
case
-
catch
-
char
-
checked
-
class
-
const
-
continue
-
decimal
-
default
-
delegate
-
do
-
double
-
else
-
enum
-
event
-
explicit
-
extern
-
false
-
finally
-
fixed
-
float
-
for
-
foreach
-
goto
-
if
-
implicit
-
in
-
int
-
interface
-
internal
-
is
-
lock
-
long
-
namespace
-
new
-
null
-
object
-
operator
-
out
-
override
-
params
-
private
-
protected
-
public
-
readonly
-
ref
-
return
-
sbyte
-
sealed
-
short
-
sizeof
-
stackalloc
-
static
-
string
-
struct
-
switch
-
this
-
throw
-
true
-
try
-
typeof
-
uint
-
ulong
-
unchecked
-
unsafe
-
ushort
-
using
(directive) -
using
(statement) -
virtual
-
void
-
volatile
-
when
-
while
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() {volatileint 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
etbool
- types d'
sbyte
basés surbyte
,sbyte
,short
,ushort
,int
ouuint
-
IntPtr
etUIntPtr
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 queInterlocked.Read
etInterlocked.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:
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
ré
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
:
en utilisant la déclaration :
Le mot-clé
using
garantit que les objets qui implémentent l'interfaceIDisposable
sont correctement éliminés après utilisation. Il existe un sujet distinct pour l' instruction usingen 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 deBaseClass
est exécutée - Si l'objet est déclaré de type
DerivedClass
alors la méthode deDerivedClass
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);
}
En C # 5.0 await
ne peut pas être utilisé dans catch
et finally
.
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.
-
char c = 'c';
-
char c = '\u0063'; //Unicode
-
char c = '\x0063'; //Hex
-
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.
où
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:
- 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.
}
- 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:
- En tant que valeurs booléennes littérales
var myTrueBool = true;
var myFalseBool = false;
- 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
}
}
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.