В последнее время экосистема .NET развивается очень динамично: постоянно появляются новые технологии и инструменты, а старые обзаводятся новыми возможностями. Уследить за всем очень сложно, поэтому в этом докладе мы постараемся обзорно взглянуть на текущее состояние платформы .NET, а также на то, что нас ждёт в ближайшем будущем. Будем говорить про грядущий C#7, про кроссплатформенность и нативную компиляцию, про новый .NET Core 5 и ASP.NET 5, про новые инструменты для разработчиков и последние анонсы от Microsoft.
20. .NET Platform Standard
Terms
.NET Platform Standard — a specific versioned set of
reference assemblies that all .NET Platforms must support as
defined in the CoreFX repo.
Principles
• Platform owners implement reference assemblies from a
particular .NET Platform Standard version.
• Platform owners may implement a subset of reference
assemblies from a particular .NET Platform Standard
version.
• Any change in a reference assembly’s API surface causes
the .NET Platform Standard to version.
• Lower versions are always compatible with higher
versions.
20/53 .NET Platform Standard
28. Tuples (Background)
public void Tally(IEnumerable<int> values, out int sum, out int count)
int s, c;
Tally(myValues, out s, out c);
Console.WriteLine($"Sum: {s}, count: {c}");
27/53 C#7
29. Tuples (Background)
public void Tally(IEnumerable<int> values, out int sum, out int count)
int s, c;
Tally(myValues, out s, out c);
Console.WriteLine($"Sum: {s}, count: {c}");
public Tuple<int, int> Tally(IEnumerable<int> values)
var t = Tally(myValues);
Console.WriteLine($"Sum: {t.Item1}, count: {t.Item2}")
27/53 C#7
30. Tuples (Background)
public void Tally(IEnumerable<int> values, out int sum, out int count)
int s, c;
Tally(myValues, out s, out c);
Console.WriteLine($"Sum: {s}, count: {c}");
public Tuple<int, int> Tally(IEnumerable<int> values)
var t = Tally(myValues);
Console.WriteLine($"Sum: {t.Item1}, count: {t.Item2}")
public struct TallyResult { public int Sum; public int Count; }
public TallyResult Tally(IEnumerable<int> values)
var t = Tally(myValues);
Console.WriteLine($"Sum: {t.Sum}, count: {t.Count}");
27/53 C#7
31. Tuples types
public (int sum, int count) Tally(IEnumerable<int> values)
var t = Tally(myValues);
Console.WriteLine($"Sum: {t.sum}, count: {t.count}");
28/53 C#7
32. Tuples types
public (int sum, int count) Tally(IEnumerable<int> values)
var t = Tally(myValues);
Console.WriteLine($"Sum: {t.sum}, count: {t.count}");
public async Task<(int sum, int count)>
TallyAsync(IEnumerable<int> values)
var t = await TallyAsync(myValues);
Console.WriteLine($"Sum: {t.sum}, count: {t.count}");
28/53 C#7
33. Tuple literals and deconstruction
var t = new (int sum, int count) { sum = 0, count = 0 };
29/53 C#7
34. Tuple literals and deconstruction
var t = new (int sum, int count) { sum = 0, count = 0 };
public (int sum, int count) Tally(IEnumerable<int> values)
{
var s = 0; var c = 0;
foreach (var value in values) { s += value; c++; }
return (s, c); // target typed to (int sum, int count)
}
29/53 C#7
35. Tuple literals and deconstruction
var t = new (int sum, int count) { sum = 0, count = 0 };
public (int sum, int count) Tally(IEnumerable<int> values)
{
var s = 0; var c = 0;
foreach (var value in values) { s += value; c++; }
return (s, c); // target typed to (int sum, int count)
}
public (int sum, int count) Tally(IEnumerable<int> values)
{
// infer tuple type from names and values
var res = (sum: 0, count: 0);
foreach (var value in values) { res.sum += value; res.count++; }
return res;
}
29/53 C#7
36. Tuple literals and deconstruction
var t = new (int sum, int count) { sum = 0, count = 0 };
public (int sum, int count) Tally(IEnumerable<int> values)
{
var s = 0; var c = 0;
foreach (var value in values) { s += value; c++; }
return (s, c); // target typed to (int sum, int count)
}
public (int sum, int count) Tally(IEnumerable<int> values)
{
// infer tuple type from names and values
var res = (sum: 0, count: 0);
foreach (var value in values) { res.sum += value; res.count++; }
return res;
}
(var sum, var count) = Tally(myValues); // deconstruct result
Console.WriteLine($"Sum: {sum}, count: {count}");
29/53 C#7
37. Tuples: проблемы
• Struct or class
• Mutability
• Tuples as fields
• Conversions
• . . .
Cм. также: roslyn#347
30/53 C#7
38. Pattern matching (Background)
// class Person(string Name);
class Person
{
public Person(string name) { this.Name = name; }
public string Name { get; }
}
// class Student(string Name, double Gpa) : Person(Name);
class Student : Person
{
public Student(string name, double gpa) : base(name)
{ this.Gpa = gpa; }
public double Gpa { get; }
}
// class Teacher(string Name, string Subject) : Person(Name);
class Teacher : Person
{
public Teacher(string name, string subject) : base(name)
{ this.Subject = subject; }
public string Subject { get; }
}
31/53 C#7
39. Pattern matching (Background)
static string PrintedForm(Person p)
{
Student s;
Teacher t;
if ((s = p as Student) != null && s.Gpa > 3.5)
{
return $"Honor Student {s.Name} ({s.Gpa})";
}
else if (s != null)
{
return $"Student {s.Name} ({s.Gpa})";
}
else if ((t = p as Teacher) != null)
{
return $"Teacher {t.Name} of {t.Subject}";
}
else
{
return $"Person {p.Name}";
}
}
32/53 C#7
40. Pattern matching (Background)
static void Main(string[] args)
{
Person[] oa = {
new Student("Einstein", 4.0),
new Student("Elvis", 3.0),
new Student("Poindexter", 3.2),
new Teacher("Feynmann", "Physics"),
new Person("Anders"),
};
foreach (var o in oa)
{
Console.WriteLine(PrintedForm(o));
}
Console.ReadKey();
}
33/53 C#7
41. Pattern matching: is
static string PrintedForm(Person p)
{
if (p is Student s && s.Gpa > 3.5)
{
return $"Honor Student {s.Name} ({s.Gpa})";
}
else if (p is Student s)
{
return $"Student {s.Name} ({s.Gpa})";
}
else if (p is Teacher t)
{
return $"Teacher {t.Name} of {t.Subject}";
}
else
{
return $"Person {p.Name}";
}
}
34/53 C#7
42. Pattern matching: switch
static string PrintedForm(Person p)
{
switch (p)
{
case Student s when s.Gpa > 3.5 :
return $"Honor Student {s.Name} ({s.Gpa})";
case Student s :
return $"Student {s.Name} ({s.Gpa})";
case Teacher t :
return $"Teacher {t.Name} of {t.Subject}";
default :
return $"Person {p.Name}";
}
}
35/53 C#7
43. Pattern matching: match
static string PrintedForm(Person p)
{
return p match (
case Student s when s.Gpa > 3.5 :
$"Honor Student {s.Name} ({s.Gpa})"
case Student s :
$"Student {s.Name} ({s.Gpa})"
case Teacher t :
$"Teacher {t.Name} of {t.Subject}"
case * :
$"Person {p.Name}"
);
}
36/53 C#7
44. Pattern matching: Exceptions
return p match (
case Student s when s.Gpa > 3.5 :
$"Honor Student {s.Name} ({s.Gpa})"
case Student s :
$"Student {s.Name} ({s.Gpa})"
case Teacher t :
$"Teacher {t.Name} of {t.Subject}"
case null :
throw new ArgumentNullException(nameof(p))
case * :
$"Person {p.Name}"
);
37/53 C#7
45. Pattern matching: members
return p match (
case Student s when s.Gpa > 3.5 :
$"Honor Student {s.Name} ({s.Gpa})"
case Student { Name is "Poindexter" } :
"A Nerd"
case Student s :
$"Student {s.Name} ({s.Gpa})"
case Teacher t :
$"Teacher {t.Name} of {t.Subject}"
case null :
throw new ArgumentNullException(nameof(p))
case * :
$"Person {p.Name}"
);
38/53 C#7
46. Pattern matching: =>
static string PrintedForm(Person p) => p match (
case Student s when s.Gpa > 3.5 :
$"Honor Student {s.Name} ({s.Gpa})"
case Student { Name is "Poindexter" } :
"A Nerd"
case Student s :
$"Student {s.Name} ({s.Gpa})"
case Teacher t :
$"Teacher {t.Name} of {t.Subject}"
case null :
throw new ArgumentNullException(nameof(p))
case * :
$"Person {p.Name}"
);
39/53 C#7
47. Readonly locals
readonly int foo = /* ... */ ;
readonly var foo = /* ... */ ;
val foo = /* ... */ ;
public void Bar(readonly int foo = 0)
public void Bar(readonly ref Matrix3D matrix)
40/53 C#7
48. Immutable Types
public immutable struct Tuple<T1, T2>
{
public Tuple(T1 item1, T2 item2)
{ Item1 = item1; Item2 = item2; }
public T1 Item1; // Implicitly readonly
public T2 Item2; // Implicitly readonly
}
41/53 C#7
49. Immutable Types
public immutable struct Tuple<T1, T2>
{
public Tuple(T1 item1, T2 item2)
{ Item1 = item1; Item2 = item2; }
public T1 Item1; // Implicitly readonly
public T2 Item2; // Implicitly readonly
}
public immutable struct ImmutableTuple<T1, T2>
(T1 item1, T2 item2)
where T1 : immutable
where T2 : immutable
{
public ImmutableTuple(T1 item1, T2 item2)
{ Item1 = item1; Item2 = item2; }
public T1 Item1;
public T2 Item2;
}
41/53 C#7
52. Nullability?!
1 Добавляем в язык только ? и ! при
объявления и кастах
2 NullReferenceException невозможен
42/53 C#7
53. Nullability?!
1 Добавляем в язык только ? и ! при
объявления и кастах
2 NullReferenceException невозможен
3 Процесс компиляции не меняется
42/53 C#7
54. Nullability?!
1 Добавляем в язык только ? и ! при
объявления и кастах
2 NullReferenceException невозможен
3 Процесс компиляции не меняется
4 Runtime ничего не знает про not-nullable
42/53 C#7
55. Nullability?!
1 Добавляем в язык только ? и ! при
объявления и кастах
2 NullReferenceException невозможен
3 Процесс компиляции не меняется
4 Runtime ничего не знает про not-nullable
5 Обратная совместимость
42/53 C#7
56. С#7: много разных идей
• Local functions
• Extensions properties
• AsyncEnumerable
• params IEnumerable
• digits separators
• binary literals
• ...
43/53 C#7
58. ILSub
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[ILSub(@"
.maxstack 2
.locals ([0] uint8& addr)
ldarg.0 // load the object
stloc.0 // convert the object pointer to a byref
ldloc.0 // load the object pointer as a byref
ldarg.1 // load the offset
add // add the offset
ldobj !!T // load a T value from the computed address
ret")]
public static T Get<T>(object obj, UIntPtr offset)
{
return default(T);
}
45/53 corefxlab
59. System.Buffers
public sealed class ManagedBufferPool<T> where T : struct
{
// 2MB taken as the default since the average HTTP page
// is 1.9MB per http://httparchive.org/interesting.php
public ManagedBufferPool(int maxBufferSizeInBytes = 2048000)
static ManagedBufferPool<byte> SharedByteBufferPool{get;}
T[] RentBuffer(int size, bool clearBuffer = false)
void EnlargeBuffer(ref T[] buffer, int newSize,
bool clearFreeSpace = false)
void ReturnBuffer(ref T[] buffer, bool clearBuffer = false)
}
46/53 corefxlab
60. System.Slices
public partial struct Span<T> : IEnumerable<T>, IEquatable<Span<T>>
{
readonly object _object;
readonly UIntPtr _offset;
public readonly int Length;
// ...
Span(T[] array, int start, int length)
unsafe Span(void* ptr, int length)
bool TryCopyTo(Span<T> dest)
Span<T> Slice(int start, int length)
}
// Extensions
static Span<T> Slice<T>(this T[] array, int start, int length)
static Span<char> Slice(this string str, int start, int length)
static Span<U> Cast<[Primitive]T, [Primitive]U>(this Span<T> slice)
static bool SequenceEqual<T>(this Span<T> first, Span<T> second)
// ...
47/53 corefxlab
62. MemCmp
/// <summary>
/// platform independent fast memory comparison
/// for x64 it is as fast as memcmp of msvcrt.dll,
/// for x86 it is up to two times faster!!
/// </summary>
internal static bool MemCmp<[Primitive]T>
(Span<T> first, Span<T> second)
where T : struct
{
unsafe
{
// ...
49/53 corefxlab
63. Array slicing syntax (roslyn#120)
int[] primes = new int[] { 2, 3, 5, 7, 9, 11, 13 };
int item = primes[1]; // Regular array access (value 3)
int[:] a = primes[0:3]; // A slice {2, 3, 5}
int[:] b = primes[1:2]; // A slice {3}
int[:] c = primes[:5]; // A slice {2, 3, 5, 7, 9}
int[:] d = primes[2:]; // A slice {5, 7, 9, 11, 13}
int[:] e = primes[:]; // A slice {2, 3, 5, 7, 9, 11, 13}
int[:] f = a[1:2]; // A slice {3}
int[:] g = primes[:]; // A slice {2, 3, 5, 7, 9, 11, 13}
int[:] h = primes; // A slice {2, 3, 5, 7, 9, 11, 13}
int[:] i = h[:]; // A slice {2, 3, 5, 7, 9, 11, 13}
50/53 corefxlab