Sök…


Introduktion

Nyckelord är fördefinierade, reserverade identifierare med speciell betydelse för kompilatorn. De kan inte användas som identifierare i ditt program utan prefixet @ . Till exempel är @if en juridisk identifierare men inte nyckelordet if .

Anmärkningar

C # har en fördefinierad samling av "nyckelord" (eller reserverade ord) som var och en har en speciell funktion. Dessa ord kan inte användas som identifierare (namn på variabler, metoder, klasser, etc.) om inte prefix med @ .

Bortsett från dessa använder C # också vissa nyckelord för att ge specifik betydelse i koden. De kallas kontextuella nyckelord. Kontextuella nyckelord kan användas som identifierare och behöver inte förinställas med @ när de används som identifierare.

stackalloc

stackalloc nyckelordet skapar ett stackalloc på stacken och returnerar en pekare till början av det minnet. Det stacktilldelade minnet tas bort automatiskt när räckvidden som det skapades i lämnas ut.

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

Används i ett osäkert sammanhang.

Som med alla pekare i C # finns det inga gränser som kontrollerar läsningar och uppdrag. Att läsa bortom gränserna för det tilldelade minnet kommer att ha oförutsägbara resultat - det kan komma åt någon godtycklig plats i minnet eller det kan orsaka ett undantag för åtkomstbrott.

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

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

Det stacktilldelade minnet tas bort automatiskt när räckvidden som det skapades i lämnas ut. Detta innebär att du aldrig ska returnera minnet som skapats med stackalloc eller lagra det utöver omfattningen.

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 endast användas när deklarering och initialisering av variabler. Följande är inte giltigt:

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

Anmärkningar:

stackalloc ska endast användas för prestationsoptimeringar (antingen för beräkning eller interop). Detta beror på att:

  • Avfallssamlaren krävs inte eftersom minnet tilldelas på bunten snarare än högen - minnet släpps så snart variabeln går ut ur räckvidden
  • Det är snabbare att tilldela minne på stacken snarare än högen
  • Öka chansen för cachträffar på CPU på grund av dataläge

flyktig

Att lägga till det volatile nyckelordet i ett fält indikerar för kompilatorn att fältets värde kan ändras av flera separata trådar. Det primära syftet med det volatile sökordet är att förhindra kompilatoroptimeringar som endast antar entrådig åtkomst. Att använda volatile säkerställer att fältets värde är det senaste värdet som är tillgängligt och att värdet inte är föremål för cachningen som icke-flyktiga värden är.

Det är bra att markera alla variabler som kan användas av flera trådar som volatile att förhindra oväntat beteende på grund av optimeringar bakom kulisserna. Tänk på följande kodblock:

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

I ovanstående kodblock läser kompilatorn uttalandena x = 5 och y = x + 10 och bestämmer att värdet på y alltid kommer att hamna som 15. Således kommer det att optimera det sista påståendet som y = 15 . Men variabeln x är i själva verket ett public fält och värdet på x kan modifieras vid körning genom en annan tråd som verkar på detta fält separat. Överväg nu detta modifierade kodblock. Observera att fältet x nu förklaras som volatile .

public class Example
{
    public volatile int x;

    public void DoStuff()
    {
        x = 5;

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

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

Nu, kompilatorn utseende för lästa användningsområden av området x och säkerställer att det aktuella värdet av fältet alltid hämtas. Detta säkerställer att även om flera trådar läser och skriver till detta fält, hämtas det aktuella värdet för x alltid.

volatile kan endast användas på fält inom class eller struct . Följande är inte giltigt :

public void MyMethod()
{
    volatile int x;
}

volatile kan endast tillämpas på fält av följande typer:

  • referenstyper eller generiska typparametrar kända som referenstyper
  • primitiva typer som sbyte , byte , short , ushort , int , uint , char , float och bool
  • enums typer baserade på byte , sbyte , short , ushort , int eller uint
  • IntPtr och UIntPtr

Anmärkningar:

  • Den volatile modifieraren används vanligtvis för ett fält som nås av flera trådar utan att använda låsmeddelandet för att serialisera åtkomst.
  • Det volatile nyckelordet kan tillämpas på fält med referenstyper
  • Det volatile nyckelordet kommer inte att fungera på 64-bitars primitiv på en 32-bitars atomatom. Interlocked operationer som Interlocked.Read och Interlocked.Exchange måste fortfarande användas för säker flertrådad åtkomst på dessa plattformar.

fast

Det fasta uttalet fixar minnet på en plats. Objekt i minnet rör sig vanligtvis runt, detta gör det möjligt att samla skräp. Men när vi använder osäkra pekare till minnesadresser får det minnet inte flyttas.

  • Vi använder det fasta uttalet för att säkerställa att soporuppsamlaren inte flyttar strängdata.

Fasta variabler

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

Används i ett osäkert sammanhang.

Fast matrisstorlek

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

fixed kan endast användas på fält i en struct (måste också användas i ett osäkert sammanhang).

standard

För klasser, gränssnitt, delegera, array, nullable (som int?) Och pekartyper default(TheType) null :

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

För strukturer och enums returnerar default(TheType) samma som den 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 vara särskilt användbar när T är en generisk parameter för vilken ingen begränsning finns för att avgöra om T är en referenstyp eller en värdetyp, till exempel:

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

readonly

Det readonly nyckelordet är en fältmodifierare. När en readonly innehåller en readonly modifierare, kan tilldelningar till det fältet endast ske som en del av deklarationen eller i en konstruktör i samma klass.

Det readonly nyckelordet skiljer sig från const nyckelordet. En const fält kan bara initieras vid deklarationen av fältet. Ett readonly fält kan initialiseras antingen vid deklarationen eller i en konstruktör. Därför kan readonly ha olika värden beroende på konstruktören som används.

Det readonly nyckelordet används ofta när man injicerar beroenden.

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

Obs! Att förklara ett fält läsbart innebär inte obrukbarhet . Om fältet är en referenstyp kan objektets innehåll ändras. Readonly används vanligtvis för att förhindra att objektet skrivs över och tilldelas endast under inställning av det objektet.

Obs: Inuti konstruktören kan ett läsbart fält tilldelas

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

//In code

private readonly Car car = new Car();

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

som

Den as sökord är en operatör som liknar en gjuten. Om en cast inte är möjlig, använder as producerar null snarare än att resultera i en InvalidCastException .

expression as type motsvarar expression is type ? (type)expression : (type)null med förbehållet som as bara gäller för referensomvandlingar, nollbara konverteringar och boxningskonverteringar. Användardefinierade omvandlingar stöds inte; en vanlig roll måste användas istället.

För utvidgningen ovan genererar kompilatorn kod så att expression endast utvärderas en gång och använder en enda dynamisk typkontroll (till skillnad från de två i exemplet ovan).

as kan vara användbart när man förväntar sig ett argument för att underlätta flera typer. Specifikt ger det användaren flera alternativ - snarare än att kontrollera alla möjligheter med is innan du gjuter, eller bara gjuter och fångar undantag. Det är bästa praxis att använda 'som' när du kastar / kontrollerar ett föremål som endast orsakar en straff som inte är boxad. Att använda is att kontrollera, då gjutning kommer att orsaka två unboxing straffar.

Om ett argument förväntas vara ett exempel av en specifik typ föredras en vanlig roll eftersom syftet är tydligare för läsaren.

Eftersom ett samtal till as kan ge null , kontrollera alltid resultatet för att undvika en NullReferenceException .

Exempel på användning

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-demonstration på .NET Fiddle

Motsvarande exempel utan att använda as :

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

Det här är användbart när man åsidosätter funktionen Equals i anpassade klasser.

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
    }

}

är

Kontrollerar om ett objekt är kompatibelt med en viss typ, dvs om ett objekt är en instans av typen BaseInterface , eller en typ som härrör från 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

Om avsikten med gjutna är att använda objektet, är det bästa praxis att använda as sökordet'

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
}

Men från C # 7- pattern matching utvidgas operatören för att leta efter en typ och förklara en ny variabel samtidigt. Samma koddel med C # 7:

7,0
if(d is BaseClass asD ){
    asD.Method();
}

sorts

Returnerar Type av ett objekt utan att behöva instansera det.

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 används för att representera värden som aldrig kommer att förändras under programmets livstid. Dess värde är konstant från kompileringstid , i motsats till det readonly nyckelordet, vars värde är konstant från körtiden.

Till exempel, eftersom ljusets hastighet aldrig kommer att förändras, kan vi lagra den i en konstant.

const double c = 299792458;  // Speed of light

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

Detta är väsentligen detsamma som att ha return mass * 299792458 * 299792458 , eftersom kompilatorn direkt kommer att ersätta c med dess konstant värde.

Som ett resultat kan c inte ändras när deklarerats. Följande kommer att skapa ett kompileringstidsfel:

const double c = 299792458;  // Speed of light 

c = 500;  //compile-time error

En konstant kan förinställas med samma åtkomstmodifierare som metoder:

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

const medlemmar är static av naturen. Att använda static uttryckligen är inte tillåtet.

Du kan också definiera metod-lokala konstanter:

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

Dessa kan inte för prefixas med ett private eller public nyckelord, eftersom de implicit är lokala för den metod de definieras i.


Inte alla typer kan användas i en const . Värdetyperna som är tillåtna är de fördefinierade typerna sbyte , byte , short , ushort , int , uint , long , ulong , char , float , double , decimal , bool och alla enum . Att försöka förklara const medlemmar med andra värdetyper (som TimeSpan eller Guid ) kommer att misslyckas vid sammanställningstiden.

För den speciella fördefinierade referenstypen string , kan konstanter deklareras med något värde. För alla andra referenstyper kan konstanter deklareras men måste alltid ha värdet null .


Eftersom const värden är kända vid kompilering, tillåts de som case i en switch uttalande som standard argument för valfria parametrar som argument för att attributspecifikationer, och så vidare.


Om const värden används över olika enheter måste man vara försiktig med versionering. Om till exempel A definierar en public const int MaxRetries = 3; och montering B använder den konstanten, om värdet på MaxRetries senare ändras till 5 i enhet A (som sedan kompileras igen) kommer den förändringen inte att vara effektiv i montering B om inte enhet B också kompileras igen (med en hänvisning till den nya versionen av A).

Av den anledningen, om ett värde kan förändras vid framtida revideringar av programmet, och om värdet måste vara synligt, förklara inte det värdet const om du inte vet att alla beroende enheter kommer att sammanställas när något ändras. Alternativet är att använda static readonly istället för const , vilket löses vid körning.

namnutrymme

namespace nyckelord är en organisationskonstruktion som hjälper oss att förstå hur en kodbas baseras. Namnområden i C # är virtuella utrymmen snarare än i en fysisk mapp.

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

Namnområden i C # kan också skrivas i kedjad syntax. Följande motsvarar ovan:

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

försök, fånga, äntligen, kasta

try , catch , finally , och throw låter dig hantera undantag i din kod.

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

Obs! return sökord kan användas i try blocket och finally blocket kommer fortfarande att genomföras (precis innan han återvände). Till exempel:

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

Uttalningsanslutningen. connection.Close() kommer att köras innan resultatet av connection.Get(query) returneras.

Fortsätta

Överför omedelbart kontrollen till nästa iteration av den medföljande slingkonstruktionen (för, förhand, gör, medan):

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

Produktion:

5
6
7
8
9

Live Demo på .NET Fiddle

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

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

Produktion:

en
b
c
d

Live-demonstration på .NET Fiddle

ref, ut

ref och out nyckelorden får ett argument att skickas genom referens, inte efter värde. För värdetyper betyder det att värdet på variabeln kan ändras med callee.

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

För referenstyper kan instansen i variabeln inte bara modifieras (som är fallet utan ref ), utan kan också ersättas helt:

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

Den huvudsakliga skillnaden mellan sökordet out och ref är att ref kräver att variabeln initieras av den som ringer, medan out överför det ansvaret till Callee.

För att använda en out parameter måste både metoddefinitionen och anropsmetoden uttryckligen använda out nyckelordet.

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 på .NET Fiddle

Följande inte sammanställa, eftersom out parametrar måste ha ett värde tilldelas innan metoden returnerar (det skulle sammanställa använda ref istället):

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

använder ut sökord som generisk modifierare

out nyckelord kan också användas i generiska parametrar när man definierar generiska gränssnitt och delegater. I detta fall out sökord anger att parametern typ är kovariant.

Med Covariance kan du använda en mer härledd typ än den som anges av den generiska parametern. Detta möjliggör implicit konvertering av klasser som implementerar variantgränssnitt och implicit konvertering av delegattyper. Covariance och contravariance stöds för referenstyper, men de stöds inte för värdetyper. - 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

kontrollerad, avmarkerad

De checked och unchecked nyckelorden definierar hur operationer hanterar matematiskt överflöde. "Överflöde" i sammanhanget för de checked och unchecked nyckelorden är när en heltals aritmetisk operation resulterar i ett värde som är större i storlek än måldatatypen kan representera.

När överflödet inträffar inom ett checked block (eller när kompilatorn är inställd på att använda kontrollerad aritmetik globalt) kastas ett undantag för att varna för oönskat beteende. Under tiden, i ett unchecked block, är överflödet tyst: inga undantag kastas, och värdet kommer helt enkelt att slingra sig till motsatt gräns. Detta kan leda till subtila, svåra att hitta buggar.

Eftersom de flesta aritmetiska operationer utförs på värden som inte är stora eller små nog att flyta över, för det mesta, finns det inget behov att uttryckligen definiera ett block som checked . Man måste vara försiktig när man gör aritmetik på obegränsad ingång som kan orsaka överflöde, till exempel när man gör aritmetik i rekursiva funktioner eller när man använder användarinmatning.

Varken checked eller unchecked påverkar flytande punkt aritmetiska operationer.

När ett block eller ett uttryck förklaras som unchecked tillåts alla aritmetiska operationer inuti det att flyta utan att orsaka ett fel. Ett exempel där detta beteende önskas skulle vara beräkningen av en kontrollsumma, där värdet tillåts "svepa runt" under beräkningen:

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

En av de vanligaste användningarna för unchecked är att implementera en anpassad åsidosättning för object.GetHashCode() , en typ av kontrollsumma. Du kan se nyckelordets användning i svaren på denna fråga: Vad är den bästa algoritmen för ett åsidosatt System.Object.GetHashCode? .

När ett block eller ett uttryck förklaras som checked , kommer alla aritmetiska operationer som orsakar ett översvämning att en OverflowException kastas.

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

Både markerade och avmarkerade kan vara i block- och uttrycksform.

Kontrollerade och avmarkerade block påverkar inte kallade metoder, endast operatörer som kallas direkt i den aktuella metoden. Till exempel Enum.ToObject() , Convert.ToInt32() och användardefinierade operatörer av anpassade kontrollerade / okontrollerade sammanhang.

Obs : Standardöverflödets standardbeteende (kontrollerat kontra ej avkryssat) kan ändras i projektegenskaperna eller genom kommandoradsknappen / kontrollerad [+ | -] . Det är vanligt att kontrollerade operationer för felsökning byggs och som inte är markerade för utgåvor. De checked och unchecked nyckelorden används sedan endast när en standardmetod inte gäller och du behöver ett tydligt beteende för att säkerställa riktigheten.

gå till

goto kan användas för att hoppa till en specifik rad i koden, specificerad av en etikett.

goto som en:

Märka:

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

Live-demonstration på .NET Fiddle

Ärende:

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-demonstration på .NET Fiddle

Detta är särskilt användbart för att utföra flera beteenden i ett switch-uttalande, eftersom C # inte stöder fall-case-block .

Undantagsförsök igen

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

Live-demonstration på .NET Fiddle

I likhet med många språk avskräcks användningen av goto-nyckelord förutom fallen nedan.

Giltiga användningar av goto som gäller C #:

  • Fall-through case i switch statement.

  • Multi-level break. LINQ kan ofta användas istället, men det har vanligtvis sämre prestanda.

  • Resursuppdelning när du arbetar med oöppnade objekt på låg nivå. I C # bör objekt på låg nivå vanligtvis lindas i separata klasser.

  • Finite state-maskiner, till exempel parsers; används internt av kompilatorgenererade async / väntar tillståndsmaskiner.

enum

enum nyckelordet berättar kompilatorn att denna klass ärver från den abstrakta klassen Enum , utan att programmeraren måste uttryckligen ärva den. Enum är en ättling till ValueType , som är avsedd för användning med distinkta uppsättningar av nämnda konstanter.

public enum DaysOfWeek
{
    Monday,
    Tuesday,
}

Du kan valfritt ange ett specifikt värde för var och en (eller några av dem):

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

I det här exemplet utelämnade jag ett värde för 0, detta är vanligtvis en dålig praxis. Ett enum kommer alltid att ha ett standardvärde som produceras genom uttrycklig konvertering (YourEnumType) 0 , där YourEnumType är din deklarerade enume typ. Utan ett värde på 0 definierat kommer ett enum inte att ha ett definierat värde vid initiering.

Den underliggande standardtypen för enum är int , du kan ändra den underliggande typen till vilken som helst integrerad typ inklusive byte , sbyte , short , ushort , int , uint , long och ulong . Nedan visas ett enum med underliggande byte :

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

Observera också att du kan konvertera till / från underliggande typ helt enkelt med en roll:

int value = (int)NotableYear.EndOfWwI;

Av dessa skäl bör du alltid kontrollera om enum är giltig när du avslöjar biblioteksfunktioner:

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

    // ...
}

bas

Den base nyckelordet används för att komma åt medlemmar från en basklass. Det används ofta för att ringa basimplementeringar av virtuella metoder eller för att specificera vilken baskonstruktör som ska kallas.

Att välja en konstruktör

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

Samtal basimplementering av virtuell metod

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

Det är möjligt att använda basnyckelordet för att ringa en basimplementering från vilken metod som helst. Detta knyter metodsamtalet direkt till basimplementeringen, vilket innebär att även om nya barnklasser åsidosätter en virtuell metod kommer basimplementeringen fortfarande att kallas så detta måste användas med försiktighet.

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

för varje

foreach används för att iterera över elementen i en matris eller objekten i en samling som implementerar IEnumerable ✝.

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

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

Detta kommer att matas ut

"Hej världen!"
"Hur mår du idag?"
"Adjö"

Live-demonstration på .NET Fiddle

Du kan avsluta foreach loop när som helst genom att använda paus sökord eller gå vidare till nästa iteration använder fortsätter sökord.

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-demonstration på .NET Fiddle

Lägg märke till att iterationsordningen är garanterad endast för vissa samlingar som matriser och List , men inte garanterad för många andra samlingar.


✝ Medan IEnumerable vanligtvis används för att indikera otaliga samlingar, kräver foreach endast att samlingen exponerar offentligt object GetEnumerator() -metoden, vilket ska returnera ett objekt som exponerar bool MoveNext() och object Current { get; } egendom.

params

params tillåter en metodparameter att ta emot ett variabelt antal argument, dvs noll, ett eller flera argument är tillåtna för den parametern.

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

Den här metoden kan nu kallas med en typisk lista med int argument eller en matris med ints.

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

params måste visas högst en gång och om den används måste den vara sist i argumentlistan, även om den efterföljande typen skiljer sig från arrayens.


Var försiktig när du överbelastar funktioner när du använder nyckelordet params . C # föredrar att matcha mer specifika överbelastningar innan man försöker använda överbelastningar med params . Till exempel om du har två metoder:

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

Då kommer den specifika 2-överbelastningen att ha företräde innan du försöker överbelastningen av params .

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

ha sönder

I en loop (för, foreach, gör, medan) den break uttalande avbryter utförandet av den innersta slingan och återgår till koden efter det. Det kan också användas med yield där den anger att en iterator har kommit till slut.

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-demonstration på .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);
}

Break-statement används också i switch-case-konstruktioner för att bryta ut från ett fall eller standardsegment.

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

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

I switch-uttalanden krävs nyckelordet "break" i slutet av varje ärende. Detta strider mot vissa språk som gör det möjligt att "falla igenom" till nästa uttalande i serien. Lösningar för detta skulle inkludera "goto" uttalanden eller stapla "case" uttalanden i följd.

Följande kod ger siffrorna 0, 1, 2, ..., 9 och den sista raden kommer inte att köras. yield break betyder slutet på funktionen (inte bara en slinga).

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-demonstration på .NET Fiddle

Observera att till skillnad från vissa andra språk finns det inget sätt att märka en viss paus i C #. Detta innebär att när det gäller kapslade öglor kommer bara den inre slingan att stoppas:

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

Om du vill bryta ut ur den yttre slingan här kan du använda en av flera olika strategier, till exempel:

  • Ett goto- uttalande för att hoppa ut ur hela loopstrukturen.
  • En specifik shouldBreak ( shouldBreak i följande exempel) som kan kontrolleras i slutet av varje iteration av den yttre slingan.
  • Refactoring koden för att använda en return uttalande i det innersta slingan, eller undvika hela kapslade slingstrukturen helt och hållet.
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

abstrakt

En klass markerad med nyckelordet abstract kan inte instanseras.

En klass måste markeras som abstrakt om den innehåller abstrakta medlemmar eller om den ärver abstrakta medlemmar som den inte implementerar. En klass kan markeras som abstrakt även om inga abstrakta medlemmar är inblandade.

Abstrakta klasser används vanligtvis som basklasser när någon del av implementeringen måste specificeras av en annan komponent.

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

En metod, egenskap eller händelse markerad med nyckelordet abstract indikerar att implementeringen för den medlemmen förväntas tillhandahållas i en underklass. Som nämnts ovan kan abstrakta medlemmar endast visas i abstrakta klasser.

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

flyta, dubbel, decimal

flyta

float är ett alias till .NET-datatypen System.Single . Det gör det möjligt att lagra IEEE 754-flyttalsnummer med enstaka precision. Denna datatyp finns i mscorlib.dll som implicit hänvisas till av alla C # -projekt när du skapar dem.

Ungefärligt intervall: -3,4 × 10 38 till 3,4 × 10 38

Decimal precision: 6-9 betydande siffror

Notation :

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

Det bör noteras att float ofta resulterar i betydande rundningsfel. I tillämpningar där precision är viktig bör andra datatyper beaktas.


dubbel-

double är ett alias till .NET-datatypen System.Double . Det representerar ett dubbelprecisions 64-bitars flytpunktsnummer. Denna datatyp finns i mscorlib.dll som implicit hänvisas till i något C # -projekt.

Område: ± 5,0 × 10 −324 till ± 1,7 × 10 308

Decimal precision: 15-16 betydande siffror

Notation :

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

decimal

decimal är ett alias till .NET-datatypen System.Decimal . Det representerar ett nyckelord som indikerar en 128-bitars datatyp. Jämfört med typer av flytande punkter har decimaltypen mer precision och ett mindre intervall, vilket gör det lämpligt för finansiella och monetära beräkningar. Denna datatyp finns i mscorlib.dll som implicit hänvisas till i något C # -projekt.

Område: -7,9 × 10 28 till 7,9 × 10 28

Decimal precision: 28-29 betydande siffror

Notation :

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

uint

Ett osignerat heltal , eller uint , är en numerisk datatyp som bara kan innehålla positiva heltal. Som namnet antyder representerar det ett osignerat 32-bitars heltal. Själva uint- nyckelordet är ett alias för Common Type System typ System.UInt32 . Denna datatyp finns i mscorlib.dll , som implicit hänvisas till av alla C # -projekt när du skapar dem. Den upptar fyra byte minne.

Osignerade heltal kan innehålla valfritt värde från 0 till 4 294 967 295.

Exempel på hur och nu inte att förklara osignerade heltal

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

Observera: Enligt Microsoft rekommenderas det att använda int- datatypen där det är möjligt eftersom uint- datatypen inte är CLS-kompatibel.

detta

Det this nyckelordet hänvisar till den aktuella instansen av klass (objekt). På så sätt kan två variabler med samma namn, en på klassnivå (ett fält) och en som är en parameter (eller lokal variabel) för en metod, särskiljas.

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

Andra användningar av nyckelordet är kedja icke-statisk konstruktör överbelastning :

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

och skriva indexerare :

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

och förklara förlängningsmetoder :

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

Om det inte finns någon konflikt med en lokal variabel eller parameter, är det en stilmässig fråga om att använda this eller inte, så this.MemberOfType och MemberOfType skulle vara likvärdiga i så fall. Se även base sökord.

Observera att om en förlängning metod att kallas på den aktuella exempel this krävs. Om du till exempel befinner dig i en icke-statisk metod i en klass som implementerar IEnumerable<> och du vill ringa anknytningen Count från tidigare måste du använda:

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

och this kan inte utelämnas där.

för

Syntax: for (initializer; condition; iterator)

  • for loop används vanligtvis när antalet iterationer är kända.
  • Uttalningarna i initializer körs endast en gång innan du går in i slingan.
  • condition innehåller ett booleskt uttryck som utvärderas i slutet av varje loop-iteration för att avgöra om slingan ska lämna eller ska köras igen.
  • iterator definierar vad som händer efter varje iteration av slingans kropp.

Detta exempel visar hur for kan användas för att iterera över tecknen i en sträng:

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

Produktion:

H
e
l
l
o

Live Demo på .NET Fiddle

Alla uttryck som definierar en for uttalande är valfria; till exempel används följande uttalande för att skapa en oändlig slinga:

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

initializer kan innehålla flera variabler, så länge de är av samma typ. condition kan bestå av alla uttryck som kan utvärderas till en bool . Och iterator kan utföra flera åtgärder separerade med komma:

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

Produktion:

Hej
hello1
hello12

Live Demo på .NET Fiddle

medan

while operatören itererar över ett kodblock tills den villkorliga frågan är lika falsk eller koden avbryts med en goto , return , break eller throw statement.

Syntax för while nyckelord:

medan ( villkor ) { kodblock; }

Exempel:

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

Produktion:

"While är på slinga nummer 1."
"While är på slinga nummer 2."
"While är på slinga nummer 3."
"While är på slinga nummer 4."
"While är på slinga nummer 5."

Live Demo på .NET Fiddle

En stundslinga är ingångskontrollerad , eftersom villkoret kontrolleras innan exekveringen av det bifogade kodblocket. Detta innebär att stundslingan inte skulle utföra sina uttalanden om villkoret är falskt.

bool a = false;

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

Att ge en while tillstånd utan provisione att bli falskt någon gång kommer att resultera i en oändlig eller oändlig loop. Så långt som möjligt bör detta undvikas, men det kan finnas vissa exceptionella omständigheter när du behöver detta.

Du kan skapa en sådan loop enligt följande:

while (true)
{
//...
}

Observera att C # -kompilatorn förvandlar slingor som

while (true)
{
// ...
}

eller

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

in i

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

Observera att en stundslinga kan ha något villkor, oavsett hur komplicerat, så länge den utvärderar (eller returnerar) ett booleskt värde (bool). Den kan också innehålla en funktion som returnerar ett booleskt värde (eftersom en sådan funktion utvärderar till samma typ som ett uttryck som `a == x '). Till exempel,

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

lämna tillbaka

MSDN: Returmeddelandet avslutar exekveringen av den metod där den visas och returnerar kontrollen till den ringande metoden. Det kan också returnera ett valfritt värde. Om metoden är en ogiltig typ kan returrättet utelämnas.

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
}

i

Den in sökord har tre användningsområden:

a) Som en del av syntaxen i ett foreach uttalande eller som en del av syntaxen i en LINQ-fråga

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

b) I samband med generiska gränssnitt och generiska delegattyper innebär contravarians för typparametern i fråga:

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

c) I samband med LINQ-frågan hänvisas till den samling som fråges

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

använder sig av

Det finns två typer av att using sökordsanvändning, using statement och using directive :

  1. använder uttalande :

    Det using nyckelordet säkerställer att objekt som implementerar det IDisposable gränssnittet bortskaffas korrekt efter användning. Det finns ett separat ämne för det använda uttalandet

  2. med direktiv

    Den using direktivet har tre användningsområden, se MSDN-sida för att använda direktivet . Det finns ett separat ämne för direktivet om användning .

sluten

När den appliceras på en klass förhindrar den sealed modifieraren att andra klasser ärver från den.

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

När den appliceras på en virtual metod (eller virtuell egenskap) förhindrar den sealed modifieraren att denna metod (egenskap) överskrids i härledda klasser.

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

storlek av

Används för att erhålla storleken i byte för en okontrollerad typ

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

statisk

Den static modifieraren används för att deklarera en statisk medlem, som inte behöver instanseras för att få åtkomst, utan istället nås helt enkelt genom dess namn, dvs. DateTime.Now .

static kan användas med klasser, fält, metoder, egenskaper, operatörer, evenemang och konstruktörer.

Medan en instans av en klass innehåller en separat kopia av alla instansfält i klassen finns det bara en kopia av varje statiskt fält.

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 lika med det totala antalet instanser av A klass.

Den statiska modifieraren kan också användas för att deklarera en statisk konstruktör för en klass, för att initialisera statisk data eller köra kod som bara behöver kallas en gång. Statiska konstruktörer kallas innan klassen refereras för första gången.

class A
{
    static public DateTime InitializationTime;

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

En static class är markerad med det static nyckelordet och kan användas som en fördelaktig behållare för en uppsättning metoder som arbetar med parametrar, men behöver inte nödvändigtvis knytas till en instans. På grund av klassens static karaktär kan den inte instansieras, men den kan innehålla en static constructor . Vissa funktioner i en static class inkluderar:

  • Kan inte ärvas
  • Kan inte ärva från något annat än Object
  • Kan innehålla en statisk konstruktör men inte en instanskonstruktör
  • Kan endast innehålla statiska medlemmar
  • Är tätad

Kompilatorn är också vänlig och kommer att informera utvecklaren om det finns några instansmedlemmar i klassen. Ett exempel skulle vara en statisk klass som konverterar mellan amerikanska och kanadensiska mätvärden:

static class ConversionHelper {
    private static double oneGallonPerLitreRate = 0.264172;

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

När klasser förklaras statiska:

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

alla funktioner, egenskaper eller medlemmar i klassen måste också förklaras statiska. Ingen instans av klassen kan skapas. I grunden ger en statisk klass dig möjlighet att skapa paket med funktioner som logiskt grupperas.

Eftersom C # 6 static också kan användas tillsammans using att importera statiska medlemmar och metoder. De kan sedan användas utan klassnamn.

Gamla sättet, utan att using static :

using System;

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

}

Exempel med using static

using static System.Console;

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

}

nackdelar

Medan statiska klasser kan vara oerhört användbara, kommer de med sina egna varningar:

  • När den statiska klassen har anropats laddas klassen i minnet och kan inte köras genom skräpkollektorn förrän AppDomain som hyser den statiska klassen lossas.

  • En statisk klass kan inte implementera ett gränssnitt.

int

int är ett alias för System.Int32 , som är en datatyp för signerade 32-bitars heltal. Denna datatyp kan hittas i mscorlib.dll som implicit hänvisas till av alla C # -projekt när du skapar dem.

Område: -2,147,483,648 till 2,147,483,647

int int1 = -10007;
var int2 = 2132012521;     

lång

Det långa nyckelordet används för att representera signerade 64-bitars heltal. Det är ett alias för datatypen System.Int64 finns i mscorlib.dll , vilket implicit refereras av varje C # -projekt när du skapar dem.

Alla långa variabler kan förklaras både uttryckligt och implicit:

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

En lång variabel kan innehålla valfritt värde från –9,223,372,036,854,775,808 till 9 223,372,036,854,775,807 och kan vara användbart i situationer där en variabel måste hålla ett värde som överstiger gränserna för vad andra variabler (som int- variabeln) kan hålla.

ulong

Nyckelord som används för osignerade 64-bitars heltal. Det representerar System.UInt64 datatyp som finns i mscorlib.dll som implicit hänvisas till av alla C # -projekt när du skapar dem.

Område: 0 till 18.446.744.073.709.551.615

ulong veryLargeInt = 18446744073609451315;
var anotherVeryLargeInt = 15446744063609451315UL;

dynamisk

Det dynamic nyckelordet används med dynamiskt typade objekt . Objekt som deklareras som dynamic avgivna statiska kontroller för kompileringstid och utvärderas istället vid körning.

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

Följande exempel använder dynamic med Newtonsofts bibliotek Json.NET för att enkelt kunna läsa data från en deserialiserad JSON-fil.

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
}

Det finns vissa begränsningar förknippade med det dynamiska sökordet. En av dem är användningen av förlängningsmetoder. Följande exempel lägger till en förlängningsmetod för sträng: SayHello .

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

Den första metoden är att kalla det som vanligt (som för en sträng):

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

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

Inget sammanställningsfel, men vid körning får du en RuntimeBinderException . Lösningen för det här är att anropa förlängningsmetoden via statisk klass:

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

virtuellt, åsidosättande, nytt

virtuellt och åsidosätta

Det virtual nyckelordet låter en metod, egenskap, indexerare eller händelse åsidosättas av härledda klasser och presentera polymorfiskt beteende. (Medlemmar är som standard icke-virtuella i C #)

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

För att åsidosätta en medlem används det override nyckelordet i de härledda klasserna. (Observera att medlemmarnas signatur måste vara identisk)

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

Det virtuella medlemmarnas polymorfa beteende innebär att när den åberopas bestäms den verkliga medlemmen som körs vid körtid istället för vid sammanställningstiden. Den övervägande medlemmen i den mest härledda klassen som det specifika objektet är ett exempel på kommer att vara den som körs.

Kort sagt, objekt kan deklareras av typen BaseClass vid sammanställningstid, men om det under körning är ett exempel på DerivedClass kommer den åsidosatta medlemmen att köras:

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

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

Att åsidosätta en metod är valfritt:

public class SecondDerivedClass: DerivedClass {}

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

ny

Eftersom endast medlemmar definierade som virtual är överbrytbara och polymorfa, kan en härledd klass som omdefinierar en icke virtuell medlem leda till oväntade resultat.

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!    

När detta händer bestäms det utförda medlemmet alltid vid sammanställningstiden baserat på objektets typ.

  • Om objektet deklareras av typen BaseClass (även om körtiden är av en härledd klass) BaseClass metoden för BaseClass
  • Om objektet deklareras av typen DerivedClass metoden för DerivedClass .

Detta är vanligtvis en olycka (När en medlem läggs till bastypen efter att en identisk tillagts till den härledda typen) och en kompilatorvarning CS0108 genereras i dessa scenarier.

Om det var avsiktligt, används det new sökordet för att undertrycka kompilatorvarningen (Och informera andra utvecklare om dina avsikter!). beteendet förblir detsamma, det new sökordet undertrycker bara kompilatorvarningen.

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! 

Användningen av åsidosättande är inte valfri

Till skillnad från i C ++ är användningen av det override sökordet inte valfritt:

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

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

Ovanstående exempel orsakar också varning CS0108 , eftersom B.Foo() inte automatiskt åsidosätter A.Foo() . Lägg till override när avsikten är att åsidosätta basklassen och orsaka polymorfiskt beteende, lägg till new när du vill ha icke-polymorfiskt beteende och lösa samtalet med den statiska typen. Det senare bör användas med försiktighet, eftersom det kan orsaka allvarlig förvirring.

Följande kod resulterar till och med i ett fel:

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

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

Deriverade klasser kan introducera polymorfism

Följande kod är helt giltig (även om den är sällsynt):

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

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

Nu använder alla objekt med en statisk referens av B (och dess derivat) polymorfism för att lösa Foo() , medan referenser till A använder 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";

Virtuella metoder kan inte vara privata

C # -kompilatorn är strikt för att förhindra meningslösa konstruktioner. Metoder markerade som virtual kan inte vara privata. Eftersom en privat metod inte kan ses från en härledd typ kan den inte heller skrivas över. Detta misslyckas med att sammanställa:

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

async, vänta

Nyckelordet await lades till som en del av C # 5.0-versionen som stöds från Visual Studio 2012 och framåt. Det utnyttjar TPL Parallel Library (TPL) vilket gjorde flertrådarna relativt lättare. async och await används i par i samma funktion som visas nedan. Nyckelordet await används för att pausa exekveringen av den aktuella asynkrona metoden tills den väntade asynkrona uppgiften är klar och / eller dess resultat returneras. För att använda nyckelordet som await måste metoden som använder det markeras med async nyckelordet.

Att använda async med void är starkt avskräckt. För mer info kan du titta här .

Exempel:

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

Produktion:

"Starta en värdelös process ..."

** ... 1 sekunders fördröjning ... **

"En värdelös process tog 1000 millisekunder att genomföra."

Nyckelordet par async och await kan utelämnas om en Task eller Task<T> returneringsmetod bara returnerar en enda asynkron operation.

Snarare än detta:

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

Det är att föredra att göra detta:

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

I C # 5.0 kan inte await användas i catch och finally .

6,0

Med C # 6.0 kan await användas i catch och finally .

röding

En kol är en bokstav som lagras i en variabel. Det är en inbyggd värdetyp som tar två byte minne. Det representerar System.Char datatyp som finns i mscorlib.dll som implicit refereras av varje C # -projekt när du skapar dem.

Det finns flera sätt att göra detta.

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

En karaktär kan implicit konverteras till ushort, int, uint, long, ulong, float, double, eller decimal och det kommer att returnera heltalets värde för char.

ushort u = c;

returnerar 99 etc.

Det finns dock inga implicita konverteringar från andra typer till char. Istället måste du kasta dem.

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

låsa

lock ger gängsäkerhet för ett kodblock, så att det bara kan nås av en tråd inom samma process. Exempel:

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

Använd fall:

När du har ett kodblock som kan ge biverkningar om de körs av flera trådar samtidigt. Låsnyckelordet tillsammans med ett delat synkroniseringsobjekt ( _objLock i exemplet) kan användas för att förhindra det.

Observera att _objLock inte kan vara null och att flera trådar som kör koden måste använda samma objektinstans (antingen genom att göra det till ett static fält eller genom att använda samma klassinstans för båda trådarna)

Från kompilatorsidan är låsnyckelordet ett syntaktiskt socker som ersätts av Monitor.Enter(_lockObj); och Monitor.Exit(_lockObj); . Så om du byter ut låset genom att omge kodblocket med dessa två metoder, skulle du få samma resultat. Du kan se faktisk kod i syntaktiskt socker i C # - låsexempel

null

En variabel av en referenstyp kan innehålla antingen en giltig referens till en instans eller en nollreferens. Nollreferensen är standardvärdet för variabler av referenstypen, liksom nollbara värdetyper.

null är nyckelordet som representerar en nollreferens.

Som ett uttryck kan det användas för att tilldela nollreferensen till variabler av nämnda typer:

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

Icke-nollbara värdetyper kan inte tilldelas en nollreferens. Alla följande uppdrag är ogiltiga:

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

Nollreferensen bör inte förväxlas med giltiga instanser av olika typer som:

  • en tom lista ( new List<int>() )
  • en tom sträng ( "" )
  • siffran noll ( 0 , 0f , 0m )
  • nolltecknet ( '\0' )

Ibland är det meningsfullt att kontrollera om något är noll eller ett tomt / standardobjekt. System.String.IsNullOrEmpty (String) -metoden kan användas för att kontrollera detta, eller så kan du implementera din egen motsvarande metod.

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

inre

Det internal nyckelordet är en åtkomstmodifierare för typer och medlemmar. Interna typer eller medlemmar är endast tillgängliga inom filer i samma enhet

användande:

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

Skillnaden mellan olika åtkomstmodifierare klargörs här

Tillgång modifierare

offentlig

Typen eller medlemmet kan nås med vilken annan kod som helst i samma enhet eller annan enhet som refererar till den.

privat

Typen eller medlemmen kan endast nås med kod i samma klass eller struktur.

skyddade

Typen eller medlemmen kan endast nås med kod i samma klass eller struktur, eller i en härledd klass.

inre

Typen eller medlemmen kan nås med vilken kod som helst i samma enhet, men inte från en annan enhet.

skyddad intern

Typen eller medlemmet kan nås med vilken kod som helst i samma enhet eller av någon härledd klass i en annan enhet.

När ingen åtkomstmodifierare är inställd används en standardåtkomstmodifierare. Så det finns alltid någon form av åtkomstmodifierare även om den inte är inställd.

var

where kan tjäna två syften i C #: typ begränsa i ett generiskt argument och filtrera LINQ frågor.

Låt oss överväga i en generisk klass

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

T kallas en typparameter. Klassdefinitionen kan införa begränsningar för de faktiska typerna som kan levereras för T.

Följande typer av begränsningar kan tillämpas:

  • värde typ
  • referens typ
  • standardkonstruktör
  • arv och genomförande

värde typ

I detta fall kan endast struct (detta inkluderar "primitiva" datatyper som int , boolean etc) tillhandahållas

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

referens typ

I detta fall kan endast klasstyper levereras

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

hybridvärde / referenstyp

Ibland är det önskvärt att begränsa typargument till de som finns tillgängliga i en databas, och dessa kartläggs vanligtvis till värdetyper och strängar. Eftersom alla typbegränsningar måste uppfyllas är det inte möjligt att ange where T : struct or string (detta är inte giltig syntax). En lösning är att begränsa IConvertible till IConvertible som har inbyggda typer av "... Boolean, SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Decimal, DateTime, Char och String. " Det är möjligt att andra objekt kommer att implementera IConvertible, men detta är sällsynt i praktiken.

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

standardkonstruktör

Endast typer som innehåller en standardkonstruktör tillåts. Detta inkluderar värdetyper och klasser som innehåller en standard (parameterlös) konstruktör

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

arv och genomförande

Endast typer som ärver från en viss basklass eller implementerar ett visst gränssnitt kan tillhandahållas.

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


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

Begränsningen kan till och med hänvisa till en annan typparameter:

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

Flera begränsningar kan anges för ett typargument:

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

De tidigare exemplen visar generiska begränsningar för en klassdefinition, men begränsningar kan användas var som helst som ett typargument tillhandahålls: klasser, strukturer, gränssnitt, metoder etc.

where kan vara en LINQ-klausul. I det här fallet är det analogt med WHERE i 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

Det extern nyckelordet används för att deklarera metoder som implementeras externt. Detta kan användas i samband med DllImport-attributet för att ringa in i onhanterad kod med Interop-tjänster. vilket i detta fall kommer med static modifierare

Till exempel:

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

Detta använder SetForegroundWindow-metoden som importeras från User32.dll-biblioteket

Detta kan också användas för att definiera ett externt monteringsalias. som låter oss hänvisa till olika versioner av samma komponenter från en enda enhet.

För att referera till två enheter med samma fullständiga kvalificerade typnamn, måste ett alias anges vid en kommandotolk, enligt följande:

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

Detta skapar de externa aliasna GridV1 och GridV2. Om du vill använda dessa alias från ett program ska du hänvisa till dem genom att använda det externa nyckelordet. Till exempel:

extern alias GridV1;
extern alias GridV2;

bool

Nyckelord för att lagra de booleska värdena true och false . bool är ett alias av System.Boolean.

Standardvärdet för en bool är falskt.

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

För att en bool ska tillåta nollvärden måste den initialiseras som en bool ?.

Standardvärdet för en bool? är inget.

bool? a // default value is null

när

when läggs till ett nyckelord i C # 6 , och det används för undantagsfiltrering.

Före införandet av when sökord skulle du ha haft en fångst klausul för varje typ av undantag, med tillägget av nyckelordet är en mer finkornig kontroll nu möjlig.

En when uttrycket är ansluten till en catch gren, och endast om when villkoret är true , det catch kommer klausulen att utföras. Det är möjligt att ha flera catch klausuler med samma typer undantag klass och olika when förhållanden.

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

okontrollerat

Det unchecked nyckelordet förhindrar kompilatorn från att kontrollera för överflöden / underflöden.

Till exempel:

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

Utan det unchecked sökordet kommer ingen av de två tilläggsoperationerna att kompilera.

När är detta användbart?

Detta är användbart eftersom det kan hjälpa till att påskynda beräkningar som definitivt inte kommer att flöda eftersom kontrollen för översvämning tar tid, eller när ett överflöde / underflöde är önskat beteende (till exempel när du genererar en hash-kod).

tomhet

Det reserverade ordet "void" är ett alias av System.Void typ och har två användningsområden:

  1. Förklara en metod som inte har ett returvärde:
public void DoSomething()
{
    // Do some work, don't return any value to the caller.
}

En metod med en avkastning typ av tomrum kan ha fortfarande return sökord i sin kropp. Detta är användbart när du vill avsluta metodens körning och returnera flödet till den som ringer:

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

    if (condition)
        return;

    // Do some more work if the condition evaluated to false.
}
  1. Förklara en pekare till en okänd typ i ett osäkert sammanhang.

I ett osäkert sammanhang kan en typ vara en pekartyp, en värdetyp eller en referenstyp. En pekartypdeklaration är vanligtvis type* identifier , där typen är en känd typ - dvs int* myInt , men kan också vara void* identifier , där typen är okänd.

Observera att deklarering av en ogiltig pekartyp avskräcks av Microsoft.

om, om ... annat, om ... annat om


if satsen används för att kontrollera flödet i programmet. Ett if uttalande identifierar vilket uttalande som ska köras baserat på värdet på ett Boolean uttryck.

För ett enda uttalande är braces {} valfria men rekommenderas.

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

if kan också ha en else klausul, som kommer att köras om villkoret utvärderas till felaktigt:

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"

if ... else if konstruktion kan du ange flera villkor:

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"

Viktigt att notera att om ett villkor är uppfyllt i exemplet ovan, hoppar styr andra tester och hoppar till slutet av den särskilt om annat construct.So är ordningen på testerna viktigt om du använder om .. annars om konstruktion

C # Booleska uttryck använder kortslutningsutvärdering . Detta är viktigt i fall där utvärdering av tillstånd kan ha biverkningar:

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

Det finns ingen garanti för att someOtherBooleanMethodWithSideEffects faktiskt kommer att köras.

Det är också viktigt i fall där tidigare förhållanden säkerställer att det är "säkert" att utvärdera senare. Till exempel:

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

Ordern är mycket viktig i det här fallet, om vi omvända ordningen:

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

det kommer att kasta en NullReferenceException om someCollection är null .

do

Do-operatören itererar över ett kodblock tills en villkorlig fråga är lika falsk. Do-while-slingan kan också avbrytas av ett uttalande om goto , return , break eller throw .

Syntaxen för do sökordet är:

gör { kodblock; } medan ( villkor );

Exempel:

int i = 0;

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

Produktion:

"Do är på slinga nummer 1."
"Do är på slinga nummer 2."
"Do är på slinga nummer 3."
"Do är på slinga nummer 4."
"Do är på slinga nummer 5."

Till skillnad från while loopen är do-while-loopen Exit Controlled . Detta innebär att do-while-loopen kommer att utföra sina uttalanden minst en gång, även om villkoret misslyckas första gången.

bool a = false;

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

operatör

De flesta av de inbyggda operatörerna (inklusive konverteringsoperatörer) kan överbelastas genom att använda operator nyckelord tillsammans med de public och static modifierarna.

Operatörerna finns i tre former: unära operatörer, binära operatörer och konverteringsoperatörer.

Unary och binära operatörer kräver minst en parameter av samma typ som den innehållande typen, och vissa kräver en kompletterande matchande operatör.

Konverteringsoperatörer måste konvertera till eller från den bifogade typen.

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

}

Exempel

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

En struct är en värdetyp som vanligtvis används för att kapsla in små grupper av relaterade variabler, till exempel koordinaterna för en rektangel eller egenskaperna hos ett objekt i en inventering.

Klasser är referenstyper, strukturer är värdetyper.

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

Strukturer kan också innehålla konstruktörer, konstanter, fält, metoder, egenskaper, indexerare, operatörer, händelser och kapslade typer, även om flera sådana medlemmar krävs, bör du överväga att göra din typ till klass istället.


Några förslag från MS om när struktur ska användas och när klassen ska användas:

ÖVERVÄGA

definiera en struktur istället för en klass om instanser av typen är små och vanligtvis kortlivade eller vanligtvis inbäddade i andra objekt.

UNDVIKA

definiera en struktur såvida inte typen har alla följande egenskaper:

  • Det representerar logiskt ett enda värde, liknande primitiva typer (int, dubbel, etc.)
  • Den har en instansstorlek under 16 byte.
  • Det är oföränderligt.
  • Det behöver inte boxas ofta.

växla

switch uttalandet är ett kontrollmeddelande som väljer ett switch-avsnitt att köra från en kandidatlista. Ett switch-uttalande innehåller en eller flera switch-sektioner. Varje switch avsnitt innehåller en eller flera case etiketter följt av en eller flera uttalanden. Om ingen falletikett innehåller ett matchande värde överförs kontrollen till default , om det finns ett. Fallnedfall stöds inte i C #, strikt talat. Men om 1 eller flera case är tomma, kommer exekveringen att följa koden för nästa case block som innehåller kod. Detta gör det möjligt att gruppera flera case etiketter med samma tillämpning. I följande exempel, om month är lika 12 koden i case 2 kommer att utföras eftersom case etiketter 12 1 och 2 är grupperade. Om ett case blocket inte är tom, en break måste finnas innan nästa case etiketten, annars kompilatorn flaggar ett fel.

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

Ett case kan endast märkas genom ett värde vid kompileringen (t.ex. 1 , "str" , Enum.A ), så en variable inte är en giltig case etikett, men en const eller en Enum -värdet är (samt varje bokstavligt värde).

gränssnitt

Ett interface innehåller signaturer av metoder, egenskaper och händelser. De härledda klasserna definierar medlemmarna eftersom gränssnittet endast innehåller medlemmarnas deklaration.

Ett gränssnitt deklareras med hjälp av interface .

interface IProduct
{
    decimal Price { get; }
}

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

osäker

Det unsafe nyckelordet kan användas i typ- eller metoddeklarationer eller för att förklara ett inline block.

Syftet med detta sökord är att möjliggöra användning av den osäkra undergruppen C # för det aktuella blocket. Den osäkra delmängden innehåller funktioner som pekare, tilldelning av staplar, C-liknande matriser och så vidare.

Osäker kod är inte verifierbar och det är därför användningen avskräcker. Sammanställning av osäker kod kräver att en omkopplare skickas till C # -kompilatorn. Dessutom kräver CLR att den löpande enheten har fullt förtroende.

Trots dessa begränsningar har osäker kod giltiga användningsområden för att göra vissa operationer mer utförande (t.ex. arrayindexering) eller enklare (t.ex. interop med vissa ostyrda bibliotek).

Som ett mycket enkelt exempel

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

När vi arbetar med pekare kan vi ändra värdena på minnesplatser direkt, snarare än att behöva adressera dem med namn. Observera att detta ofta kräver användning av det fasta nyckelordet för att förhindra eventuell minneskada eftersom sopor samlaren rör sig runt (annars kan du få fel CS0212 ). Eftersom en variabel som har "fixats" inte kan skrivas till, måste vi ofta ha en andra pekare som börjar peka på samma plats som den första.

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

Produktion:

1
4
9
16
25
36
49
64
81
100

unsafe tillåter också användning av stackalloc som kommer att fördela minne på stacken som _alloca i C-run-time-biblioteket. Vi kan modifiera exemplet ovan för att använda stackalloc enligt följande:

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 är samma som ovan)

implicit

Det implicit nyckelordet används för att överbelasta en konverteringsoperatör. Till exempel kan du förklara en Fraction som automatiskt ska konverteras till en double vid behov, och som automatiskt kan konverteras från 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);
    }
}

sant falskt

De true och false nyckelorden har två användningsområden:

  1. Som bokstavliga booleska värden
var myTrueBool = true;
var myFalseBool = false;
  1. Som operatörer som kan överbelastas
public static bool operator true(MyClass x)
{
    return x.value >= 0;
}

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

Överbelastning av den falska operatören var användbar före C # 2.0, innan introduktionen av Nullable typer.
En typ som överbelaster den true operatören måste också överbelasta den false operatören.

sträng

string är ett alias till .NET-datatypen System.String , som gör det möjligt att System.String text (sekvenser med tecken).

Notation:

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

Varje tecken i strängen är kodad i UTF-16, vilket innebär att varje tecken kräver minst 2 byte lagringsutrymme.

USHORT

En numerisk typ som används för att lagra 16-bitars positiva heltal. ushort är ett alias för System.UInt16 och tar upp 2 byte minne.

Giltigt intervall är 0 till 65535 .

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

sbyte

En numerisk typ som används för att lagra 8-bitars signerade heltal. sbyte är ett alias för System.SByte och tar upp 1 byte minne. För den osignerade motsvarigheten, använd byte .

Giltigt intervall är -127 till 127 (resterna används för att lagra skylten).

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

var

En implicit typad lokal variabel som är starkt typ precis som om användaren hade förklarat typen. Till skillnad från andra variabla deklarationer bestämmer kompilatorn vilken typ av variabel som detta representerar baserat på det värde som tilldelas den.

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

Till skillnad från andra typer av variabler måste variabla definitioner med detta sökord initialiseras när de deklareras. Detta beror på var nyckelordet representerar en implicit-skrivit variabel.

var i;
i = 10;

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

Det var nyckelordet kan också användas för att skapa nya datatyper i farten. Dessa nya datatyper är kända som anonyma typer . De är ganska användbara, eftersom de tillåter en användare att definiera en uppsättning egenskaper utan att behöva uttryckligen förklara någon form av objekttyp först.

Vanlig anonym typ

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

LINQ-fråga som returnerar en anonym typ

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

Du kan använda var nyckelord i förhandssatsning

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

    return false;
}

delegera

Delegater är typer som representerar en hänvisning till en metod. De används för att överföra metoder som argument till andra metoder.

Delegater kan innehålla statiska metoder, instansmetoder, anonyma metoder eller lambda-uttryck.

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

Vid tilldelning av en metod till en delegat är det viktigt att notera att metoden måste ha samma returtyp och parametrar. Detta skiljer sig från 'normal' metodöverbelastning, där bara parametrarna definierar metodens signatur.

Händelser byggs ovanpå delegaterna.

händelse

En event gör det möjligt för utvecklaren att implementera ett meddelandemönster.

Enkelt exempel

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

partiell

Nyckelordet partial kan användas under typdefinition av klass, struktur eller gränssnitt för att låta typdefinitionen delas upp i flera filer. Detta är användbart för att integrera nya funktioner i automatisk genererad kod.

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

Obs: En klass kan delas upp i valfritt antal filer. Alla deklarationer måste dock vara under samma namnutrymme och samma församling.

Metoder kan också förklaras delvis med hjälp av det partial nyckelordet. I det här fallet innehåller en fil endast metoddefinitionen och en annan fil kommer att innehålla implementeringen.

En partiell metod har sin signatur definierad i en del av en partiell typ, och dess implementering definierad i en annan del av typen. Partiella metoder gör det möjligt för klassdesigners att tillhandahålla metodkrokar, liknande evenemangshanterare, som utvecklare kan besluta att implementera eller inte. Om utvecklaren inte tillhandahåller en implementering tar kompilatorn bort signaturen vid kompileringstillfället. Följande villkor gäller för partiella metoder:

  • Signaturerna i båda delarna av den partiella typen måste matcha.
  • Metoden måste återgå till ogiltig.
  • Inga åtkomstmodifierare är tillåtna. Partiella metoder är implicit privat.

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

Obs: Typen som innehåller den partiella metoden måste också förklaras delvis.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow