C# - What's Next?
Christian Nagel
csharp.christiannagel.com
Goals
• Wie entwickelt sich C#?
• Versionen
• C# Current (C# 7.x)
• Future Plans
• Constraints
• Einige Features können jetzt schon verwendet werden
• Etwas wird sich noch ändern
Agenda
Productivity Performance Reduce
Errors
Christian Nagel
• Training
• Coaching
• Consulting
• Microsoft MVP
• www.cninnovation.com
• csharp.christiannagel.com
Expressions Everywhere (C# 7.0)
• Property Accessors
• Constructors
• Destructors
• Event Accessors
• Local Functions
private string _firstName;
public string FirstName
{
get => _firstName;
set => Set(ref _firstName, value);
}
Out Vars (C# 7.0)
• Declare variable on use
string n = "42";
if (string.TryParse(n, out var result)
{
Console.WriteLine(
$"Converting to a number was successful: {result}");
}
Non-Trailing Named Arguments (C# 7.2)
• Assign the name to any argument
if (Enum.TryParse(weekdayRecommendation.Entity, ignoreCase: true,
out DayOfWeek weekday))
{
reservation.Weekday = weekday;
}
Local Functions (C# 7.0)
• Declare functions within methods
• Scope within the method
• Simplifies Lambda syntax
• Helps with parameter checking (yield)
public void SomeFunStuff()
{
int add(int x, int y) => x + y;
int result = add(38, 4);
Console.WriteLine(result);
}
public void SomeFunStuff()
{
Func<int, int, int> add = (x, y) => x + y;
int result = add(38, 4);
Console.WriteLine(result);
}
Throw Expressions (C# 7.0)
• Throw in expressions instead of statements
private readonly IBooksService _booksService;
public BookController(BooksService booksService)
{
if (booksService == null)
{
throw new ArgumentNullException(nameof(b));
}
_booksService = booksService;
}
private readonly IBooksService _booksService;
public BookController(BooksService booksService)
{
_booksService = booksService ?? throw new ArgumentNullException(nameof(b));
}
Tuples (C# 7.0)
• Combine multiple values
• Strong names
• Value types
var t1 = (n: 42, s: "magic");
int i1 = t1.n;
string s1 = t1.s;
Infer Tuple Names (C# 7.1)
• Infer tuple names from parameters
• Similar to anonymous types
var t1 = (racer.FirstName, racer.Wins);
int wins = t1.Wins;
Deconstruction (C# 7.0)
• Create parts from objects or tuples
• Implement Deconstruct Method
var p1 = new Person("Tom", "Turbo");
(string firstName, string lastName) = p1;
Pattern Matching (C# 7.0)
• is Operator and switch statement extended
• Const Pattern
• Type Pattern
• Var Pattern
public void IsSample(object o)
{
if (o is 42)
{
}
if (o is Person p)
{
}
if (o is var v1)
{
}
}
public void PatternMatchingWithSwitchStatement(object o)
{
switch (o)
{
case 42:
break;
case Person p when p.FirstName == "Katharina":
break;
case Person p:
break;
case var v:
break;
}
}
Records (C# 8.0 - Planned)
• Immutable Type
• class or struct
public struct Pair(object First, object Second);
var p1 = new Pair("one", "two");
var p2 = p1.With(First: "alpha");
Record Implementation (C# 8.0 - Planned)
• Implements
• IEquatable
• GetHashCode
• Deconstruct
• With
public struct Pair(object First, object Second);
public struct Pair : IEquatable<Pair>
{
public object First { get; }
public object Second { get; }
public Pair(object First, object Second)
{
this.First = First;
this.Second = Second;
}
public bool Equals(Pair other) =>
Equals(First, other.First) && Equals(Second, other.Second);
public override bool Equals(object other) =>
(other as Pair)?.Equals(this) == true;
public override int GetHashCode() =>
(First?.GetHashCode()*17 + Second?.GetHashCode()).GetValueOrDefault();
public Pair With(object First = this.First, object Second = this.Second) =>
new Pair(First, Second);
public void Deconstruct(out object First, out object Second)
{
First = this.First;
Second = this.Second;
}
}
var p1 = new Pair("one", "two");
var p2 = p1.With(First: "alpha");
Productivity - Summary
• Expressions everywhere
• Out vars
• Non-trailing named arguments
• Local functions
• Throw expressions
• Tuples
• Pattern matching
• Records
Performance
Async Streams (C# 8 - Planned)
• Exchange stream of data across asynchronous boundary
Async Streams (C# 8 - Planned)
• IAsyncDisposable
• IAsyncEnumerable
• IAsyncEnumerator
public interface IAsyncDisposable
{
Task DisposeAsync();
}
public interface IAsyncEnumerable<out T>
{
IAsyncEnumerator<T> GetAsyncEnumerator();
}
public interface IAsyncEnumerator<out T> :
IAsyncDiposable
{
Task<bool> MoveNextAsync();
T Current { get; }
}
Using Async Streams (C# 8 Planned)
• foreach await
IAsyncEnumerator<T> enumerator =
enumerable.GetAsyncEnumerator();
try
{
while (await enumerator.MoveNextAsync())
{
Use(enumerator.Current);
}
}
finally
{
await enumerator.DisposeAsync();
}
foreach await (var i in enumerable)
{
Use(i);
}
Async with yield (C# 8 - Planned)
• return IAsyncEnumerable static async IAsyncEnumerable<int> MyIterator()
{
try
{
for (int i = 0; i < 100; i++)
{
await Task.Delay(1000);
yield return i;
}
}
finally
{
await Task.Delay(200);
Console.WriteLine("finally");
}
}
ArrayPool
• Reduce work needed by Garbage Collector
• Pool of arrays managed by ArrayPool
int[] arr = ArrayPool<int>.Shared.Rent(length);
//... use array
ArrayPool<int>.Shared.Return(arr, clearArray: true);
Reference Semantics
• Reference Types
• class
• Copy By Reference
• Use Managed Heap
• Value Types
• struct
• Copy By Value
• Use Stack or Managed Heap (Boxing)
• GC doesn't need to cleanup the stack
Pass Value Type by…
• by value
• by reference
• by readonly reference
• C# 7.2
public void SetItem(int makeACopy)
{
}
public void SetItem(ref int byReference)
{
byReference = 42;
}
public void SetItem(in int byReference)
{
// guarantee no change
}
Returning Value Types By Reference
• ref return (7.0)
• ref local (7.0)
• ref readonly return (7.2)
• ref readonly local (7.2)
public ref int GetItem(int index)
{
return ref _items[index];
}
public ref readonly int GetReadonlyItem(int index)
{
return ref _items[index];
}
ref int x = ref GetItem(4);
.NET Type Categories
• Managed Heap
• class
• Stack or Managed Heap
• struct
• Stack Only (C# 7.2)
• ref struct
Span
• Accesses range of native memory, managed heap, stack
• Continous range of memory
• Reference semantics
• Specialized for arrays, strings
string text = GetString();
ReadOnlySpan<char> span1 = text.AsSpan();
ReadOnlySpan<char> slice1 = span1.Slice(5, 20);
string newString = new string(slice1.ToArray());
Overloads Span and Memory
• Parse
• System.Random
• System.Text.StringBuilder
• System.Net.Sockets
• Specialized extension methods
for arrays and strings
public virtual ValueTask<int> ReadAsync(
Memory<byte> destination,
CancellationToken cancellationToken = default);
Slicing (ranges) – C# 7.3 Planned
• Operator ..
var slice = source[5..10];
switch (codePoint)
{
case 1536..1541:
break;
foreach (var item in 3..5)
{
Performance - Summary
• Reduce copy needs with value types
• Reduce need for unsafe code
• Span
• ArrayPool
Avoiding Errors
What is the most common exception?
• NullReferenceException
Null Conditional Operator (C# 6)
• Reduce Null-Checks
int? length = customers?.Length;
Customer first = customers?[0];
int? count = customers?[0]?.Orders?.Count();
public void OnPropertyChanged([CallerMemberName] string propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
Coalescing Operator (C# 5)
• Default values for null
int length = customers?.Length ?? 0;
public bool CanExecute(object parameter) =>
_canExecute?.Invoke() ?? true;
Nullable Reference Types (C# 8 Planned)
• Helps finding bugs, no guarantee
• Flow analysis tracks nullable reference variables
• Breaks existing code (opt-in)
• Nullability implemented with metadata (ignored by downlevel
compilers)
• string, T non-nullable
• string?, T? nullable
Declare Nullable Reference Types
• Default to non-nullable
• ? to make reference type nullable
class Book
{
public Book(string title, string? isbn = null)
{
Title = title;
Isbn = isbn;
}
public string Title; // not null
public string? Isbn; // may be null
}
Using nullable reference type
void M1(string? ns)
{
Console.WriteLine(ns.Length); // compiler warning
void M2(string? ns)
{
if (ns == null) throw new ArgumentNullException(nameof(ns));
Console.WriteLine(ns.Length); // ok, not null
ns = null;
Console.WriteLine(ns.Length); // compiler warning
void M3(string? ns)
{
if (ns != null)
{
Console.WriteLine(ns.Length); // ok
Using non-nullable reference type
void M1(string ns)
{
Console.WriteLine(ns.Length); // ok
void M2(Book b)
{
b.Title = null; // warning
string isbn = b.Isbn; // warning – may be null
string title = b.Title; // ok
Avoiding Errors - Summary
• Get rid of the most common exception
Summary
Productivity
Performance
Avoiding Errors
Questions?
More Information (1)
• https://dot.net
• https://github.com/dotnet/csharplang
• Sample Code:
• https://github.com/CNinnovation
More Information (2)
• https://github.com/ProfessionalCSharp
• https://csharp.christiannagel.com
• https://www.cninnovation.com
• Training & Coaching
Thank you!
C# 7.0
• Binary literals
• Digit separators
• Local functions
• Out var
• Throw expressions
C# 7.1
• Async Main
• Generics Pattern Match
• Infer Tuple Names
• Target-typed default
C# 7.2
• Conditional Ref
• Leading Separator
• Non-trailing named arguments
• Private Protected
• Readonly Ref
• Span Safety
C# 7.3 Candidates
• Generic constraints
• delegate, enum
• == != on tuples
• Blittable
• Slicing
• Extension methods with parameter modifiers ref and in
8.0 Candidates
• Pattern Matching
• Recursive
• Subpattern, property…
• Range pattern
• Deconstruct pattern
• Tuple pattern
• Default Interface Methods
• Records
• Nullable Reference Types
• Async Streams
• UTF8 String Literals

C# - What's Next?

  • 1.
    C# - What'sNext? Christian Nagel csharp.christiannagel.com
  • 2.
    Goals • Wie entwickeltsich C#? • Versionen • C# Current (C# 7.x) • Future Plans • Constraints • Einige Features können jetzt schon verwendet werden • Etwas wird sich noch ändern
  • 3.
  • 4.
    Christian Nagel • Training •Coaching • Consulting • Microsoft MVP • www.cninnovation.com • csharp.christiannagel.com
  • 6.
    Expressions Everywhere (C#7.0) • Property Accessors • Constructors • Destructors • Event Accessors • Local Functions private string _firstName; public string FirstName { get => _firstName; set => Set(ref _firstName, value); }
  • 7.
    Out Vars (C#7.0) • Declare variable on use string n = "42"; if (string.TryParse(n, out var result) { Console.WriteLine( $"Converting to a number was successful: {result}"); }
  • 8.
    Non-Trailing Named Arguments(C# 7.2) • Assign the name to any argument if (Enum.TryParse(weekdayRecommendation.Entity, ignoreCase: true, out DayOfWeek weekday)) { reservation.Weekday = weekday; }
  • 9.
    Local Functions (C#7.0) • Declare functions within methods • Scope within the method • Simplifies Lambda syntax • Helps with parameter checking (yield) public void SomeFunStuff() { int add(int x, int y) => x + y; int result = add(38, 4); Console.WriteLine(result); } public void SomeFunStuff() { Func<int, int, int> add = (x, y) => x + y; int result = add(38, 4); Console.WriteLine(result); }
  • 10.
    Throw Expressions (C#7.0) • Throw in expressions instead of statements private readonly IBooksService _booksService; public BookController(BooksService booksService) { if (booksService == null) { throw new ArgumentNullException(nameof(b)); } _booksService = booksService; } private readonly IBooksService _booksService; public BookController(BooksService booksService) { _booksService = booksService ?? throw new ArgumentNullException(nameof(b)); }
  • 11.
    Tuples (C# 7.0) •Combine multiple values • Strong names • Value types var t1 = (n: 42, s: "magic"); int i1 = t1.n; string s1 = t1.s;
  • 12.
    Infer Tuple Names(C# 7.1) • Infer tuple names from parameters • Similar to anonymous types var t1 = (racer.FirstName, racer.Wins); int wins = t1.Wins;
  • 13.
    Deconstruction (C# 7.0) •Create parts from objects or tuples • Implement Deconstruct Method var p1 = new Person("Tom", "Turbo"); (string firstName, string lastName) = p1;
  • 14.
    Pattern Matching (C#7.0) • is Operator and switch statement extended • Const Pattern • Type Pattern • Var Pattern public void IsSample(object o) { if (o is 42) { } if (o is Person p) { } if (o is var v1) { } } public void PatternMatchingWithSwitchStatement(object o) { switch (o) { case 42: break; case Person p when p.FirstName == "Katharina": break; case Person p: break; case var v: break; } }
  • 15.
    Records (C# 8.0- Planned) • Immutable Type • class or struct public struct Pair(object First, object Second); var p1 = new Pair("one", "two"); var p2 = p1.With(First: "alpha");
  • 16.
    Record Implementation (C#8.0 - Planned) • Implements • IEquatable • GetHashCode • Deconstruct • With public struct Pair(object First, object Second); public struct Pair : IEquatable<Pair> { public object First { get; } public object Second { get; } public Pair(object First, object Second) { this.First = First; this.Second = Second; } public bool Equals(Pair other) => Equals(First, other.First) && Equals(Second, other.Second); public override bool Equals(object other) => (other as Pair)?.Equals(this) == true; public override int GetHashCode() => (First?.GetHashCode()*17 + Second?.GetHashCode()).GetValueOrDefault(); public Pair With(object First = this.First, object Second = this.Second) => new Pair(First, Second); public void Deconstruct(out object First, out object Second) { First = this.First; Second = this.Second; } } var p1 = new Pair("one", "two"); var p2 = p1.With(First: "alpha");
  • 17.
    Productivity - Summary •Expressions everywhere • Out vars • Non-trailing named arguments • Local functions • Throw expressions • Tuples • Pattern matching • Records
  • 18.
  • 19.
    Async Streams (C#8 - Planned) • Exchange stream of data across asynchronous boundary
  • 20.
    Async Streams (C#8 - Planned) • IAsyncDisposable • IAsyncEnumerable • IAsyncEnumerator public interface IAsyncDisposable { Task DisposeAsync(); } public interface IAsyncEnumerable<out T> { IAsyncEnumerator<T> GetAsyncEnumerator(); } public interface IAsyncEnumerator<out T> : IAsyncDiposable { Task<bool> MoveNextAsync(); T Current { get; } }
  • 21.
    Using Async Streams(C# 8 Planned) • foreach await IAsyncEnumerator<T> enumerator = enumerable.GetAsyncEnumerator(); try { while (await enumerator.MoveNextAsync()) { Use(enumerator.Current); } } finally { await enumerator.DisposeAsync(); } foreach await (var i in enumerable) { Use(i); }
  • 22.
    Async with yield(C# 8 - Planned) • return IAsyncEnumerable static async IAsyncEnumerable<int> MyIterator() { try { for (int i = 0; i < 100; i++) { await Task.Delay(1000); yield return i; } } finally { await Task.Delay(200); Console.WriteLine("finally"); } }
  • 23.
    ArrayPool • Reduce workneeded by Garbage Collector • Pool of arrays managed by ArrayPool int[] arr = ArrayPool<int>.Shared.Rent(length); //... use array ArrayPool<int>.Shared.Return(arr, clearArray: true);
  • 24.
    Reference Semantics • ReferenceTypes • class • Copy By Reference • Use Managed Heap • Value Types • struct • Copy By Value • Use Stack or Managed Heap (Boxing) • GC doesn't need to cleanup the stack
  • 25.
    Pass Value Typeby… • by value • by reference • by readonly reference • C# 7.2 public void SetItem(int makeACopy) { } public void SetItem(ref int byReference) { byReference = 42; } public void SetItem(in int byReference) { // guarantee no change }
  • 26.
    Returning Value TypesBy Reference • ref return (7.0) • ref local (7.0) • ref readonly return (7.2) • ref readonly local (7.2) public ref int GetItem(int index) { return ref _items[index]; } public ref readonly int GetReadonlyItem(int index) { return ref _items[index]; } ref int x = ref GetItem(4);
  • 27.
    .NET Type Categories •Managed Heap • class • Stack or Managed Heap • struct • Stack Only (C# 7.2) • ref struct
  • 28.
    Span • Accesses rangeof native memory, managed heap, stack • Continous range of memory • Reference semantics • Specialized for arrays, strings string text = GetString(); ReadOnlySpan<char> span1 = text.AsSpan(); ReadOnlySpan<char> slice1 = span1.Slice(5, 20); string newString = new string(slice1.ToArray());
  • 29.
    Overloads Span andMemory • Parse • System.Random • System.Text.StringBuilder • System.Net.Sockets • Specialized extension methods for arrays and strings public virtual ValueTask<int> ReadAsync( Memory<byte> destination, CancellationToken cancellationToken = default);
  • 30.
    Slicing (ranges) –C# 7.3 Planned • Operator .. var slice = source[5..10]; switch (codePoint) { case 1536..1541: break; foreach (var item in 3..5) {
  • 31.
    Performance - Summary •Reduce copy needs with value types • Reduce need for unsafe code • Span • ArrayPool
  • 32.
  • 33.
    What is themost common exception? • NullReferenceException
  • 34.
    Null Conditional Operator(C# 6) • Reduce Null-Checks int? length = customers?.Length; Customer first = customers?[0]; int? count = customers?[0]?.Orders?.Count(); public void OnPropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  • 35.
    Coalescing Operator (C#5) • Default values for null int length = customers?.Length ?? 0; public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true;
  • 36.
    Nullable Reference Types(C# 8 Planned) • Helps finding bugs, no guarantee • Flow analysis tracks nullable reference variables • Breaks existing code (opt-in) • Nullability implemented with metadata (ignored by downlevel compilers) • string, T non-nullable • string?, T? nullable
  • 37.
    Declare Nullable ReferenceTypes • Default to non-nullable • ? to make reference type nullable class Book { public Book(string title, string? isbn = null) { Title = title; Isbn = isbn; } public string Title; // not null public string? Isbn; // may be null }
  • 38.
    Using nullable referencetype void M1(string? ns) { Console.WriteLine(ns.Length); // compiler warning void M2(string? ns) { if (ns == null) throw new ArgumentNullException(nameof(ns)); Console.WriteLine(ns.Length); // ok, not null ns = null; Console.WriteLine(ns.Length); // compiler warning void M3(string? ns) { if (ns != null) { Console.WriteLine(ns.Length); // ok
  • 39.
    Using non-nullable referencetype void M1(string ns) { Console.WriteLine(ns.Length); // ok void M2(Book b) { b.Title = null; // warning string isbn = b.Isbn; // warning – may be null string title = b.Title; // ok
  • 40.
    Avoiding Errors -Summary • Get rid of the most common exception
  • 41.
  • 42.
  • 43.
    More Information (1) •https://dot.net • https://github.com/dotnet/csharplang • Sample Code: • https://github.com/CNinnovation
  • 44.
    More Information (2) •https://github.com/ProfessionalCSharp • https://csharp.christiannagel.com • https://www.cninnovation.com • Training & Coaching
  • 45.
  • 46.
    C# 7.0 • Binaryliterals • Digit separators • Local functions • Out var • Throw expressions
  • 47.
    C# 7.1 • AsyncMain • Generics Pattern Match • Infer Tuple Names • Target-typed default
  • 48.
    C# 7.2 • ConditionalRef • Leading Separator • Non-trailing named arguments • Private Protected • Readonly Ref • Span Safety
  • 49.
    C# 7.3 Candidates •Generic constraints • delegate, enum • == != on tuples • Blittable • Slicing • Extension methods with parameter modifiers ref and in
  • 50.
    8.0 Candidates • PatternMatching • Recursive • Subpattern, property… • Range pattern • Deconstruct pattern • Tuple pattern • Default Interface Methods • Records • Nullable Reference Types • Async Streams • UTF8 String Literals