Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSharpNotesForProfessionals.pdf
Скачиваний:
57
Добавлен:
20.05.2023
Размер:
6.12 Mб
Скачать

can be rewritten as:

Version ≥ 7.0

if(o is string s)

{

//Do something with s

};

Also note that the scope of the pattern variable s is extended to outside the if block reaching the end of the enclosing scope, example:

if(someCondition)

{

if(o is string s)

{

//Do something with s

}

else

{

// s is unassigned here, but accessible

}

// s is unassigned here, but accessible

}

// s is not accessible here

Section 71.5: Digit separators

The underscore _ may be used as a digit separator. Being able to group digits in large numeric literals has a significant impact on readability.

The underscore may occur anywhere in a numeric literal except as noted below. Di erent groupings may make sense in di erent scenarios or with di erent numeric bases.

Any sequence of digits may be separated by one or more underscores. The _ is allowed in decimals as well as exponents. The separators have no semantic impact - they are simply ignored.

int bin =

0b1001_1010_0001_0100;

int hex =

0x1b_a0_44_fe;

int

dec =

33_554_432;

int

weird

=

1_2__3___4____5_____6______7_______8________9;

double real

= 1_000.111_1e-1_000;

 

 

 

 

Where the _ digit separator may not be used:

at the beginning of the value (_121)

at the end of the value (121_ or 121.05_)

next to the decimal (10_.0)

next to the exponent character (1.1e_1) next to the type specifier (10_f)

immediately following the 0x or 0b in binary and hexadecimal literals (might be changed to allow e.g. 0b_1001_1000)

Section 71.6: Binary literals

The 0b prefix can be used to represent Binary literals.

GoalKicker.com – C# Notes for Professionals

407

Binary literals allow constructing numbers from zeroes and ones, which makes seeing which bits are set in the binary representation of a number much easier. This can be useful for working with binary flags.

The following are equivalent ways of specifying an int with value 34 (=25 + 21):

//Using a binary literal:

//bits: 76543210

int a1 = 0b00100010; // binary: explicitly specify bits

// Existing methods:

 

 

int a2

= 0x22;

// hexadecimal: every digit corresponds to 4 bits

int

a3

=

34;

//

decimal: hard to visualise which bits are set

int

a4

=

(1 << 5) | (1 << 1); //

bitwise arithmetic: combining non-zero bits

Flags enumerations

Before, specifying flag values for an enum could only be done using one of the three methods in this example:

[Flags]

 

 

 

 

 

 

public enum DaysOfWeek

 

 

 

 

{

 

 

 

 

 

 

// Previously available methods:

 

 

//

 

decimal

 

hex

bit shifting

Monday

=

1,

//

= 0x01

= 1 << 0

Tuesday

=

2,

//

= 0x02

= 1 << 1

Wednesday

=

4,

//

= 0x04

= 1 << 2

Thursday

=

8,

//

= 0x08

= 1

<< 3

Friday

= 16,

//

= 0x10

= 1

<< 4

Saturday

= 32,

//

= 0x20

= 1

<< 5

Sunday

= 64,

//

= 0x40

= 1

<< 6

Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday, Weekends = Saturday | Sunday

}

With binary literals it is more obvious which bits are set, and using them does not require understanding hexadecimal numbers and bitwise arithmetic:

[Flags]

public enum DaysOfWeek

{

Monday

= 0b00000001,

Tuesday

= 0b00000010,

Wednesday = 0b00000100,

Thursday

= 0b00001000,

Friday

= 0b00010000,

Saturday

= 0b00100000,

Sunday

= 0b01000000,

Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday, Weekends = Saturday | Sunday

}

Section 71.7: throw expressions

C# 7.0 allows throwing as an expression in certain places:

class Person

{

GoalKicker.com – C# Notes for Professionals

408

public string Name { get; }

public Person(string name) => Name = name ?? throw new ArgumentNullException(nameof(name));

public string GetFirstName()

{

var parts = Name.Split(' ');

return (parts.Length > 0) ? parts[0] : throw new InvalidOperationException("No name!");

}

public string GetLastName() => throw new NotImplementedException();

}

Prior to C# 7.0, if you wanted to throw an exception from an expression body you would have to:

var spoons = "dinner,desert,soup".Split(',');

var spoonsArray = spoons.Length > 0 ? spoons : null;

if (spoonsArray == null)

{

throw new Exception("There are no spoons");

}

Or

var spoonsArray = spoons.Length > 0 ? spoons

:new Func<string[]>(() =>

{

throw new Exception("There are no spoons"); })();

In C# 7.0 the above is now simplified to:

var spoonsArray = spoons.Length > 0 ? spoons : throw new Exception("There are no spoons");

Section 71.8: Extended expression bodied members list

C# 7.0 adds accessors, constructors and finalizers to the list of things that can have expression bodies:

class Person

{

private static ConcurrentDictionary<int, string> names = new ConcurrentDictionary<int, string>();

private int id = GetId();

public Person(string name) => names.TryAdd(id, name); // constructors

~Person() => names.TryRemove(id, out _);

// finalizers

public string Name

 

{

 

get => names[id];

// getters

set => names[id] = value;

// setters

}

 

}

GoalKicker.com – C# Notes for Professionals

409

Also see the out var declaration section for the discard operator.

Section 71.9: ref return and ref local

Ref returns and ref locals are useful for manipulating and returning references to blocks of memory instead of copying memory without resorting to unsafe pointers.

Ref Return

public static ref TValue Choose<TValue>(

Func<bool> condition, ref TValue left, ref TValue right)

{

return condition() ? ref left : ref right;

}

With this you can pass two values by reference with one of them being returned based on some condition:

Matrix3D left = …, right = ;

Choose(chooser, ref left, ref right).M20 = 1.0;

Ref Local

public static ref int Max(ref int first, ref int second, ref int third)

{

ref int max = first > second ? ref first : ref second; return max > third ? ref max : ref third;

}

int a = 1, b = 2, c = 3; Max(ref a, ref b, ref c) = 4; Debug.Assert(a == 1); // true Debug.Assert(b == 2); // true Debug.Assert(c == 4); // true

Unsafe Ref Operations

In System.Runtime.CompilerServices.Unsafe a set of unsafe operations have been defined that allow you to

manipulate ref values as if they were pointers, basically.

For example, reinterpreting a memory address (ref) as a di erent type:

byte[] b = new byte[4] { 0x42, 0x42, 0x42, 0x42 };

ref int r = ref Unsafe.As<byte, int>(ref b[0]); Assert.Equal(0x42424242, r);

0x0EF00EF0;

Assert.Equal(0xFE, b[0] | b[1] | b[2] | b[3]);

Beware of endianness when doing this, though, e.g. check BitConverter.IsLittleEndian if needed and handle accordingly.

Or iterate over an array in an unsafe manner:

int[] a = new int[] { 0x123, 0x234, 0x345, 0x456 };

ref int r1 = ref Unsafe.Add(ref a[0], 1); Assert.Equal(0x234, r1);

ref int r2 = ref Unsafe.Add(ref r1, 2);

GoalKicker.com – C# Notes for Professionals

410