C# Language
Nyckelord
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 @
.
-
abstract
-
as
-
base
-
bool
-
break
-
byte
-
case
-
catch
-
char
-
checked
-
class
-
const
-
continue
-
decimal
-
default
-
delegate
-
do
-
double
-
else
-
enum
-
event
-
explicit
-
extern
-
false
-
finally
-
fixed
-
float
-
for
-
foreach
-
goto
-
if
-
implicit
-
in
-
int
-
interface
-
internal
-
is
-
lock
-
long
-
namespace
-
new
-
null
-
object
-
operator
-
out
-
override
-
params
-
private
-
protected
-
public
-
readonly
-
ref
-
return
-
sbyte
-
sealed
-
short
-
sizeof
-
stackalloc
-
static
-
string
-
struct
-
switch
-
this
-
throw
-
true
-
try
-
typeof
-
uint
-
ulong
-
unchecked
-
unsafe
-
ushort
-
using
(direktiv) -
using
(uttalande) -
virtual
-
void
-
volatile
-
when
-
while
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() {volatileint 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
ochbool
- enums typer baserade på
byte
,sbyte
,short
,ushort
,int
elleruint
-
IntPtr
ochUIntPtr
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 somInterlocked.Read
ochInterlocked.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:
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
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;
}
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
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
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."
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
:
använder uttalande :
Det
using
nyckelordet säkerställer att objekt som implementerar detIDisposable
gränssnittet bortskaffas korrekt efter användning. Det finns ett separat ämne för det använda uttalandetmed 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örBaseClass
- Om objektet deklareras av typen
DerivedClass
metoden förDerivedClass
.
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);
}
I C # 5.0 kan inte await
användas i catch
och finally
.
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.
-
char c = 'c';
-
char c = '\u0063'; //Unicode
-
char c = '\x0063'; //Hex
-
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:
- 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.
}
- 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:
- Som bokstavliga booleska värden
var myTrueBool = true;
var myFalseBool = false;
- 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
}
}
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.