Zoeken…


Invoering

Sleutelwoorden zijn vooraf gedefinieerde, gereserveerde identificatiegegevens met een speciale betekenis voor de compiler. Ze kunnen niet als identificatiemiddelen in uw programma worden gebruikt zonder het voorvoegsel @ . @if is bijvoorbeeld een juridische id, maar niet het trefwoord if .

Opmerkingen

C # heeft een vooraf gedefinieerde verzameling "sleutelwoorden" (of gereserveerde woorden) die elk een speciale functie hebben. Deze woorden kunnen niet worden gebruikt als identificatiegegevens (namen voor variabelen, methoden, klassen, enz.) Tenzij voorafgegaan door @ .

Afgezien hiervan gebruikt C # ook enkele sleutelwoorden om een specifieke betekenis in code te geven. Ze worden contextuele sleutelwoorden genoemd. Contextuele zoekwoorden kunnen worden gebruikt als identificatiemiddelen en hoeven niet te worden voorafgegaan door @ wanneer ze worden gebruikt als identificatiemiddelen.

stackalloc

Het sleutelwoord stackalloc creëert een geheugengebied op de stapel en retourneert een aanwijzer naar het begin van dat geheugen. Het toegewezen geheugen wordt automatisch verwijderd wanneer de scope waarin het is gemaakt, wordt verlaten.

//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;
...

Gebruikt in een onveilige context.

Zoals met alle verwijzingen in C # is er geen limiet bij het lezen en toewijzen. Lezen buiten de grenzen van het toegewezen geheugen heeft onvoorspelbare resultaten - het kan toegang krijgen tot een willekeurige locatie in het geheugen of het kan een uitzondering voor toegangsfout veroorzaken.

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

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

Het toegewezen geheugen wordt automatisch verwijderd wanneer de scope waarin het is gemaakt, wordt verlaten. Dit betekent dat u nooit het geheugen dat met stackalloc is gemaakt, moet retourneren of bewaren na de levensduur van de scope.

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 kan alleen worden gebruikt bij het declareren en initialiseren van variabelen. Het volgende is niet geldig:

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

Opmerkingen:

stackalloc mag alleen worden gebruikt voor prestatie-optimalisaties (voor berekening of interop). Dit komt omdat:

  • De vuilnisman is niet nodig omdat het geheugen op de stapel wordt toegewezen in plaats van op de heap - het geheugen wordt vrijgegeven zodra de variabele buiten bereik valt
  • Het is sneller om geheugen toe te wijzen aan de stapel dan aan de heap
  • Verhoog de kans op cache-hits op de CPU vanwege de locatie van gegevens

vluchtig

Het toevoegen van het volatile sleutelwoord aan een veld geeft aan de compiler aan dat de waarde van het veld kan worden gewijzigd door meerdere afzonderlijke threads. Het primaire doel van het volatile trefwoord is het voorkomen van compileroptimalisaties die alleen toegang via één thread veronderstellen. Het gebruik van volatile zorgt ervoor dat de waarde van het veld de meest recente waarde is die beschikbaar is, en de waarde is niet onderhevig aan de caching die niet-vluchtige waarden zijn.

Het is een goede gewoonte om elke variabele die door meerdere threads kan worden gebruikt, als volatile te markeren om onverwacht gedrag als gevolg van optimalisaties achter de schermen te voorkomen. Overweeg het volgende codeblok:

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);
    }    
}

In het bovenstaande codeblok leest de compiler de instructies x = 5 en y = x + 10 en bepaalt dat de waarde van y altijd 15 wordt. Aldus wordt de laatste instructie geoptimaliseerd als y = 15 . De variabele x is echter in feite een public veld en de waarde van x kan tijdens runtime worden gewijzigd via een andere thread die afzonderlijk op dit veld werkt. Overweeg nu dit gewijzigde codeblok. Merk op dat het veld x nu als volatile wordt verklaard.

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);
    }    
}

Nu, de compiler looks voor read gebruiken van het veld x en zorgt ervoor dat de huidige waarde van het veld wordt altijd opgehaald. Dit zorgt ervoor dat zelfs als meerdere threads naar dit veld lezen en schrijven, de huidige waarde van x altijd wordt opgehaald.

volatile kan alleen worden gebruikt op velden binnen class of struct . Het volgende is niet geldig :

public void MyMethod()
{
    volatile int x;
}

volatile kan alleen worden toegepast op velden van de volgende typen:

  • referentietypes of generieke typeparameters bekend als referentietypes
  • primitieve typen zoals sbyte , byte , short , ushort , int , uint , char , float en bool
  • enums types gebaseerd op byte , sbyte , short , ushort , int of uint
  • IntPtr en UIntPtr

Opmerkingen:

  • De volatile modificator wordt meestal gebruikt voor een veld dat toegankelijk is voor meerdere threads zonder de vergrendelingsverklaring te gebruiken om toegang te serialiseren.
  • Het volatile sleutelwoord kan worden toegepast op velden met referentietypen
  • Het volatile sleutelwoord maakt het werken op 64-bits primitieven op een 32-bits platform niet atomair. Vergrendelde bewerkingen zoals Interlocked.Read en Interlocked.Exchange moeten nog steeds worden gebruikt voor veilige toegang met meerdere threads op deze platforms.

gemaakt

De vaste verklaring fixeert geheugen op één locatie. Objecten in het geheugen bewegen meestal rond, dit maakt vuilophaling mogelijk. Maar wanneer we onveilige verwijzingen naar geheugenadressen gebruiken, mag dat geheugen niet worden verplaatst.

  • We gebruiken de vaste verklaring om ervoor te zorgen dat de vuilnisman de tekenreeksgegevens niet verplaatst.

Vaste variabelen

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.
}

Gebruikt in een onveilige context.

Vaste matrixgrootte

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

fixed kan alleen worden gebruikt op velden in een struct (moet ook worden gebruikt in een onveilige context).

standaard

Voor klassen, interfaces, delegate, array, nullable (zoals int?) En pointer types, geeft default(TheType) null terug:

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

Voor structs en enums retourneert default(TheType) hetzelfde als 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) kan met name handig zijn wanneer T een generieke parameter is waarvoor geen beperking aanwezig is om te beslissen of T een referentietype of een waardetype is, bijvoorbeeld:

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

alleen lezen

Het readonly sleutelwoord is een veldmodificator. Wanneer een readonly een readonly modifier bevat, kunnen toewijzingen aan dat veld alleen plaatsvinden als onderdeel van de declaratie of in een constructor in dezelfde klasse.

Het readonly trefwoord verschilt van het const trefwoord. Een const veld kan alleen worden geïnitialiseerd bij de aangifte van het veld. Een readonly veld kan worden geïnitialiseerd bij de aangifte of in een constructor. Daarom kunnen readonly velden verschillende waarden hebben, afhankelijk van de gebruikte constructor.

Het readonly sleutelwoord wordt vaak gebruikt bij het injecteren van afhankelijkheden.

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
    }
}

Opmerking: Het uitroepen van een gebied alleen-lezen impliceert niet onveranderlijkheid. Als het veld een referentietype is, kan de inhoud van het object worden gewijzigd. Alleen-lezen wordt meestal gebruikt om te voorkomen dat het object wordt overschreven en alleen wordt toegewezen tijdens het instantiëren van dat object.

Opmerking: Binnen de constructor kan een alleen-lezen veld opnieuw worden toegewezen

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

//In code

private readonly Car car = new Car();

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

net zo

Het sleutelwoord as is een operator die vergelijkbaar is met een cast . Als een cast niet mogelijk is, produceert as null plaats van dat het resulteert in een InvalidCastException .

expression as type is equivalent aan expression is type ? (type)expression : (type)null met het voorbehoud dat as alleen geldig op referentie conversies nullable conversies en dozen conversies. Door de gebruiker gedefinieerde conversies worden niet ondersteund; in plaats daarvan moet een normale cast worden gebruikt.

Voor de bovenstaande uitbreiding genereert de compiler code zodanig dat expression slechts eenmaal wordt geëvalueerd en een enkele dynamische typecontrole gebruikt (in tegenstelling tot de twee in het bovenstaande voorbeeld).

as nuttig kan zijn wanneer een argument wordt verwacht om verschillende typen te vergemakkelijken. In het bijzonder verleent het de gebruiker meerdere opties - in plaats van elke mogelijkheid te controleren met is vóór casten, of alleen casten en uitzonderingen maken. Het is best om 'als' te gebruiken bij het casten / controleren van een object dat slechts één unboxing-penalty oplevert. Gebruik is om te controleren, dan casten zal twee unboxing penalty's veroorzaken.

Als een argument naar verwachting een instantie van een specifiek type is, heeft een reguliere cast de voorkeur omdat het doel duidelijker is voor de lezer.

Omdat een aanroep naar as null kan produceren, moet u altijd het resultaat controleren om een NullReferenceException te voorkomen.

Voorbeeld gebruik

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);

Live demo op .NET Fiddle

Gelijkwaardig voorbeeld zonder te gebruiken as :

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

Dit is handig wanneer u de functie Equals in aangepaste klassen overschrijft.

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
    }

}

is

Controleert of een object compatibel is met een bepaald type, dat wil zeggen of een object een instantie van het type BaseInterface is, of een type dat is afgeleid van 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

Als de bedoeling van de cast is om het object te gebruiken, is het best om het trefwoord ' as ' te gebruiken

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
}

Maar vanaf C # 7 breidt de functie voor het pattern matching de operator is uit om op een type te controleren en tegelijkertijd een nieuwe variabele te declareren. Zelfde codedeel met C # 7:

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

soort van

Retourneert het Type van een object, zonder het te instantiëren.

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 wordt gebruikt om waarden weer te geven die nooit zullen veranderen tijdens de levensduur van het programma. De waarde ervan is constant vanaf compilatie , in tegenstelling tot het readonly sleutelwoord, waarvan de waarde constant is tijdens runtime.

Omdat de snelheid van het licht bijvoorbeeld nooit verandert, kunnen we deze in een constante opslaan.

const double c = 299792458;  // Speed of light

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

Dit is in wezen hetzelfde als het hebben van return mass * 299792458 * 299792458 , omdat de compiler c rechtstreeks door zijn constante waarde zal vervangen.

Als gevolg hiervan kan c niet meer worden gewijzigd nadat deze is gedeclareerd. Het volgende zal een compilatie-fout veroorzaken:

const double c = 299792458;  // Speed of light 

c = 500;  //compile-time error

Aan een constante kunnen dezelfde toegangsmodificaties worden toegevoegd als aan methoden:

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

const leden zijn van nature static . Het is echter niet toegestaan om static expliciet te gebruiken.

U kunt ook methode-lokale constanten definiëren:

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

Deze kunnen niet worden voorafgegaan door een private of public trefwoord, omdat ze impliciet lokaal zijn voor de methode waarin ze zijn gedefinieerd.


Niet alle typen kunnen in een const aangifte worden gebruikt. De toegestane waardetypes zijn de vooraf gedefinieerde typen sbyte , byte , short , ushort , int , uint , long , ulong , char , float , double , decimal , bool en alle enum typen. Proberen te verklaren const leden met andere types waarde (zoals TimeSpan of Guid ) zal falen tijdens het compileren.

Voor het speciale vooraf gedefinieerde verwijzingstype string , kunnen constanten aangegeven worden met enige waarde. Voor alle andere referentietypen kunnen constanten worden gedeclareerd, maar moeten altijd de waarde null .


Omdat const waarden bekend tijdens het compileren, mogen zij als case labels in een switch statement, standaard argumenten voor optionele parameters als argumenten voor attribuut specificaties, enzovoort.


Als const waarden worden gebruikt voor verschillende assemblages, moet u voorzichtig zijn met versiebeheer. Als bijvoorbeeld assemblage A een public const int MaxRetries = 3; definieert public const int MaxRetries = 3; en assemblage B gebruikt die constante, en als de waarde van MaxRetries later wordt gewijzigd in 5 in assemblage A (die vervolgens opnieuw wordt gecompileerd), is die wijziging niet effectief in assemblage B tenzij assemblage B ook opnieuw wordt gecompileerd (met een verwijzing naar de nieuwe versie van A).

Om die reden, als een waarde zou kunnen veranderen in toekomstige revisies van het programma, en als de waarde publiekelijk zichtbaar moet zijn, declareer die waarde const tenzij u weet dat alle afhankelijke assemblages opnieuw zullen worden gecompileerd wanneer er iets wordt gewijzigd. Het alternatief is static readonly plaats van const , dat tijdens runtime wordt opgelost.

namespace

Het sleutelwoord voor de namespace is een organisatieconstructie die ons helpt te begrijpen hoe een codebase is gerangschikt. Naamruimten in C # zijn virtuele spaties en bevinden zich niet in een fysieke map.

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

Naamruimten in C # kunnen ook worden geschreven in gekoppelde syntaxis. Het volgende komt overeen met hierboven:

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

proberen, vangen, eindelijk gooien

try , catch , finally en throw om uitzonderingen in uw code te verwerken.

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();
}

Opmerking: het return trefwoord kan worden gebruikt in het try blok en het finally blok wordt nog steeds uitgevoerd (net voordat het terugkeert). Bijvoorbeeld:

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

De instructie connection.Close() wordt uitgevoerd voordat het resultaat van connection.Get(query) wordt geretourneerd.

doorgaan met

Geef de besturing onmiddellijk door aan de volgende iteratie van het omringende lusconstructie (foreach, do, while):

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

Output:

5
6
7
8
9

Live demo op .NET Fiddle

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

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

Output:

een
b
c
d

Live demo op .NET Fiddle

ref, eruit

De ref en out sleutelwoorden zorgen ervoor dat een argument wordt doorgegeven door verwijzing, niet door waarde. Voor waardetypes betekent dit dat de waarde van de variabele door de callee kan worden gewijzigd.

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

Voor referentietypen kan de instantie in de variabele niet alleen worden gewijzigd (zoals het geval is zonder ref ), maar deze kan ook helemaal worden vervangen:

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

Het belangrijkste verschil tussen het trefwoord out en ref is dat ref vereist dat de variabele door de beller wordt geïnitialiseerd, terwijl out die verantwoordelijkheid doorgeeft aan de gebruiker.

Als u een parameter out wilt gebruiken, moeten zowel de methode-definitie als de aanroepmethode expliciet het trefwoord 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;
}

Live demo op .NET Fiddle

Het volgende compileert niet , omdat out parameters een waarde moet worden toegewezen voordat de methode terugkeert (in plaats daarvan zou het compileren met ref ):

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

met behulp van trefwoord als Generic Modifier

out trefwoord kan ook worden gebruikt in generieke typeparameters bij het definiëren van generieke interfaces en gedelegeerden. In dit geval geeft het sleutelwoord out aan dat de parameter type covariant is.

Covariance stelt u in staat om een meer afgeleid type te gebruiken dan gespecificeerd door de generieke parameter. Dit maakt impliciete conversie mogelijk van klassen die variantinterfaces implementeren en impliciete conversie van delegatietypen. Covariantie en contravarantie worden ondersteund voor referentietypen, maar worden niet ondersteund voor waardetypes. - 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

aangevinkt, niet aangevinkt

De checked en unchecked sleutelwoorden definiëren hoe bewerkingen omgaan met wiskundige overflow. "Overloop" in de context van de checked en unchecked sleutelwoorden is wanneer een gehele rekenkundige bewerking resulteert in een waarde die groter is dan het doelgegevenstype kan vertegenwoordigen.

Wanneer overflow optreedt binnen een checked blok (of wanneer de compiler is ingesteld om globaal gecontroleerd rekenen te gebruiken), wordt een uitzondering gegenereerd om te waarschuwen voor ongewenst gedrag. Ondertussen, in een unchecked blok, is de overstroming stil: er worden geen uitzonderingen gegenereerd en de waarde loopt eenvoudig naar de tegenovergestelde grens. Dit kan leiden tot subtiele, moeilijk te vinden bugs.

Aangezien de meeste rekenkundige bewerkingen worden uitgevoerd op waarden die niet groot of klein genoeg zijn om te overstromen, is het meestal niet nodig om een blok expliciet als checked te definiëren. Voorzichtigheid is geboden bij het doen van rekenkundige bewerkingen op onbegrensde invoer die overflow kan veroorzaken, bijvoorbeeld bij het doen van rekenkundige bewerkingen in recursieve functies of tijdens het invoeren van gebruikersinvoer.

Niet checked of unchecked rekenkundige bewerkingen met drijvende komma beïnvloeden.

Wanneer een blok of expressie als unchecked wordt verklaard, mogen alle rekenkundige bewerkingen erin overlopen zonder een fout te veroorzaken. Een voorbeeld waar dit gedrag gewenst is, zou de berekening van een controlesom zijn, waarbij de waarde tijdens de berekening "rondloopt":

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

Een van de meest voorkomende toepassingen voor unchecked is het implementeren van een aangepaste opheffing voor object.GetHashCode() , een soort controlesom. U kunt het gebruik van het trefwoord zien in de antwoorden op deze vraag: Wat is het beste algoritme voor een onderdrukt System.Object.GetHashCode? .

Wanneer een blok of expressie als checked wordt verklaard, resulteert elke rekenkundige bewerking die een overloop veroorzaakt in een OverflowException .

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

Zowel aangevinkt als uitgeschakeld zijn mogelijk blok- en uitdrukkingsvormen.

Aangevinkte en niet-aangevinkte blokken hebben geen invloed op de opgeroepen methoden, alleen op operators die rechtstreeks in de huidige methode worden aangeroepen. Enum.ToObject() , Convert.ToInt32() en door de gebruiker gedefinieerde operatoren worden bijvoorbeeld niet beïnvloed door aangepaste aangevinkte / niet-aangevinkte contexten.

Opmerking : het standaardgedrag van de standaardoverloop (aangevinkt versus uitgeschakeld) kan worden gewijzigd in de Projecteigenschappen of via de schakeloptie / check [+ | -] . Het is gebruikelijk om standaard te controleren op bewerkingen voor debug-builds en uitgeschakeld voor release-builds. De checked en unchecked sleutelwoorden worden dan alleen gebruikt wanneer een standaardbenadering niet van toepassing is en u expliciet gedrag nodig hebt om de juistheid te waarborgen.

ga naar

goto kan worden gebruikt om naar een specifieke regel in de code te springen, aangegeven door een label.

goto als:

Etiket:

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

Live demo op .NET Fiddle

Case verklaring:

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
}

Live demo op .NET Fiddle

Dit is vooral handig bij het uitvoeren van meerdere gedrag in een switch statement, zoals C # ondersteunt geen fall-through-case blokken .

Uitzondering Opnieuw proberen

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

Live demo op .NET Fiddle

Net als in vele talen wordt het gebruik van het Goto-trefwoord afgeraden, behalve in de onderstaande gevallen.

Geldig gebruik van goto die van toepassing zijn op C #:

  • Doorloopgeval in schakelverklaring.

  • Multi-level break. LINQ kan vaak in plaats daarvan worden gebruikt, maar het heeft meestal slechtere prestaties.

  • Deallocatie van bronnen bij het werken met niet-verpakte objecten op laag niveau. In C # moeten objecten op laag niveau meestal in afzonderlijke klassen worden verpakt.

  • Eindige toestandsmachines, bijvoorbeeld parsers; intern gebruikt door compiler gegenereerde async / wacht op staat machines.

enum

Het enum sleutelwoord vertelt de compiler dat deze klasse van de abstracte klasse Enum erft, zonder dat de programmeur het expliciet moet erven. Enum is een afstammeling van ValueType , dat is bedoeld voor gebruik met een afzonderlijke set benoemde constanten.

public enum DaysOfWeek
{
    Monday,
    Tuesday,
}

U kunt desgewenst een specifieke waarde voor elke waarde (of sommige) opgeven:

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

In dit voorbeeld heb ik een waarde voor 0 weggelaten, dit is meestal een slechte gewoonte. Een enum heeft altijd een standaardwaarde die wordt geproduceerd door expliciete conversie (YourEnumType) 0 , waarbij YourEnumType u opgegeven enume type is. Zonder een gedefinieerde waarde van 0, zal een enum bij het initiëren geen gedefinieerde waarde hebben.

Het standaard onderliggende type enum is int , u kunt het onderliggende type wijzigen in elk integraal type, inclusief byte , sbyte , short , ushort , int , uint , long en ulong . Hieronder is een opsomming met onderliggende type byte :

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

Merk ook op dat je eenvoudig kunt converteren naar / van het onderliggende type met een cast:

int value = (int)NotableYear.EndOfWwI;

Om deze redenen kunt u beter altijd controleren of een enum geldig is wanneer u bibliotheekfuncties blootstelt:

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

    // ...
}

baseren

De base zoekwoord wordt gebruikt om de leden toegang vanaf een basisklasse. Het wordt vaak gebruikt om basisimplementaties van virtuele methoden aan te roepen of om aan te geven welke basisconstructor moet worden aangeroepen.

Een constructeur kiezen

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);
    }
}

Oproep basisimplementatie van virtuele methode

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

Het is mogelijk om het basiszoekwoord te gebruiken om een basisimplementatie aan te roepen vanuit elke methode. Dit verbindt de methodeaanroep rechtstreeks met de basisimplementatie, wat betekent dat zelfs als nieuwe onderliggende klassen een virtuele methode overschrijven, de basisimplementatie nog steeds wordt aangeroepen, dus dit moet voorzichtig worden gebruikt.

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());
    }
}

foreach

foreach wordt gebruikt om de elementen van een array of de items in een verzameling te doorlopen die IEnumerable ✝ implementeren.

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

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

Dit wordt uitgevoerd

"Hallo Wereld!"
"Hoe is het met jouw vandaag?"
"Vaarwel"

Live demo op .NET Fiddle

U kunt de foreach lus op elk gewenst moment verlaten met het sleutelwoord break of doorgaan naar de volgende iteratie met het sleutelwoord 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, 

Live demo op .NET Fiddle

Merk op dat de volgorde van iteratie alleen wordt gegarandeerd voor bepaalde collecties zoals arrays en List , maar niet voor veel andere collecties.


✝ Hoewel IEnumerable meestal wordt gebruikt om opsommende collecties aan te geven, vereist foreach alleen dat de collectie de object GetEnumerator() -methode openbaar maakt, die een object moet retourneren dat de bool MoveNext() -methode en het object Current { get; } eigenschap.

params

params kan een params een variabel aantal argumenten ontvangen, dwz nul, één of meerdere argumenten zijn toegestaan voor die parameter.

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

Deze methode kan nu worden opgeroepen met een typische lijst met int argumenten of een array met int .

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

params moeten maximaal één keer verschijnen en indien gebruikt, moet deze als laatste in de lijst met argumenten voorkomen, zelfs als het volgende type verschilt van dat van de array.


Wees voorzichtig bij het overladen van functies bij het gebruik van het trefwoord params . C # geeft er de voorkeur aan om meer specifieke overbelastingen te matchen voordat het probeert overbelastingen met params . Als u bijvoorbeeld twee methoden heeft:

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;
}

Dan heeft de specifieke overbelasting met 2 argumenten voorrang voordat de overbelasting van de params geprobeerd.

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)

breken

In een lus (for, foreach, do, while) break instructie break de uitvoering van de binnenste lus af en keert terug naar de code erna. Het kan ook worden gebruikt met yield waarin wordt aangegeven dat een iterator is afgelopen.

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.");
}

Live demo op .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);
}

De break-statement wordt ook gebruikt in switch-case constructies om uit een case of standaardsegment te breken.

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

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

In switch-instructies is het sleutelwoord 'break' vereist aan het einde van elke case-instructie. Dit is in tegenstelling tot sommige talen die het mogelijk maken om 'door te vallen' naar de volgende case-statement in de serie. Tijdelijke oplossingen hiervoor zijn 'goto'-instructies of opeenvolgende stapeling van de' case'-instructies.

De volgende code geeft de cijfers 0, 1, 2, ..., 9 en de laatste regel wordt niet uitgevoerd. yield break betekent het einde van de functie (niet alleen een lus).

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");
}

Live demo op .NET Fiddle

Merk op dat in tegenstelling tot sommige andere talen, er geen manier is om een bepaalde pauze in C # te labelen. Dit betekent dat in het geval van geneste lussen alleen de binnenste lus wordt gestopt:

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; 
    }
}

Als je hier uit de buitenste lus wilt breken, kun je een van verschillende strategieën gebruiken, zoals:

  • Een ga- statement om uit de hele looping-structuur te springen.
  • Een specifieke shouldBreak ( shouldBreak in het volgende voorbeeld) die kan worden gecontroleerd aan het einde van elke iteratie van de buitenste lus.
  • Refactoring de code gebruiken om een return statement in de binnenste lus orgaan of geheel voorkomen dat de gehele geneste lusstructuur.
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

abstract

Een klasse gemarkeerd met het trefwoord abstract kan niet worden gestart.

Een klasse moet als abstract worden gemarkeerd als het abstracte leden bevat of als het abstracte leden erft die het niet implementeert. Een klasse kan als abstract worden gemarkeerd, zelfs als er geen abstracte leden bij betrokken zijn.

Abstracte klassen worden meestal gebruikt als basisklassen wanneer een deel van de implementatie door een andere component moet worden gespecificeerd.

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

Een methode, eigenschap of gebeurtenis gemarkeerd met het trefwoord abstract geeft aan dat de implementatie voor dat lid naar verwachting in een subklasse wordt geleverd. Zoals hierboven vermeld, kunnen abstracte leden alleen in abstracte klassen verschijnen.

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; }
}

zwevend, dubbel, decimaal

vlotter

float is een alias voor het .NET-gegevenstype System.Single . Hiermee kunnen IEEE 754 zwevende puntnummers met enkele precisie worden opgeslagen. Dit gegevenstype is aanwezig in mscorlib.dll waarnaar door elk C # -project impliciet wordt verwezen wanneer u ze maakt.

Geschat bereik: -3.4 × 10 38 tot 3.4 × 10 38

Decimale precisie: 6-9 significante cijfers

Notatie :

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

Opgemerkt moet worden dat het float vaak resulteert in significante afrondingsfouten. In toepassingen waar precisie belangrijk is, moeten andere gegevenstypen worden overwogen.


dubbele

double is een alias voor het .NET-gegevenstype System.Double . Het vertegenwoordigt een dubbel-precisie 64-bit drijvende-kommagetal. Dit gegevenstype is aanwezig in mscorlib.dll waarnaar impliciet wordt verwezen in elk C # -project.

Bereik: ± 5,0 × 10 −324 tot ± 1,7 × 10 308

Decimale precisie: 15-16 significante cijfers

Notatie :

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

decimale

decimal is een alias voor het .NET-gegevenstype System.Decimal . Het vertegenwoordigt een trefwoord dat een 128-bits gegevenstype aangeeft. In vergelijking met drijvende-komma typen heeft het decimale type meer precisie en een kleiner bereik, waardoor het geschikt is voor financiële en monetaire berekeningen. Dit gegevenstype is aanwezig in mscorlib.dll waarnaar impliciet wordt verwezen in elk C # -project.

Bereik: -7.9 × 10 28 tot 7.9 × 10 28

Decimale precisie: 28-29 significante cijfers

Notatie :

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

uint

Een geheel getal zonder teken of uint is een numeriek gegevenstype dat alleen positieve gehele getallen kan bevatten. Zoals de naam al doet vermoeden, vertegenwoordigt het een 32-bits geheel getal zonder teken. Het sleutelwoord uint zelf is een alias voor het Common Type Systeemtype System.UInt32 . Dit gegevenstype is aanwezig in mscorlib.dll , waarnaar bij elk C # -project impliciet wordt verwezen wanneer u ze maakt. Het neemt vier bytes geheugenruimte in beslag.

Niet-ondertekende gehele getallen kunnen een waarde hebben van 0 tot 4.294.967.295.

Voorbeelden over hoe en nu om niet-ondertekende gehele getallen te declareren

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

Let op: Volgens Microsoft wordt het aanbevolen om het datatype int waar mogelijk te gebruiken, omdat het datatype uint niet CLS-compatibel is.

deze

Het trefwoord this verwijst naar de huidige instantie van klasse (object). Op die manier kunnen twee variabelen met dezelfde naam worden onderscheiden, één op klassenniveau (een veld) en één die een parameter (of lokale variabele) van een methode is.

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;
    }
}

Andere gebruiksmogelijkheden van het trefwoord zijn niet-statische constructielastingen in een keten :

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

en schrijven van indexers :

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

en uitbreidingsmethoden :

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

Als er geen conflict is met een lokale variabele of parameter, is het een kwestie van stijl om this dan niet te gebruiken, dus this.MemberOfType en MemberOfType zouden in dat geval gelijkwaardig zijn. Zie ook base trefwoord.

Merk op dat als een uitbreidingsmethode moet worden aangeroepen in het huidige exemplaar, this vereist is. Als u bijvoorbeeld deel uitmaakt van een niet-statische methode van een klasse die IEnumerable<> implementeert en u de extensie Count van eerder wilt gebruiken, moet u het volgende gebruiken:

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

en this kan daar niet worden weggelaten.

voor

Syntaxis: for (initializer; condition; iterator)

  • De for lus wordt meestal gebruikt wanneer het aantal iteraties bekend is.
  • De instructies in het initializer worden slechts één keer uitgevoerd voordat u de lus betreedt.
  • De condition bevat een booleaanse uitdrukking die aan het einde van elke lusherhaling wordt geëvalueerd om te bepalen of de lus moet worden afgesloten of opnieuw moet worden uitgevoerd.
  • De iterator sectie definieert wat er gebeurt na elke iteratie van het lichaam van de lus.

Dit voorbeeld laat zien hoe for kan worden gebruikt om de tekens van een string te herhalen:

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

Output:

H
e
l
l
O

Live demo op .NET Fiddle

Alle uitdrukkingen die een for definiëren zijn optioneel; De volgende instructie wordt bijvoorbeeld gebruikt om een oneindige lus te maken:

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

Het initializer kan meerdere variabelen bevatten, zolang ze maar van hetzelfde type zijn. De condition sectie kan bestaan uit elke expressie die kan worden geëvalueerd naar een bool . En de iterator sectie kan meerdere acties uitvoeren gescheiden door komma's:

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

Output:

Hallo
hello1
hello12

Live demo op .NET Fiddle

terwijl

De while operator herhaalt een codeblok totdat de voorwaardelijke query gelijk is aan false of de code wordt onderbroken met een instructie goto , return , break of throw .

Syntaxis voor while trefwoord:

terwijl ( staat ) { codeblok; }

Voorbeeld:

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

Output:

"While is op lus nummer 1."
"While is op lus nummer 2."
"While is op lus nummer 3."
"While is op lus nummer 4."
"While is op lus nummer 5."

Live demo op .NET Fiddle

Een while-lus is Entry Controlled , omdat de toestand wordt gecontroleerd voordat het bijgevoegde codeblok wordt uitgevoerd. Dit betekent dat de while-lus zijn instructies niet zou uitvoeren als de voorwaarde niet waar is.

bool a = false;

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

Het geven van een while zonder te voorzien dat het op een gegeven moment vals wordt, zal resulteren in een oneindige of eindeloze lus. Dit moet zoveel mogelijk worden vermeden, maar er kunnen zich uitzonderlijke omstandigheden voordoen wanneer u dit nodig hebt.

Je kunt zo'n lus als volgt maken:

while (true)
{
//...
}

Merk op dat de C # compiler lussen zal transformeren zoals

while (true)
{
// ...
}

of

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

in

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

Merk op dat een while-lus elke voorwaarde kan hebben, hoe complex ook, zolang deze een booleaanse waarde (bool) oplevert (of retourneert). Het kan ook een functie bevatten die een booleaanse waarde retourneert (een dergelijke functie evalueert hetzelfde type als een uitdrukking zoals `a == x '). Bijvoorbeeld,

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

terugkeer

MSDN: de retourinstructie beëindigt de uitvoering van de methode waarin deze wordt weergegeven en stuurt de besturing terug naar de aanroepingsmethode. Het kan ook een optionele waarde retourneren. Als de methode een ongeldig type is, kan de retourinstructie worden weggelaten.

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
}

in

De in zoekwoord heeft drie toepassingen:

a) Als onderdeel van de syntaxis in een foreach instructie of als onderdeel van de syntaxis in een LINQ-query

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

b) In de context van generieke interfaces en generieke deelnemerstypen betekent een tegenstelling voor de betreffende typeparameter :

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

c) In de context van LINQ verwijst zoekopdracht naar de verzameling die wordt opgevraagd

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

gebruik makend van

Er zijn twee soorten using trefwoordgebruik, using statement en using directive :

  1. met behulp van verklaring :

    Het sleutelwoord using zorgt ervoor dat objecten die de IDisposable interface IDisposable na gebruik correct worden verwijderd. Er is een apart onderwerp voor de gebruiksinstructie

  2. richtlijn gebruiken

    De using heeft drie gebruiksmogelijkheden, zie de msdn-pagina voor de gebruiksrichtlijn . Er is een apart onderwerp voor de gebruiksrichtlijn .

verzegeld

Wanneer toegepast op een klasse, voorkomt de sealed modificator dat andere klassen ervan erven.

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

Wanneer toegepast op een virtual methode (of virtuele eigenschap), voorkomt de sealed modificator dat deze methode (eigenschap) wordt overschreven in afgeleide klassen.

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"; 
    }
}

De grootte van

Wordt gebruikt om de grootte in bytes te verkrijgen voor een onbeheerd type

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

statisch

De static modificator wordt gebruikt om een statisch lid aan te geven, dat niet hoeft te worden geïnstantieerd om toegang te krijgen, maar in plaats daarvan alleen toegankelijk is via de naam, dat wil zeggen DateTime.Now .

static kan worden gebruikt met klassen, velden, methoden, eigenschappen, operatoren, gebeurtenissen en constructors.

Hoewel een instantie van een klasse een afzonderlijke kopie van alle instantievelden van de klasse bevat, is er slechts één kopie van elk statisch veld.

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 gelijk aan het totale aantal instanties van klasse A

De statische modificator kan ook worden gebruikt om een statische constructor voor een klasse te declareren, om statische gegevens te initialiseren of code uit te voeren die maar één keer hoeft te worden aangeroepen. Statische constructors worden aangeroepen voordat naar de klasse wordt verwezen.

class A
{
    static public DateTime InitializationTime;

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

Een static class is gemarkeerd met het static trefwoord en kan worden gebruikt als een nuttige container voor een reeks methoden die werken op parameters, maar die niet noodzakelijkerwijs aan een instantie zijn gebonden. Vanwege de static aard van de klasse kan deze niet worden geïnstantieerd, maar deze kan een static constructor bevatten. Enkele kenmerken van een static class zijn:

  • Kan niet worden geërfd
  • Kan niets anders erven dan Object
  • Kan een statische constructor bevatten, maar geen instantieconstructor
  • Kan alleen statische leden bevatten
  • Is verzegeld

De compiler is ook vriendelijk en laat de ontwikkelaar weten of er instantieleden binnen de klasse bestaan. Een voorbeeld is een statische klasse die converteert tussen Amerikaanse en Canadese statistieken:

static class ConversionHelper {
    private static double oneGallonPerLitreRate = 0.264172;

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

Wanneer klassen statisch worden verklaard:

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

alle functies, eigenschappen of leden binnen de klasse moeten ook statisch worden verklaard. Er kan geen instantie van de klasse worden gemaakt. In wezen kunt u met een statische klasse bundels van functies maken die logisch zijn gegroepeerd.

Omdat C # 6 static ook kan worden gebruikt naast het using van statische leden en methoden. Ze kunnen dan zonder klassenaam worden gebruikt.

Oude manier, zonder using static :

using System;

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

}

Voorbeeld met het using static

using static System.Console;

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

}

nadelen

Hoewel statische klassen ongelooflijk nuttig kunnen zijn, hebben ze hun eigen kanttekeningen:

  • Nadat de statische klasse is opgeroepen, wordt de klasse in het geheugen geladen en kan deze niet meer door de garbagecollector worden uitgevoerd totdat de AppDomain-behuizing van de statische klasse is gelost.

  • Een statische klasse kan geen interface implementeren.

int

int is een alias voor System.Int32 , een gegevenstype voor 32-bits gehele getallen. Dit gegevenstype kan worden gevonden in mscorlib.dll waarnaar door elk C # -project impliciet wordt verwezen wanneer u ze maakt.

Bereik: -2.147.483.648 tot 2.147.483.647

int int1 = -10007;
var int2 = 2132012521;     

lang

Het lange sleutelwoord wordt gebruikt om 64-bits gehele getallen weer te geven. Het is een alias voor het System.Int64 gegevenstype dat aanwezig is in mscorlib.dll , waarnaar door elk C # -project impliciet wordt verwezen wanneer u ze maakt.

Elke lange variabele kan zowel expliciet als impliciet worden aangegeven:

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

Een lange variabele kan elke waarde bevatten van –9.223.372.036.854.775.808 tot 9.223.372.036.854.775.807 en kan nuttig zijn in situaties waarin een variabele een waarde moet hebben die de grenzen overschrijdt van wat andere variabelen (zoals de variabele int ) kunnen bevatten.

Ulong

Trefwoord gebruikt voor niet-ondertekende 64-bits gehele getallen. Het vertegenwoordigt System.UInt64 data type gevonden in mscorlib.dll dat impliciet wordt verwezen door ieder C # project wanneer u ze maakt.

Bereik: 0 tot 18.446.744.073.709.551.615

ulong veryLargeInt = 18446744073609451315;
var anotherVeryLargeInt = 15446744063609451315UL;

dynamisch

Het dynamic trefwoord wordt gebruikt met dynamisch getypte objecten . Objecten die zijn gedeclareerd als dynamic compilatie statische controles achterwege en worden in plaats daarvan geëvalueerd tijdens runtime.

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

In het volgende voorbeeld wordt dynamic gebruikt met de Json.NET-bibliotheek van Newtonsoft, om gemakkelijk gegevens uit een gedeserialiseerd JSON-bestand te lezen.

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
}

Er zijn enkele beperkingen verbonden aan het dynamische trefwoord. Een daarvan is het gebruik van uitbreidingsmethoden. In het volgende voorbeeld wordt een uitbreidingsmethode voor tekenreeks toegevoegd: SayHello .

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

De eerste benadering zal zijn om het zoals gewoonlijk te noemen (zoals voor een string):

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

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

Geen compilatiefout, maar tijdens runtime krijg je een RuntimeBinderException . De oplossing hiervoor is om de extensiemethode aan te roepen via de statische klasse:

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

virtueel, negeren, nieuw

virtueel en negeren

Met het virtual sleutelwoord kan een methode, eigenschap, indexer of gebeurtenis worden overschreven door afgeleide klassen en aanwezig polymorf gedrag. (Leden zijn standaard niet-virtueel in C #)

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

Om een lid te onderdrukken, wordt het trefwoord override gebruikt in de afgeleide klassen. (Let op: de handtekening van de leden moet identiek zijn)

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

Het polymorfe gedrag van virtuele leden betekent dat wanneer ze worden opgeroepen, het werkelijke lid dat wordt uitgevoerd tijdens runtime wordt bepaald in plaats van tijdens het compileren. Het overheersende lid in de meest afgeleide klasse waarvan het specifieke object een instantie is, wordt degene die wordt uitgevoerd.

Kort BaseClass kan object tijdens het compileren van het type BaseClass worden verklaard, maar als het tijdens runtime een instantie van DerivedClass wordt het overschreven lid uitgevoerd:

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

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

Een methode overschrijven is optioneel:

public class SecondDerivedClass: DerivedClass {}

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

nieuw

Omdat alleen leden die als virtual zijn gedefinieerd, overschrijfbaar en polymorf zijn, kan een afgeleide klasse die een niet-virtueel lid opnieuw definieert, tot onverwachte resultaten leiden.

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!    

Wanneer dit gebeurt, wordt het uitgevoerde lid altijd tijdens het compileren bepaald op basis van het type object.

  • Als het object wordt gedeclareerd van het type BaseClass (zelfs als het tijdens runtime van een afgeleide klasse is), wordt de methode BaseClass uitgevoerd
  • Als het object wordt gedeclareerd van het type DerivedClass wordt de methode DerivedClass uitgevoerd.

Dit is meestal een ongeval (wanneer een lid wordt toegevoegd aan het basistype nadat een identiek lid werd toegevoegd aan het afgeleide type) en een compilerwaarschuwing CS0108 wordt gegenereerd in die scenario's.

Als het opzettelijk was, wordt het new trefwoord gebruikt om de compilerwaarschuwing te onderdrukken (en andere ontwikkelaars op de hoogte te brengen van uw bedoelingen!). het gedrag blijft hetzelfde, het new trefwoord onderdrukt alleen de compilerwaarschuwing.

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! 

Het gebruik van opheffing is niet optioneel

Anders dan in C ++ is het gebruik van het trefwoord override niet optioneel:

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

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

Het bovenstaande voorbeeld veroorzaakt ook waarschuwen CS0108 omdat B.Foo() niet automatisch dwingende A.Foo() . Voeg override wanneer het de bedoeling is om de basisklasse te negeren en polymorf gedrag te veroorzaken, voeg new als u niet-polymorf gedrag wilt en los de oproep op met het statische type. Dit laatste moet met voorzichtigheid worden gebruikt, omdat het ernstige verwarring kan veroorzaken.

De volgende code resulteert zelfs in een fout:

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

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

Afgeleide klassen kunnen polymorfisme introduceren

De volgende code is volkomen geldig (hoewel zeldzaam):

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

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

Nu gebruiken alle objecten met een statische referentie van B (en zijn derivaten) polymorfisme om Foo() te lossen, terwijl referenties van A 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";

Virtuele methoden kunnen niet privé zijn

De C # compiler is strikt in het voorkomen van zinloze constructies. Methoden gemarkeerd als virtual kunnen niet privé zijn. Omdat een privémethode niet kan worden afgeleid van een afgeleid type, kan deze ook niet worden overschreven. Dit kan niet worden gecompileerd:

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

async, wacht af

Het sleutelwoord await is toegevoegd als onderdeel van C # 5.0 release die wordt ondersteund vanaf Visual Studio 2012 en hoger. Het maakt gebruik van Task Parallel Library (TPL) waardoor het multi-threading relatief eenvoudiger werd. De async en await worden in paren gebruikt in dezelfde functie als hieronder weergegeven. De await sleutelwoord wordt gebruikt om de uitvoering van de huidige asynchrone methode onderbroken totdat de verwachte asynchrone taak is voltooid en / of de resultaten geretourneerd. Als u het sleutelwoord await wilt gebruiken, moet de methode die het gebruikt, worden gemarkeerd met het sleutelwoord async .

Het gebruik van async met void wordt sterk afgeraden. Voor meer info kun je hier kijken.

Voorbeeld:

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;
}

Output:

"Een nutteloos proces starten ..."

** ... 1 seconde vertraging ... **

"Een nutteloos proces kostte 1000 milliseconden om uit te voeren."

Het sleutelwoord paren async en await kunnen worden weggelaten als een Task of Task<T> terugkerende methode slechts één asynchrone bewerking retourneert.

In plaats van dit:

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

Het heeft de voorkeur om dit te doen:

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

In C # 5.0 kan await niet worden gebruikt als catch en finally .

6.0

Met C # 6.0 kan await worden gebruikt in de catch en finally .

verkolen

Een teken is een enkele letter opgeslagen in een variabele. Het is een ingebouwd waardetype dat twee bytes geheugenruimte in beslag neemt. Het vertegenwoordigt het gegevenstype System.Char dat wordt gevonden in mscorlib.dll waarnaar impliciet wordt verwezen door elk C # -project wanneer u ze maakt.

Er zijn meerdere manieren om dit te doen.

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

Een teken kan impliciet worden omgezet in ushort, int, uint, long, ulong, float, double, of decimal en het zal de gehele waarde van dat teken teruggeven.

ushort u = c;

retourneert 99 etc.

Er zijn echter geen impliciete conversies van andere typen naar char. In plaats daarvan moet je ze casten.

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

slot

lock biedt thread-veiligheid voor een codeblok, zodat het toegankelijk is voor slechts één thread binnen hetzelfde proces. Voorbeeld:

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

Gebruik gevallen:

Wanneer u een codeblok hebt dat bijwerkingen kan veroorzaken als het door meerdere threads tegelijkertijd wordt uitgevoerd. Het vergrendelingswoord samen met een gedeeld synchronisatieobject ( _objLock in het voorbeeld) kan worden gebruikt om dat te voorkomen.

Merk op dat _objLock niet null kan zijn en dat meerdere threads die de code uitvoeren dezelfde objectinstantie moeten gebruiken (door het een static veld te maken, of door dezelfde klasse-instantie voor beide threads te gebruiken)

Van de compilerzijde is het sleutelwoord lock een syntactische suiker die wordt vervangen door Monitor.Enter(_lockObj); en Monitor.Exit(_lockObj); . Dus als u het slot vervangt door het codeblok met deze twee methoden te omgeven, krijgt u dezelfde resultaten. U kunt de werkelijke code zien in Syntactische suiker in C # - slotvoorbeeld

nul

Een variabele van een referentietype kan een geldige verwijzing naar een instantie of een nulreferentie bevatten. De nulreferentie is de standaardwaarde van variabelen van het referentietype, evenals nulwaardetypetypen.

null is het trefwoord dat een nulreferentie vertegenwoordigt.

Als een uitdrukking kan het worden gebruikt om de nulverwijzing aan variabelen van de bovengenoemde typen toe te wijzen:

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

Aan niet-nulbare waarden kunnen geen nulreferenties worden toegewezen. Alle volgende opdrachten zijn ongeldig:

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

De nulreferentie mag niet worden verward met geldige instanties van verschillende typen, zoals:

  • een lege lijst ( new List<int>() )
  • een lege string ( "" )
  • het getal nul ( 0 , 0f , 0m )
  • het nul-teken ( '\0' )

Soms is het zinvol om te controleren of iets nul is of een leeg / standaardobject. De methode System.String.IsNullOrEmpty (String) kan worden gebruikt om dit te controleren, of u kunt uw eigen equivalente methode implementeren.

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 + "!");
    }
}

intern

Het internal trefwoord is een toegangsmodificator voor typen en typeleden. Interne typen of leden zijn alleen toegankelijk binnen bestanden in dezelfde assembly

gebruik:

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

Het verschil tussen verschillende toegangsmodificatoren wordt hier verduidelijkt

Toegang modifiers

openbaar

Het type of lid is toegankelijk voor elke andere code in dezelfde assembly of een andere assembly die ernaar verwijst.

privaat

Het type of lid is alleen toegankelijk via code in dezelfde klasse of structuur.

beschermde

Het type of lid is alleen toegankelijk via code in dezelfde klasse of struct, of in een afgeleide klasse.

intern

Het type of lid is toegankelijk voor elke code in dezelfde assembly, maar niet voor een andere assembly.

beschermde intern

Het type of lid is toegankelijk voor elke code in dezelfde assembly of voor elke afgeleide klasse in een andere assembly.

Als er geen toegangsmodificator is ingesteld, wordt een standaardtoegangsmodificator gebruikt. Er is dus altijd een vorm van toegangsmodificatie, zelfs als deze niet is ingesteld.

waar

where twee doelen in C # kunnen dienen: typebeperking in een generiek argument en filtering van LINQ-query's.

Laten we eens kijken in een generieke klasse

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

T wordt een typeparameter genoemd. De klassedefinitie kan beperkingen opleggen aan de daadwerkelijke typen die kunnen worden geleverd voor T.

De volgende soorten beperkingen kunnen worden toegepast:

  • waarde type
  • referentietype
  • standaard constructor
  • overerving en implementatie

waarde type

In dit geval kunnen alleen struct s (inclusief 'primitieve' gegevenstypen zoals int , boolean enz.) Worden geleverd

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

referentietype

In dit geval kunnen alleen klassen worden geleverd

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

hybride waarde / referentietype

Af en toe is het wenselijk om typeargumenten te beperken tot die beschikbaar in een database, en deze zullen meestal toewijzen aan waardetypes en tekenreeksen. Omdat aan alle typebeperkingen moet worden voldaan, is het niet mogelijk om te specificeren where T : struct or string (dit is geen geldige syntaxis). U kunt dit probleem omzeilen door IConvertible te beperken tot IConvertible die ingebouwde typen heeft: "... Boolean, SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Decimal, DateTime, Char en String. " Het is mogelijk dat andere objecten IConvertible implementeren, hoewel dit in de praktijk zeldzaam is.

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

standaard constructor

Alleen typen die een standaardconstructor bevatten, zijn toegestaan. Dit omvat waardetypes en klassen die een standaard (parameterloze) constructor bevatten

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

overerving en implementatie

Alleen types die erven van een bepaalde basisklasse of een bepaalde interface implementeren kunnen worden geleverd.

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


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

De beperking kan zelfs verwijzen naar een andere typeparameter:

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

Er kunnen meerdere beperkingen worden opgegeven voor een typeargument:

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

De vorige voorbeelden tonen generieke beperkingen voor een klassedefinitie, maar beperkingen kunnen overal worden gebruikt waar een typeargument wordt geleverd: klassen, structs, interfaces, methoden, enz.

where ook een LINQ-clausule kan zijn. In dit geval is het analoog aan WHERE in 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

extern

Het extern sleutelwoord wordt gebruikt om methoden aan te geven die extern zijn geïmplementeerd. Dit kan worden gebruikt in combinatie met het kenmerk DllImport om onbeheerde code aan te roepen met behulp van Interop-services. die in dit geval met static modifier komt

Bijvoorbeeld:

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);
    }
}

Dit gebruikt de methode SetForegroundWindow geïmporteerd uit de User32.dll-bibliotheek

Dit kan ook worden gebruikt om een externe assemblage-alias te definiëren. waarmee we naar verschillende versies van dezelfde componenten kunnen verwijzen vanuit één assemblage.

Om naar twee merken met dezelfde volledig gekwalificeerde typenamen te verwijzen, moet een alias als volgt worden opgegeven bij een opdrachtprompt:

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

Hiermee worden de externe aliassen GridV1 en GridV2 gemaakt. Als u deze aliassen vanuit een programma wilt gebruiken, verwijst u ernaar met behulp van het externe trefwoord. Bijvoorbeeld:

extern alias GridV1;
extern alias GridV2;

bool

Trefwoord voor het opslaan van de Booleaanse waarden true en false . bool is een alias van System.Boolean.

De standaardwaarde van een bool is false.

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

Wil een bool null-waarden toestaan, dan moet deze als een bool worden geïnitialiseerd ?.

De standaardwaarde van een bool? is niets.

bool? a // default value is null

wanneer

De when is een trefwoord toegevoegd in C # 6 en wordt gebruikt voor uitzonderingsfiltering.

Vóór de invoering van de when zoekwoord, kan je er een vangclausule voor elk type uitzondering hebben gehad; met de toevoeging van het trefwoord is een fijnmaziger controle nu mogelijk.

Een when expressie een bevestigd catch tak, en indien het when voorwaarde true , de catch wordt clausule worden uitgevoerd. Het is mogelijk om verschillende catch te hebben met dezelfde typen uitzonderingsklasse en verschillend when voorwaarden gelden.

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);

ongehinderd

Het unchecked trefwoord voorkomt dat de compiler controleert op over- / onderstromen.

Bijvoorbeeld:

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

Zonder het unchecked sleutelwoord zal geen van beide toevoegingen worden gecompileerd.

Wanneer is dit nuttig?

Dit is handig omdat het kan helpen berekeningen te versnellen die zeker niet zullen overstromen, omdat het controleren op overloop tijd kost, of wanneer een overloop / onderstroom gewenst gedrag is (bijvoorbeeld bij het genereren van een hash-code).

leegte

Het gereserveerde woord "void" is een alias van het type System.Void en heeft twee toepassingen:

  1. Declareer een methode die geen retourwaarde heeft:
public void DoSomething()
{
    // Do some work, don't return any value to the caller.
}

Een werkwijze met een return type void kan nog het return zoekwoord in zijn lichaam. Dit is handig wanneer u de uitvoering van de methode wilt verlaten en de stroom naar de beller wilt teruggeven:

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

    if (condition)
        return;

    // Do some more work if the condition evaluated to false.
}
  1. Verklaar een wijzer naar een onbekend type in een onveilige context.

In een onveilige context kan een type een pointertype, een waardetype of een referentietype zijn. Een pointer type-aangifte is meestal type* identifier , waarbij het type een bekend type is - dat wil zeggen int* myInt , maar kan ook een void* identifier , wanneer het type onbekend is.

Merk op dat het afkeuren van een ongeldig pointertype wordt afgeraden door Microsoft.

als, als ... anders, als ... anders als


De instructie if wordt gebruikt om de stroom van het programma te regelen. Een if instructie geeft aan welke instructie moet worden uitgevoerd op basis van de waarde van een Boolean expressie.

Voor een enkele instructie zijn de braces {} optioneel, maar aanbevolen.

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

De if kan ook een else clausule hebben, die wordt uitgevoerd als de voorwaarde als onwaar evalueert:

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"

Met de if ... else if constructie kunt u meerdere voorwaarden opgeven:

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"

Het is belangrijk op te merken dat als aan een voorwaarde in het bovenstaande voorbeeld wordt voldaan, de besturing andere tests overslaat en naar het einde van dat specifieke if-construct springt. Dus de volgorde van tests is belangrijk als u if gebruikt ... anders als construct

C # Booleaanse uitdrukkingen gebruiken kortsluitevaluatie . Dit is belangrijk in gevallen waarin het evalueren van omstandigheden bijwerkingen kan hebben:

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

Er is geen garantie dat someOtherBooleanMethodWithSideEffects daadwerkelijk worden uitgevoerd.

Het is ook belangrijk in gevallen waarin eerdere omstandigheden ervoor zorgen dat het "veilig" is om latere te evalueren. Bijvoorbeeld:

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

De bestelling is in dit geval erg belangrijk omdat, als we de bestelling omdraaien:

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

het zal een NullReferenceException als someCollection null .

Doen

De operator do itereert over een codeblok totdat een voorwaardelijke query onwaar is. De do-while-lus kan ook worden onderbroken door een instructie goto , return , break of throw .

De syntaxis voor het do sleutelwoord is:

do { codeblok; } while ( staat );

Voorbeeld:

int i = 0;

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

Output:

"Do staat op lus nummer 1."
"Do is op lus nummer 2."
"Do is op lus nummer 3."
"Do is op lus nummer 4."
"Do is op lus nummer 5."

In tegenstelling tot de while lus is de do-while-lus Exit Controlled . Dit betekent dat de do-while-lus zijn instructies ten minste één keer zou uitvoeren, zelfs als de voorwaarde de eerste keer mislukt.

bool a = false;

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

operator

De meeste ingebouwde operators (inclusief conversie-operators) kunnen worden overbelast door het operator trefwoord te gebruiken in combinatie met de public en static modificaties.

De operatoren zijn er in drie vormen: unaire operatoren, binaire operatoren en conversieoperatoren.

Unaire en binaire operatoren vereisen ten minste één parameter van hetzelfde type als het bevattende type, en sommige vereisen een complementaire matchingoperator.

Conversie-operators moeten converteren naar of van het omhullende type.

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}}}";

}

Voorbeeld

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

Een struct type is een waardetype dat meestal wordt gebruikt om kleine groepen gerelateerde variabelen in te kapselen, zoals de coördinaten van een rechthoek of de kenmerken van een item in een inventaris.

Klassen zijn referentietypes, structs zijn waardetypes.

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();
        }
    }
}

Structuren kunnen ook constructors, constanten, velden, methoden, eigenschappen, indexers, operatoren, gebeurtenissen en geneste typen bevatten, hoewel als meerdere van dergelijke leden vereist zijn, u zou moeten overwegen om uw type in plaats daarvan een klasse te maken.


Enkele suggesties van MS over wanneer struct en wanneer klasse moet worden gebruikt:

OVERWEGEN

het definiëren van een struct in plaats van een klasse als exemplaren van het type klein zijn en gewoonlijk van korte duur zijn of gewoonlijk zijn ingebed in andere objecten.

AVOID

het definiëren van een struct tenzij het type alle volgende kenmerken heeft:

  • Het vertegenwoordigt logisch een enkele waarde, vergelijkbaar met primitieve typen (int, dubbel, etc.)
  • Het heeft een instantiegrootte van minder dan 16 bytes.
  • Het is onveranderlijk.
  • Het hoeft niet vaak in dozen te worden gedaan.

schakelaar

De switch instructie is een control-instructie die een switch-sectie selecteert om uit een lijst met kandidaten uit te voeren. Een schakelinstructie bevat een of meer schakelgedeelten. Elke switch sectie bevat één of meer case labels gevolgd door één of meer uitspraken. Als er geen hoofdetiket een overeenkomende waarde bevat, wordt het besturingselement overgedragen naar de default , als die er is. Doorval van zaken wordt strikt niet ondersteund in C #. Als echter 1 of meer case labels leeg zijn, volgt de uitvoering de code van het volgende case blok dat code bevat. Dit maakt het groeperen van meerdere case labels met dezelfde implementatie mogelijk. In het volgende voorbeeld, als month gelijk is aan 12, de code in case 2 zal worden uitgevoerd, omdat de case etiketten 12 1 en 2 zijn gegroepeerd. Als een case blok niet leeg is, een break aanwezig zijn voordat de volgende case label, anders zal de compiler vlag fout.

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;
}

Een case kan alleen worden gelabeld met een waarde die bekend is tijdens het compileren (bijvoorbeeld 1 , "str" , Enum.A ), dus een variable is geen geldig case label, maar een const of een Enum waarde is (evenals letterlijke waarde).

koppel

Een interface bevat de handtekeningen van methoden, eigenschappen en gebeurtenissen. De afgeleide klassen definiëren de leden, omdat de interface alleen de verklaring van de leden bevat.

Een interface wordt verklaard met behulp van het interface trefwoord.

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); } }
}

onveilig

Het unsafe trefwoord kan worden gebruikt in type- of methodedeclaraties of om een inline-blok aan te geven.

Het doel van dit trefwoord is het gebruik van de onveilige subset van C # voor het betreffende blok mogelijk te maken. De onveilige subset bevat functies zoals pointers, stackallocatie, C-achtige arrays, enzovoort.

Onveilige code is niet verifieerbaar en daarom wordt het gebruik ervan afgeraden. Voor het compileren van onveilige code moet een schakelaar worden doorgegeven aan de C # compiler. Bovendien vereist de CLR dat de lopende vergadering volledig vertrouwen heeft.

Ondanks deze beperkingen heeft onveilige code geldig gebruik om sommige bewerkingen performanter te maken (bijvoorbeeld array-indexering) of eenvoudiger (bijvoorbeeld interop met sommige onbeheerde bibliotheken).

Als een heel eenvoudig voorbeeld

// 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
   }
}

Tijdens het werken met pointers kunnen we de waarden van geheugenlocaties rechtstreeks wijzigen, in plaats van ze bij naam te moeten aanspreken. Merk op dat dit vaak het gebruik van het vaste sleutelwoord vereist om mogelijke geheugenbeschadiging te voorkomen terwijl de vuilnisman dingen verplaatst (anders krijgt u mogelijk de fout CS0212 ). Omdat er niet naar een variabele kan worden 'geschreven', hebben we vaak ook een tweede wijzer nodig die begint te wijzen naar dezelfde locatie als de eerste.

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++;"
        }
    }
}

Output:

1
4
9
16
25
36
49
64
81
100

unsafe ook het gebruik van stackalloc toe die geheugen toewijst aan de stapel zoals _alloca in de C run-time bibliotheek. We kunnen het bovenstaande voorbeeld wijzigen om stackalloc als volgt te gebruiken:

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++;
}

(Output is hetzelfde als hierboven)

stilzwijgend

Het implicit trefwoord wordt gebruikt om een conversie-operator te overbelasten. U kunt bijvoorbeeld een Fraction declareren die indien nodig automatisch naar een double moet worden geconverteerd en die automatisch kan worden geconverteerd vanuit 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);
    }
}

waar onwaar

De true en false zoekwoorden kunnen op twee manieren worden gebruikt:

  1. Als letterlijke Booleaanse waarden
var myTrueBool = true;
var myFalseBool = false;
  1. Als operators die kunnen worden overbelast
public static bool operator true(MyClass x)
{
    return x.value >= 0;
}

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

Het overladen van de valse operator was nuttig vóór C # 2.0, vóór de introductie van Nullable typen.
Een type dat de true operator overbelast, moet ook de false operator overbelasten.

draad

string is een alias voor het .NET-gegevenstype System.String , waarmee tekst (reeksen tekens) kunnen worden opgeslagen.

notatie:

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

Elk teken in de tekenreeks is gecodeerd in UTF-16, wat betekent dat elk teken minimaal 2 bytes opslagruimte nodig heeft.

ushort

Een numeriek type dat wordt gebruikt om 16-bits positieve gehele getallen op te slaan. ushort is een alias voor System.UInt16 en neemt 2 bytes geheugen in System.UInt16 .

Geldig bereik is 0 tot 65535 .

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

sbyte

Een numeriek type dat wordt gebruikt om 8-bits ondertekende gehele getallen op te slaan. sbyte is een alias voor System.SByte en neemt 1 byte geheugen in sbyte . Gebruik byte voor het niet-ondertekende equivalent.

Geldig bereik is -127 tot 127 (de rest wordt gebruikt om het teken op te slaan).

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

var

Een impliciet getypeerde lokale variabele die sterk wordt getypt, net alsof de gebruiker het type had opgegeven. In tegenstelling tot andere variabele declaraties, bepaalt de compiler het type variabele dat dit vertegenwoordigt op basis van de waarde die eraan is toegewezen.

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).

In tegenstelling tot andere typen variabelen moeten variabeldefinities met dit trefwoord worden geïnitialiseerd wanneer ze worden gedeclareerd. Dit komt doordat het trefwoord var een impliciet getypeerde variabele vertegenwoordigt.

var i;
i = 10;

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

Het var- trefwoord kan ook worden gebruikt om direct nieuwe gegevenstypen te maken. Deze nieuwe datatypes staan bekend als anonieme types . Ze zijn vrij handig, omdat ze een gebruiker in staat stellen een set eigenschappen te definiëren zonder eerst expliciet een objecttype te moeten aangeven.

Gewoon anoniem type

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

LINQ-query die een anoniem type retourneert

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);
}

U kunt het var-trefwoord gebruiken in de instructie foreach

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

    return false;
}

delegeren

Afgevaardigden zijn typen die een verwijzing naar een methode vertegenwoordigen. Ze worden gebruikt voor het doorgeven van methoden als argumenten aan andere methoden.

Afgevaardigden kunnen statische methoden, instantiemethoden, anonieme methoden of lambda-expressies bevatten.

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;
    }
}

Bij het toewijzen van een methode aan een gemachtigde is het belangrijk op te merken dat de methode hetzelfde retourtype en parameters moet hebben. Dit verschilt van 'normale' methode-overbelasting, waarbij alleen de parameters de handtekening van de methode definiëren.

Evenementen worden bovenop afgevaardigden gebouwd.

evenement

Met een event kan de ontwikkelaar een meldingspatroon implementeren.

Eenvoudig voorbeeld

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
    }
}

MSDN-referentie

gedeeltelijk

Het trefwoord partial kan worden gebruikt tijdens de typedefinitie van klasse, struct of interface om de typedefinitie in verschillende bestanden op te splitsen. Dit is handig om nieuwe functies in automatisch gegenereerde code op te nemen.

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;}
    }
}

Opmerking: een klasse kan worden opgesplitst in een willekeurig aantal bestanden. Elke aangifte moet echter onder dezelfde naamruimte en dezelfde assembly staan.

Methoden kunnen ook gedeeltelijk worden verklaard met behulp van het partial trefwoord. In dit geval bevat het ene bestand alleen de methodedefinitie en bevat een ander bestand de implementatie.

Een gedeeltelijke methode heeft zijn handtekening gedefinieerd in een deel van een gedeeltelijk type en de implementatie ervan is gedefinieerd in een ander deel van het type. Gedeeltelijke methoden stellen klasseontwerpers in staat om methodehaken te bieden, vergelijkbaar met event-handlers, die ontwikkelaars kunnen beslissen om te implementeren of niet. Als de ontwikkelaar geen implementatie levert, verwijdert de compiler de handtekening tijdens het compileren. De volgende voorwaarden zijn van toepassing op gedeeltelijke methoden:

  • Handtekeningen in beide delen van het gedeeltelijke type moeten overeenkomen.
  • De methode moet ongeldig terugkeren.
  • Geen toegangsmodificaties toegestaan. Gedeeltelijke methoden zijn impliciet privé.

- 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);
        }
    }
}

Opmerking: het type dat de gedeeltelijke methode bevat, moet ook gedeeltelijk worden verklaard.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow