w w w. l a m b d a 3 . c o m . b r
Novidades do C# 7 e 8
Giovanni Bassi
• Programador
• Microsoft MVP
• Não gerente
• blog.lambda3.com.br, podcast.lambda3.com.br,
dotnetarchitects.net, nodebr, dockersp
@giovannibassi
/giggio.tech
• Escalador e ciclista
podcast.lambda3.com.br
0 – Eventos
1 – Docker
2 – .NET Core RC2
3 – Git
4 – Estudo
5 – Open Source
6 – Xamarin
7 – Node.js
8 – Democracia organizacional
9 – O programador poliglota
...
Toda semana em:
Acreditamos que a diversidade, em
suas diversas expressões, deve ser
respeitada e valorizada, e que uma
sociedade mais diversa é uma
sociedade melhor.
diversidade.tech
bit.ly/novidadescsharp7
Agenda
Evolução do C#
Código gerenciado
Generics
Language Integrated Query
Dinamismo + paridade nas linguagens
C# 5 + VB 11
Programação assíncrona
C# 1 + VB 7
C# 2 + VB 8
C# 3 + VB 9
C# 4 + VB 10
C# 6 + VB 14
Roslyn
Código gerenciado
Generics
Language Integrated Query
Dinamismo + paridade nas linguagens
C# 5 + VB 11
Programação assíncrona
C# 1 + VB 7
C# 2 + VB 8
C# 3 + VB 9
C# 4 + VB 10
C# 6 + VB 14
Roslyn
C# 7 + VB 15
Features!
Código gerenciado
Generics
Language Integrated Query
Dinamismo + paridade nas linguagens
C# 5 + VB 11
Programação assíncrona
C# 1 + VB 7
C# 2 + VB 8
C# 3 + VB 9
C# 4 + VB 10
C# 6 + VB 14
Roslyn
C# 7 + VB 15
Features! Features! Features!Features!
Código gerenciado
Generics
Language Integrated Query
Dinamismo + paridade nas linguagens
C# 5 + VB 11
Programação assíncrona
C# 1 + VB 7
C# 2 + VB 8
C# 3 + VB 9
C# 4 + VB 10
C# 6 + VB 14
Roslyn
C# 7 + VB 15
Features! Features! Features!
Features (parte 2)?
C# 8 + VB 16?
https://insights.stackoverflow.com/survey/2018/#most-popular-technologies
https://insights.stackoverflow.com/survey/2018/#most-popular-technologies
F#
10,000’s
Tens of thousands
VB
100,000’s
Hundreds of thousands
C#
1,000,000’s
Millions
System.ValueTuple
<PropertyGroup>
<LangVersion>latest</LangVersion>
</PropertyGroup>
C# 7.0
public static int DiceSum(IEnumerable<object> values)
{
var sum = 0;
foreach (var item in values)
{
if (item is int val)
sum += val;
else if (item is IEnumerable<object> subList)
sum += DiceSum(subList);
}
return sum;
}
https://docs.microsoft.com/dotnet/csharp/pattern-matching
http://bit.ly/cspatternmatching
https://docs.microsoft.com/dotnet/csharp/pattern-matching
public static int DiceSum(IEnumerable<object> values)
{
var sum = 0;
foreach (var item in values)
{
switch (item)
{
}
}
return sum;
}
http://bit.ly/cspatternmatching
case 0:
break;
case int val:
sum += val;
break;
case PercentileDie die:
sum += die.Multiplier * die.Value;
break;
https://docs.microsoft.com/dotnet/csharp/pattern-matching
public static int DiceSum(IEnumerable<object> values)
{
var sum = 0;
foreach (var item in values)
{
switch (item)
{
}
}
return sum;
}
http://bit.ly/cspatternmatching
case 0:
break;
case int val:
sum += val;
break;
case PercentileDie die:
sum += die.Multiplier * die.Value;
break;
https://docs.microsoft.com/dotnet/csharp/pattern-matching
case IEnumerable<object> subList when subList.Any():
sum += DiceSum(subList);
break;
case IEnumerable<object> subList:
break;
case null:
break;
default:
throw new InvalidOperationException("unknown");
public static int DiceSum(IEnumerable<object> values)
{
var sum = 0;
foreach (var item in values)
{
switch (item)
{
}
}
return sum;
}
http://bit.ly/cspatternmatching
var numbers1 = (1, 2);
var objs = ("1", 2);
WriteLine($"{numbers1.Item1}, {numbers1.Item2}");
(int one, int two) numbers2 = (1, 2);
WriteLine($"{numbers2.Item1}, {numbers2.Item2}");
WriteLine($"{numbers2.one}, {numbers2.two}");
var numbers3 = (one: 1, two: 2);
WriteLine($"{numbers3.one}, {numbers3.two}");
(int uno, int due) numbers4 = (one: 1, two: 2);
WriteLine($"{numbers4.uno}, {numbers4.due}");
// WriteLine($"{numbers4.one}, {numbers4.two}"); // error
https://docs.microsoft.com/dotnet/csharp/tuples
http://bit.ly/cstuplas
void M()
{
var (name, company) = Person.Get();
WriteLine($"{name}, {company}");
}
class Person
{
public string Name { get; set; }
public string Company { get; set; }
public void Deconstruct(out string name, out string company)
{
name = Name;
company = Company;
}
public static Person Get() => new Person { Name = "…", Company = "…" };
} https://docs.microsoft.com/dotnet/csharp/tupleshttp://bit.ly/cstuplas
var numbers = (one: 1, two: 2);
var (uno, due) = numbers;
WriteLine($"{uno}, {due}");
https://docs.microsoft.com/dotnet/csharp/tuples
http://bit.ly/cstuplas
if (int.TryParse("1", out int result))
WriteLine(result);
if (int.TryParse("1", out var result2))
WriteLine(result2);
https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7#out-variables
http://bit.ly/csoutvars
(int x, int y) GetCoordinates() => (1, 2);
var (_, y) = GetCoordinates();
https://docs.microsoft.com/dotnet/csharp/discards
http://bit.ly/csdiscards
void ObjectDeconstruction()
{
var (name, _) = Person.Get();
}
https://docs.microsoft.com/dotnet/csharp/discards
http://bit.ly/csdiscards
if (int.TryParse("1", out _))
WriteLine("It is a number!");
https://docs.microsoft.com/dotnet/csharp/discards
http://bit.ly/csdiscards
object o = 1;
if (o is Person p)
WriteLine(p.Company);
else if (o is null)
WriteLine("null");
else if (o is var _)
WriteLine("Unknown");
https://docs.microsoft.com/dotnet/csharp/discards
http://bit.ly/csdiscards
ref int Find(int[,] matrix, Func<int, bool> predicate)
{
for (int i = 0; i < matrix.GetLength(0); i++)
for (int j = 0; j < matrix.GetLength(1); j++)
if (predicate(matrix[i, j]))
return ref matrix[i, j];
throw new InvalidOperationException("Not found");
}
https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7#ref-locals-and-returns
http://bit.ly/csreflocals
void M()
{
var matrix = new [,] { { 1, 2, 3, 4, 5 },
{ 6, 7, 8, 42, 10 } };
ref var item = ref Find(matrix, val => val == 42);
WriteLine(item);
item = 24;
WriteLine(matrix[1, 3]);
}
https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7#ref-locals-and-returns
http://bit.ly/csreflocals
public ExpressionBodiedMembers(string name) => Name = name;
~ExpressionBodiedMembers() => WriteLine("Finalized!");
private string name;
public string Name
{
get => name;
set => name = value;
}
https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7#more-expression-bodied-members
http://bit.ly/csmaisebm
private string a = GetA() ?? throw new Exception();
private static string GetA() => throw new Exception();
https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7#throw-expressions
http://bit.ly/csexpthrow
int sixteen = 0b0001_0000;
int thirtyTwo = 0b0010_0000;
int sixtyFour = 0b0100_0000;
int oneHundredTwentyEight = 0b1000_0000;
long oneHundredBillions = 100_000_000_000;
double AvogadroConstant = 6.022_140_857_747_474e23;
decimal GoldenRatio = 1.618_033_988_749M;
https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7#numeric-literal-syntax-improvements
http://bit.ly/csnumlit
double Average(IEnumerable<int> ns)
{
double Divide(double a, double b)
{
if (b == 0) throw new DivideByZeroException();
return a / b;
}
var sum = ns.Sum();
return Divide(sum, ns.Count());
}
https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7#local-functions
http://bit.ly/cslocalfunc
void Add1AndSumAll(int[] ms)
{
IEnumerable<int> Sum1(IEnumerable<int> ns)
{
foreach (var n in ns)
yield return n + 1;
}
if (ms == null) throw new NullReferenceException();
WriteLine(Sum1(ms).Sum());
}
https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7#local-functions
http://bit.ly/cslocalfunc
public async ValueTask<int> Get5()
{
await Task.Delay(100);
return 5;
}
https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7#generalized-async-return-types
http://bit.ly/csasyncgeneral
public async ValueTask<int> SumAsync(IEnumerable<ValueTask<int>> ns)
{
var sum = 0;
foreach (var nTask in ns)
{
var n = await nTask;
sum += n;
}
return sum;
}
https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7#generalized-async-return-types
http://bit.ly/csasyncgeneral
C# 7.1
var x = (f1: 1, f2: 2);
var tuple = (x.f1, x.f2, x);
https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7-1#inferred-tuple-element-names
http://bit.ly/cstuplainfer
Func<string, bool> predicate = default;
int i = default;
N(default);
https://docs.microsoft.com/dotnet/csharp/programming-guide/statements-expressions-operators/default-value-expressions
http://bit.ly/csliteraldefault
(int, int) N(int[] ns = default) => default;
string O()
{
return default;
}
T P<T>()
{
T t = default;
return t;
}
https://docs.microsoft.com/dotnet/csharp/programming-guide/statements-expressions-operators/default-value-expressions
http://bit.ly/csliteraldefault
static async Task<int> Main(string[] args)
{
await Task.Delay(500);
WriteLine("Done");
return 0;
}
https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7-1#async-main
http://bit.ly/csasyncmain
C# 7.2
void M(int i, int j) { }
void N()
{
M(i: 2, 3);
}
https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/non-trailing-named-arguments.md
http://bit.ly/csntna
var m = 0b_101; //5
var n = 0x_00C0; // 192
https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/leading-separator.md
http://bit.ly/cs_num
class Base
{
private protected int f;
}
class DerivedInSameAssembly : Base
{
void M()
{
var x = base.f;
}
}
class DerivedInOtherAssembly : Base
{
void M()
{
var x = base.f; //error
}
}
https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/private-protected.md
http://bit.ly/csprpr
Vector Add(in Vector v1, in Vector v2)
{
v1 = default(Vector); // not OK!!
v1.X = 0; // not OK!!
Foo(ref v1.X); // not OK!!
// OK:
return new Vector(v1.X +v2.X, v1.Y + v2.Y);
}
https://docs.microsoft.com/dotnet/csharp/reference-semantics-with-value-types
http://bit.ly/csmodin
https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/readonly-ref.md#refin-extension-methods
public static Ponto Soma(in this Ponto p1, in Ponto p2) =>
new Ponto(p1.X + p2.X, p1.Y + p2.Y);
void In()
{
Ponto p1 = new Ponto(1, 2), p2 = new Ponto(3, 4);
var pResultado = p1.Soma(p2);
var pResultado2 = p1.Soma(new Ponto(3, 4));
var pResultado3 = new Ponto().Soma(new Ponto(3, 4));
}
http://bit.ly/csmodin
public static Ponto Subtrai(ref this Ponto p1, ref Ponto p2) =>
new Ponto(p1.X - p2.X, p1.Y - p2.Y);
void Ref()
{
Ponto p1 = new Ponto(1, 2), p2 = new Ponto(3, 4);
var pResultado = p1.Subtrai(ref p2);
p1.Subtrai(ref new Ponto()); // não compila
new Ponto().Subtrai(ref p1); // não compila
}
https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/readonly-ref.md#refin-extension-methods
http://bit.ly/csextref
https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/readonly-ref.md#refin-extension-methods
public static void Foo<T>(ref this T p1)
where T : struct
{
}
http://bit.ly/csextref
ref readonly Vector FetchOrigin() => ref origin;
void M()
{
var v1 = new Vector(1, 2, 3);
var v2 = new Vector(4, 5, 6);
var vSum = Add(v1, v2);
ref readonly var origin = ref FetchOrigin();
}
private readonly Vector origin = new Vector(0, 0, 0);
https://docs.microsoft.com/dotnet/csharp/reference-semantics-with-value-types
http://bit.ly/csretrefro
ref readonly var origin = ref FetchOrigin();
var origin2 = FetchOrigin(); // copy created
https://docs.microsoft.com/dotnet/csharp/reference-semantics-with-value-types
http://bit.ly/csretrefro
readonly struct Ponto
{
public Ponto(float x, float y)
{
X = x;
Y = y;
}
public float X { get; }
public float Y { get; }
private readonly static Ponto origem = new Ponto();
public static ref readonly Ponto Origem => ref origem;
}
https://docs.microsoft.com/dotnet/csharp/reference-semantics-with-value-types#readonly-struct-type
http://bit.ly/csrostruct
ref readonly var p = ref Ponto.Origem;
var x = p.X; // sem cópia
https://docs.microsoft.com/dotnet/csharp/reference-semantics-with-value-types#readonly-struct-type
http://bit.ly/csrostruct
ref int M()
{
var arr = new[] { 1, 2, 3 };
var otherArr = new[] { 4, 5, 6 };
ref var r = ref (arr != null ? ref arr[0]: ref
otherArr[0]);
Foo(ref (arr != null ? ref arr[0]: ref otherArr[0]));
(arr != null ? ref arr[0]: ref otherArr[0]) = 1;
return ref (arr != null ? ref arr[0]: ref otherArr[0]);
}
void Foo(ref int i) => i = 42;
https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/conditional-ref.md
http://bit.ly/csternarioref
ref struct TwoSpans<T>
{
// pode ter campos desde que não sejam classes
public Span<T> first;
public Span<T> second;
}
// erro: arrays of ref-like types are not allowed.
TwoSpans<int>[] arr = null;
https://docs.microsoft.com/en-us/dotnet/csharp/reference-semantics-with-value-types#ref-struct-type
readonly ref struct ReadOnlyRefPoint2D
{
public int X { get; }
public int Y { get; }
public ReadOnlyRefPoint2D(int x, int y) =>
(X, Y) = (x, y);
}
https://docs.microsoft.com/dotnet/csharp/reference-semantics-with-value-types#readonly-ref-struct-type
void M()
{
Span<int> arr = stackalloc int[3];
}
https://msdn.microsoft.com/en-us/magazine/mt814808.aspx
Não é unsafe
C# 7.3
Span<int> RowFive = new Span<int>(PascalsTriangle, 10, 5);
fixed (int* ptrToRow = RowFive)
{
// Soma os números 1,4,6,4,1
var sum = 0;
for (int i = 0; i < RowFive.Length; i++)
{
sum += *(ptrToRow + i);
}
Console.WriteLine(sum);
}
https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/pattern-based-fixed.md
int[] PascalsTriangle = {
1,
1, 1,
1, 2, 1,
1, 3, 3, 1,
1, 4, 6, 4, 1,
1, 5, 10, 10, 5, 1 };
Span<T>.GetPinnableReference()
unsafe struct S
{
public fixed int myFixedField[10];
}
class C
{
private static S s = new S();
unsafe public void M()
{
int p = s.myFixedField[5];
}
}
https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7-3#indexing-fixed-fields-does-not-require-pinning
// CSharp 7.2
fixed (int* ptr = s.myFixedField)
{
int p = ptr[5];
}
var structs = new StructGrande[]
{ new StructGrande(), new StructGrande() };
ref StructGrande refLocal = ref structs[0];
refLocal = ref structs[1];
https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7-3#ref-local-variables-may-be-reassigned
unsafe void M()
{
int* pArr = stackalloc int[3] { 1, 2, 3 };
int* pArr2 = stackalloc int[] { 1, 2, 3 };
Span<int> arr = stackalloc[] { 1, 2, 3 };
}
https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7-3#stackalloc-arrays-support-initializers
unsafe public static byte[] ToByteArray<T>
(this T argument) where T : unmanaged
{
var size = sizeof(T);
var result = new byte[size];
byte* p = (byte*)&argument;
for (var i = 0; i < size; i++)
result[i] = *p++;
return result;
}
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters#unmanaged-constraint
public static TDelegate TypeSafeCombine<TDelegate>
(this TDelegate source, TDelegate target)
where TDelegate : System.Delegate =>
System.Delegate.Combine(source, target)
as TDelegate;
Action first = () => Console.WriteLine("this");
Action second = () => Console.WriteLine("that");
var combined = first.TypeSafeCombine(second);
combined();
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters#delegate-constraints
public static Dictionary<int, string> EnumNamedValues<T>()
where T : System.Enum
{
var result = new Dictionary<int, string>();
var values = Enum.GetValues(typeof(T));
foreach (int item in values)
result.Add(item, Enum.GetName(typeof(T), item));
return result;
}
enum Rainbow { Red, Orange, Yellow, Green, Blue, Indigo, Violet }
var map = EnumNamedValues<Rainbow>();
foreach (var pair in map)
Console.WriteLine($"{pair.Key}:t{pair.Value}");
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters#enum-constraints
https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/expression-variables-in-initializers.md
var t1 = (1, 2);
var t2 = (3, 4);
var iguais = t1 == t2;
var diferentes = t1 != t2;
https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/expression-variables-in-initializers.md
var query = from text in strings
select int.TryParse(text, out int value)
? value : (int?)null;
public class Foo
{
protected Foo(out int i) => i = 1;
}
public class Bar : Foo
{
public int Value { get; }
public Bar() : base(out int value) => Value = value;
}
https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/expression-variables-in-initializers.md
public class Baz
{
private readonly int field1 =
int.TryParse(SomeStatic.Text, out int value)
? Value
: -1;
}
https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/expression-variables-in-initializers.md
https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/auto-prop-field-attrs.md
[Serializable]
public class Foo
{
[field: NonSerialized]
public string MySecret { get; set; }
}
C# 8.0
public class Cartesian { public int X { get; } public int Y { get; } }
public class Polar
{
public static bool operator is(Cartesian c, out double R, out double Theta)
{
R = Math.Sqrt(c.X * c.X + c.Y * c.Y);
Theta = Math.Atan2(c.Y, c.X);
return c.X != 0 || c.Y != 0;
}
}
void M()
{
var c = new Cartesian(3, 4);
if (c is Polar(var R, _)) WriteLine(R);
}
https://github.com/dotnet/csharplang/blob/master/proposals/patterns.md
void N(string? aNullableString, bool condition) {
WriteLine(aNullableString.Length); // warning
if (aNullableString != null)
WriteLine(aNullableString); // no warning
if (!string.IsNullOrWhiteSpace(aNullableString))
WriteLine(aNullableString!.Length); // I know better
var anotherNullableString = condition ? "Hello" : aNullableString;
WriteLine(anotherNullableString.Length); // warning
var yetAnotherNullableString = condition ? "Hello" : null;
WriteLine(yetAnotherNullableString.Length); // warning
string nonNullableString = null; //warning
WriteLine(nonNullableString); // no warning
}
https://github.com/dotnet/csharplang/blob/master/proposals/nullable-reference-types.md
https://github.com/dotnet/roslyn/blob/features/NullableReferenceTypes/docs/features/NullableReferenceTypes/README.md
async Task M()
{
foreach await (var i in GetStream())
WriteLine(i);
}
IAsyncEnumerable<int> GetStream() => new[] { 1, 2, 3 }.ToAsyncEnumerable();
async IAsyncEnumerable<int> MyIterator()
{
for (int i = 0; i < 100; i++)
{
await Task.Delay(1000);
yield return i;
}
}
https://github.com/dotnet/csharplang/blob/master/proposals/async-streams.md
using await (IAsyncDisposable resource = await store.GetRecordAsync(…)) { … }
class Foo : IAsyncDisposable
{
public async Task DisposeAsync()
{
await ...
}
}
https://github.com/dotnet/csharplang/blob/master/proposals/async-streams.md
interface I
{
void M() { WriteLine("I.M"); }
}
class C : I { } // OK
void M()
{ I i = new C();
i.M(); // prints "I.M"
}
https://github.com/dotnet/csharplang/blob/master/proposals/default-interface-methods.md
Dictionary<string, List<int>> field =
new() { { "item1", new() { 1, 2, 3 } } };
XmlReader.Create(reader,
new() { IgnoreWhitespace = true });
https://github.com/dotnet/csharplang/blob/master/proposals/target-typed-new.md
// list[Index.CreateFromEnd(1)]
var ultimoItem = list[^1];
// list[Index.CreateFromEnd(2)]
var penultimoItem = list[^2];
// list[3, Index.CreateFromEnd(2)]
var multiDimensional = list[3, ^2];
https://github.com/dotnet/csharplang/blob/master/proposals/ranges.md
// list[Range.Create(2, Index.CreateFromEnd(3))]
var slice1 = list[2..^3];
// list[Range.ToEnd(Index.CreateFromEnd(3))]
var slice2 = list[..^3];
// list[Range.FromStart(2)]
var slice3 = list[2..];
// list[Range.All]
var slice4 = list[..];
// list[Range.Create(1, 2), Range.All]
var multiDimensional = list[1..2, ..]
https://github.com/dotnet/csharplang/blob/master/proposals/ranges.md
class Person : IEquatable<Person>
{
public string First { get; }
public string Last { get; }
public Person(string First, string Last) => (this.First, this.Last) = (First, Last);
public void Deconstruct(out string First, out string Last)
=> (First, Last) = (this.First, this.Last);
public bool Equals(Person other)
=> other != null && First == other.First && Last == other.Last;
public override bool Equals(object obj) => obj is Person other ? Equals(other) : false;
public override int GetHashCode() => GreatHashFunction(First, Last);
…
}
class Person(string First, string Last);
https://github.com/dotnet/csharplang/blob/master/proposals/records.md
extension class Enrollee extends Person
{
// static field
static Dictionary<Person, Professor> enrollees = new Dictionary<Person, Professor>();
// instance method
public void Enroll(Professor supervisor) { enrollees[this] = supervisor; }
// instance property
public Professor Supervisor => enrollees.TryGetValue(this, out var supervisor) ? supervisor : null;
// static property
public static ICollection<Person> Students => enrollees.Keys;
// instance constructor
public Person(string name, Professor supervisor) : this(name) { this.Enroll(supervisor); }
}
https://github.com/dotnet/csharplang/issues/192
public shape SGroup<T>
{
static T operator +(T t1, T t2);
static T Zero { get; }
}
https://github.com/dotnet/csharplang/issues/164
public static AddAll<T>(T[] ts) where T : SGroup<T>
{
var result = T.Zero;
foreach (var t in ts) { result += t; }
return result;
}
https://github.com/dotnet/csharplang/issues/164
public shape SGroup<T>
{
static T operator +(T t1, T t2);
static T Zero { get; }
}
https://github.com/dotnet/csharplang/issues/164
w w w. l a m b d a 3 . c o m . b r
Obrigado!
Giovanni Bassi
@giovannibassi
/giggio.tech

TDC2018SP | Trilha .Net - Novidades do C# 7 e 8

  • 1.
    w w w.l a m b d a 3 . c o m . b r Novidades do C# 7 e 8
  • 2.
    Giovanni Bassi • Programador •Microsoft MVP • Não gerente • blog.lambda3.com.br, podcast.lambda3.com.br, dotnetarchitects.net, nodebr, dockersp @giovannibassi /giggio.tech • Escalador e ciclista
  • 4.
    podcast.lambda3.com.br 0 – Eventos 1– Docker 2 – .NET Core RC2 3 – Git 4 – Estudo 5 – Open Source 6 – Xamarin 7 – Node.js 8 – Democracia organizacional 9 – O programador poliglota ... Toda semana em:
  • 6.
    Acreditamos que adiversidade, em suas diversas expressões, deve ser respeitada e valorizada, e que uma sociedade mais diversa é uma sociedade melhor. diversidade.tech
  • 7.
  • 8.
  • 9.
  • 10.
    Código gerenciado Generics Language IntegratedQuery Dinamismo + paridade nas linguagens C# 5 + VB 11 Programação assíncrona C# 1 + VB 7 C# 2 + VB 8 C# 3 + VB 9 C# 4 + VB 10 C# 6 + VB 14 Roslyn
  • 11.
    Código gerenciado Generics Language IntegratedQuery Dinamismo + paridade nas linguagens C# 5 + VB 11 Programação assíncrona C# 1 + VB 7 C# 2 + VB 8 C# 3 + VB 9 C# 4 + VB 10 C# 6 + VB 14 Roslyn C# 7 + VB 15 Features!
  • 12.
    Código gerenciado Generics Language IntegratedQuery Dinamismo + paridade nas linguagens C# 5 + VB 11 Programação assíncrona C# 1 + VB 7 C# 2 + VB 8 C# 3 + VB 9 C# 4 + VB 10 C# 6 + VB 14 Roslyn C# 7 + VB 15 Features! Features! Features!Features!
  • 13.
    Código gerenciado Generics Language IntegratedQuery Dinamismo + paridade nas linguagens C# 5 + VB 11 Programação assíncrona C# 1 + VB 7 C# 2 + VB 8 C# 3 + VB 9 C# 4 + VB 10 C# 6 + VB 14 Roslyn C# 7 + VB 15 Features! Features! Features! Features (parte 2)? C# 8 + VB 16?
  • 14.
  • 15.
  • 16.
    F# 10,000’s Tens of thousands VB 100,000’s Hundredsof thousands C# 1,000,000’s Millions
  • 18.
  • 19.
  • 20.
  • 21.
    public static intDiceSum(IEnumerable<object> values) { var sum = 0; foreach (var item in values) { if (item is int val) sum += val; else if (item is IEnumerable<object> subList) sum += DiceSum(subList); } return sum; } https://docs.microsoft.com/dotnet/csharp/pattern-matching http://bit.ly/cspatternmatching
  • 22.
    https://docs.microsoft.com/dotnet/csharp/pattern-matching public static intDiceSum(IEnumerable<object> values) { var sum = 0; foreach (var item in values) { switch (item) { } } return sum; } http://bit.ly/cspatternmatching
  • 23.
    case 0: break; case intval: sum += val; break; case PercentileDie die: sum += die.Multiplier * die.Value; break; https://docs.microsoft.com/dotnet/csharp/pattern-matching public static int DiceSum(IEnumerable<object> values) { var sum = 0; foreach (var item in values) { switch (item) { } } return sum; } http://bit.ly/cspatternmatching
  • 24.
    case 0: break; case intval: sum += val; break; case PercentileDie die: sum += die.Multiplier * die.Value; break; https://docs.microsoft.com/dotnet/csharp/pattern-matching case IEnumerable<object> subList when subList.Any(): sum += DiceSum(subList); break; case IEnumerable<object> subList: break; case null: break; default: throw new InvalidOperationException("unknown"); public static int DiceSum(IEnumerable<object> values) { var sum = 0; foreach (var item in values) { switch (item) { } } return sum; } http://bit.ly/cspatternmatching
  • 25.
    var numbers1 =(1, 2); var objs = ("1", 2); WriteLine($"{numbers1.Item1}, {numbers1.Item2}"); (int one, int two) numbers2 = (1, 2); WriteLine($"{numbers2.Item1}, {numbers2.Item2}"); WriteLine($"{numbers2.one}, {numbers2.two}"); var numbers3 = (one: 1, two: 2); WriteLine($"{numbers3.one}, {numbers3.two}"); (int uno, int due) numbers4 = (one: 1, two: 2); WriteLine($"{numbers4.uno}, {numbers4.due}"); // WriteLine($"{numbers4.one}, {numbers4.two}"); // error https://docs.microsoft.com/dotnet/csharp/tuples http://bit.ly/cstuplas
  • 26.
    void M() { var (name,company) = Person.Get(); WriteLine($"{name}, {company}"); } class Person { public string Name { get; set; } public string Company { get; set; } public void Deconstruct(out string name, out string company) { name = Name; company = Company; } public static Person Get() => new Person { Name = "…", Company = "…" }; } https://docs.microsoft.com/dotnet/csharp/tupleshttp://bit.ly/cstuplas
  • 27.
    var numbers =(one: 1, two: 2); var (uno, due) = numbers; WriteLine($"{uno}, {due}"); https://docs.microsoft.com/dotnet/csharp/tuples http://bit.ly/cstuplas
  • 28.
    if (int.TryParse("1", outint result)) WriteLine(result); if (int.TryParse("1", out var result2)) WriteLine(result2); https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7#out-variables http://bit.ly/csoutvars
  • 29.
    (int x, inty) GetCoordinates() => (1, 2); var (_, y) = GetCoordinates(); https://docs.microsoft.com/dotnet/csharp/discards http://bit.ly/csdiscards
  • 30.
    void ObjectDeconstruction() { var (name,_) = Person.Get(); } https://docs.microsoft.com/dotnet/csharp/discards http://bit.ly/csdiscards
  • 31.
    if (int.TryParse("1", out_)) WriteLine("It is a number!"); https://docs.microsoft.com/dotnet/csharp/discards http://bit.ly/csdiscards
  • 32.
    object o =1; if (o is Person p) WriteLine(p.Company); else if (o is null) WriteLine("null"); else if (o is var _) WriteLine("Unknown"); https://docs.microsoft.com/dotnet/csharp/discards http://bit.ly/csdiscards
  • 33.
    ref int Find(int[,]matrix, Func<int, bool> predicate) { for (int i = 0; i < matrix.GetLength(0); i++) for (int j = 0; j < matrix.GetLength(1); j++) if (predicate(matrix[i, j])) return ref matrix[i, j]; throw new InvalidOperationException("Not found"); } https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7#ref-locals-and-returns http://bit.ly/csreflocals
  • 34.
    void M() { var matrix= new [,] { { 1, 2, 3, 4, 5 }, { 6, 7, 8, 42, 10 } }; ref var item = ref Find(matrix, val => val == 42); WriteLine(item); item = 24; WriteLine(matrix[1, 3]); } https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7#ref-locals-and-returns http://bit.ly/csreflocals
  • 35.
    public ExpressionBodiedMembers(string name)=> Name = name; ~ExpressionBodiedMembers() => WriteLine("Finalized!"); private string name; public string Name { get => name; set => name = value; } https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7#more-expression-bodied-members http://bit.ly/csmaisebm
  • 36.
    private string a= GetA() ?? throw new Exception(); private static string GetA() => throw new Exception(); https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7#throw-expressions http://bit.ly/csexpthrow
  • 37.
    int sixteen =0b0001_0000; int thirtyTwo = 0b0010_0000; int sixtyFour = 0b0100_0000; int oneHundredTwentyEight = 0b1000_0000; long oneHundredBillions = 100_000_000_000; double AvogadroConstant = 6.022_140_857_747_474e23; decimal GoldenRatio = 1.618_033_988_749M; https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7#numeric-literal-syntax-improvements http://bit.ly/csnumlit
  • 38.
    double Average(IEnumerable<int> ns) { doubleDivide(double a, double b) { if (b == 0) throw new DivideByZeroException(); return a / b; } var sum = ns.Sum(); return Divide(sum, ns.Count()); } https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7#local-functions http://bit.ly/cslocalfunc
  • 39.
    void Add1AndSumAll(int[] ms) { IEnumerable<int>Sum1(IEnumerable<int> ns) { foreach (var n in ns) yield return n + 1; } if (ms == null) throw new NullReferenceException(); WriteLine(Sum1(ms).Sum()); } https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7#local-functions http://bit.ly/cslocalfunc
  • 40.
    public async ValueTask<int>Get5() { await Task.Delay(100); return 5; } https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7#generalized-async-return-types http://bit.ly/csasyncgeneral
  • 41.
    public async ValueTask<int>SumAsync(IEnumerable<ValueTask<int>> ns) { var sum = 0; foreach (var nTask in ns) { var n = await nTask; sum += n; } return sum; } https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7#generalized-async-return-types http://bit.ly/csasyncgeneral
  • 42.
  • 43.
    var x =(f1: 1, f2: 2); var tuple = (x.f1, x.f2, x); https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7-1#inferred-tuple-element-names http://bit.ly/cstuplainfer
  • 44.
    Func<string, bool> predicate= default; int i = default; N(default); https://docs.microsoft.com/dotnet/csharp/programming-guide/statements-expressions-operators/default-value-expressions http://bit.ly/csliteraldefault
  • 45.
    (int, int) N(int[]ns = default) => default; string O() { return default; } T P<T>() { T t = default; return t; } https://docs.microsoft.com/dotnet/csharp/programming-guide/statements-expressions-operators/default-value-expressions http://bit.ly/csliteraldefault
  • 46.
    static async Task<int>Main(string[] args) { await Task.Delay(500); WriteLine("Done"); return 0; } https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-7-1#async-main http://bit.ly/csasyncmain
  • 47.
  • 48.
    void M(int i,int j) { } void N() { M(i: 2, 3); } https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/non-trailing-named-arguments.md http://bit.ly/csntna
  • 49.
    var m =0b_101; //5 var n = 0x_00C0; // 192 https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/leading-separator.md http://bit.ly/cs_num
  • 50.
    class Base { private protectedint f; } class DerivedInSameAssembly : Base { void M() { var x = base.f; } } class DerivedInOtherAssembly : Base { void M() { var x = base.f; //error } } https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/private-protected.md http://bit.ly/csprpr
  • 51.
    Vector Add(in Vectorv1, in Vector v2) { v1 = default(Vector); // not OK!! v1.X = 0; // not OK!! Foo(ref v1.X); // not OK!! // OK: return new Vector(v1.X +v2.X, v1.Y + v2.Y); } https://docs.microsoft.com/dotnet/csharp/reference-semantics-with-value-types http://bit.ly/csmodin
  • 52.
    https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/readonly-ref.md#refin-extension-methods public static PontoSoma(in this Ponto p1, in Ponto p2) => new Ponto(p1.X + p2.X, p1.Y + p2.Y); void In() { Ponto p1 = new Ponto(1, 2), p2 = new Ponto(3, 4); var pResultado = p1.Soma(p2); var pResultado2 = p1.Soma(new Ponto(3, 4)); var pResultado3 = new Ponto().Soma(new Ponto(3, 4)); } http://bit.ly/csmodin
  • 53.
    public static PontoSubtrai(ref this Ponto p1, ref Ponto p2) => new Ponto(p1.X - p2.X, p1.Y - p2.Y); void Ref() { Ponto p1 = new Ponto(1, 2), p2 = new Ponto(3, 4); var pResultado = p1.Subtrai(ref p2); p1.Subtrai(ref new Ponto()); // não compila new Ponto().Subtrai(ref p1); // não compila } https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/readonly-ref.md#refin-extension-methods http://bit.ly/csextref
  • 54.
  • 55.
    ref readonly VectorFetchOrigin() => ref origin; void M() { var v1 = new Vector(1, 2, 3); var v2 = new Vector(4, 5, 6); var vSum = Add(v1, v2); ref readonly var origin = ref FetchOrigin(); } private readonly Vector origin = new Vector(0, 0, 0); https://docs.microsoft.com/dotnet/csharp/reference-semantics-with-value-types http://bit.ly/csretrefro
  • 56.
    ref readonly varorigin = ref FetchOrigin(); var origin2 = FetchOrigin(); // copy created https://docs.microsoft.com/dotnet/csharp/reference-semantics-with-value-types http://bit.ly/csretrefro
  • 57.
    readonly struct Ponto { publicPonto(float x, float y) { X = x; Y = y; } public float X { get; } public float Y { get; } private readonly static Ponto origem = new Ponto(); public static ref readonly Ponto Origem => ref origem; } https://docs.microsoft.com/dotnet/csharp/reference-semantics-with-value-types#readonly-struct-type http://bit.ly/csrostruct
  • 58.
    ref readonly varp = ref Ponto.Origem; var x = p.X; // sem cópia https://docs.microsoft.com/dotnet/csharp/reference-semantics-with-value-types#readonly-struct-type http://bit.ly/csrostruct
  • 59.
    ref int M() { vararr = new[] { 1, 2, 3 }; var otherArr = new[] { 4, 5, 6 }; ref var r = ref (arr != null ? ref arr[0]: ref otherArr[0]); Foo(ref (arr != null ? ref arr[0]: ref otherArr[0])); (arr != null ? ref arr[0]: ref otherArr[0]) = 1; return ref (arr != null ? ref arr[0]: ref otherArr[0]); } void Foo(ref int i) => i = 42; https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/conditional-ref.md http://bit.ly/csternarioref
  • 60.
    ref struct TwoSpans<T> { //pode ter campos desde que não sejam classes public Span<T> first; public Span<T> second; } // erro: arrays of ref-like types are not allowed. TwoSpans<int>[] arr = null; https://docs.microsoft.com/en-us/dotnet/csharp/reference-semantics-with-value-types#ref-struct-type
  • 61.
    readonly ref structReadOnlyRefPoint2D { public int X { get; } public int Y { get; } public ReadOnlyRefPoint2D(int x, int y) => (X, Y) = (x, y); } https://docs.microsoft.com/dotnet/csharp/reference-semantics-with-value-types#readonly-ref-struct-type
  • 62.
    void M() { Span<int> arr= stackalloc int[3]; } https://msdn.microsoft.com/en-us/magazine/mt814808.aspx Não é unsafe
  • 63.
  • 64.
    Span<int> RowFive =new Span<int>(PascalsTriangle, 10, 5); fixed (int* ptrToRow = RowFive) { // Soma os números 1,4,6,4,1 var sum = 0; for (int i = 0; i < RowFive.Length; i++) { sum += *(ptrToRow + i); } Console.WriteLine(sum); } https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/pattern-based-fixed.md int[] PascalsTriangle = { 1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1, 1, 5, 10, 10, 5, 1 }; Span<T>.GetPinnableReference()
  • 65.
    unsafe struct S { publicfixed int myFixedField[10]; } class C { private static S s = new S(); unsafe public void M() { int p = s.myFixedField[5]; } } https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7-3#indexing-fixed-fields-does-not-require-pinning // CSharp 7.2 fixed (int* ptr = s.myFixedField) { int p = ptr[5]; }
  • 66.
    var structs =new StructGrande[] { new StructGrande(), new StructGrande() }; ref StructGrande refLocal = ref structs[0]; refLocal = ref structs[1]; https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7-3#ref-local-variables-may-be-reassigned
  • 67.
    unsafe void M() { int*pArr = stackalloc int[3] { 1, 2, 3 }; int* pArr2 = stackalloc int[] { 1, 2, 3 }; Span<int> arr = stackalloc[] { 1, 2, 3 }; } https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7-3#stackalloc-arrays-support-initializers
  • 68.
    unsafe public staticbyte[] ToByteArray<T> (this T argument) where T : unmanaged { var size = sizeof(T); var result = new byte[size]; byte* p = (byte*)&argument; for (var i = 0; i < size; i++) result[i] = *p++; return result; } https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters#unmanaged-constraint
  • 69.
    public static TDelegateTypeSafeCombine<TDelegate> (this TDelegate source, TDelegate target) where TDelegate : System.Delegate => System.Delegate.Combine(source, target) as TDelegate; Action first = () => Console.WriteLine("this"); Action second = () => Console.WriteLine("that"); var combined = first.TypeSafeCombine(second); combined(); https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters#delegate-constraints
  • 70.
    public static Dictionary<int,string> EnumNamedValues<T>() where T : System.Enum { var result = new Dictionary<int, string>(); var values = Enum.GetValues(typeof(T)); foreach (int item in values) result.Add(item, Enum.GetName(typeof(T), item)); return result; } enum Rainbow { Red, Orange, Yellow, Green, Blue, Indigo, Violet } var map = EnumNamedValues<Rainbow>(); foreach (var pair in map) Console.WriteLine($"{pair.Key}:t{pair.Value}"); https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters#enum-constraints
  • 71.
  • 72.
    https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/expression-variables-in-initializers.md var query =from text in strings select int.TryParse(text, out int value) ? value : (int?)null;
  • 73.
    public class Foo { protectedFoo(out int i) => i = 1; } public class Bar : Foo { public int Value { get; } public Bar() : base(out int value) => Value = value; } https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/expression-variables-in-initializers.md
  • 74.
    public class Baz { privatereadonly int field1 = int.TryParse(SomeStatic.Text, out int value) ? Value : -1; } https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/expression-variables-in-initializers.md
  • 75.
  • 76.
  • 77.
    public class Cartesian{ public int X { get; } public int Y { get; } } public class Polar { public static bool operator is(Cartesian c, out double R, out double Theta) { R = Math.Sqrt(c.X * c.X + c.Y * c.Y); Theta = Math.Atan2(c.Y, c.X); return c.X != 0 || c.Y != 0; } } void M() { var c = new Cartesian(3, 4); if (c is Polar(var R, _)) WriteLine(R); } https://github.com/dotnet/csharplang/blob/master/proposals/patterns.md
  • 78.
    void N(string? aNullableString,bool condition) { WriteLine(aNullableString.Length); // warning if (aNullableString != null) WriteLine(aNullableString); // no warning if (!string.IsNullOrWhiteSpace(aNullableString)) WriteLine(aNullableString!.Length); // I know better var anotherNullableString = condition ? "Hello" : aNullableString; WriteLine(anotherNullableString.Length); // warning var yetAnotherNullableString = condition ? "Hello" : null; WriteLine(yetAnotherNullableString.Length); // warning string nonNullableString = null; //warning WriteLine(nonNullableString); // no warning } https://github.com/dotnet/csharplang/blob/master/proposals/nullable-reference-types.md https://github.com/dotnet/roslyn/blob/features/NullableReferenceTypes/docs/features/NullableReferenceTypes/README.md
  • 79.
    async Task M() { foreachawait (var i in GetStream()) WriteLine(i); } IAsyncEnumerable<int> GetStream() => new[] { 1, 2, 3 }.ToAsyncEnumerable(); async IAsyncEnumerable<int> MyIterator() { for (int i = 0; i < 100; i++) { await Task.Delay(1000); yield return i; } } https://github.com/dotnet/csharplang/blob/master/proposals/async-streams.md
  • 80.
    using await (IAsyncDisposableresource = await store.GetRecordAsync(…)) { … } class Foo : IAsyncDisposable { public async Task DisposeAsync() { await ... } } https://github.com/dotnet/csharplang/blob/master/proposals/async-streams.md
  • 81.
    interface I { void M(){ WriteLine("I.M"); } } class C : I { } // OK void M() { I i = new C(); i.M(); // prints "I.M" } https://github.com/dotnet/csharplang/blob/master/proposals/default-interface-methods.md
  • 82.
    Dictionary<string, List<int>> field= new() { { "item1", new() { 1, 2, 3 } } }; XmlReader.Create(reader, new() { IgnoreWhitespace = true }); https://github.com/dotnet/csharplang/blob/master/proposals/target-typed-new.md
  • 83.
    // list[Index.CreateFromEnd(1)] var ultimoItem= list[^1]; // list[Index.CreateFromEnd(2)] var penultimoItem = list[^2]; // list[3, Index.CreateFromEnd(2)] var multiDimensional = list[3, ^2]; https://github.com/dotnet/csharplang/blob/master/proposals/ranges.md
  • 84.
    // list[Range.Create(2, Index.CreateFromEnd(3))] varslice1 = list[2..^3]; // list[Range.ToEnd(Index.CreateFromEnd(3))] var slice2 = list[..^3]; // list[Range.FromStart(2)] var slice3 = list[2..]; // list[Range.All] var slice4 = list[..]; // list[Range.Create(1, 2), Range.All] var multiDimensional = list[1..2, ..] https://github.com/dotnet/csharplang/blob/master/proposals/ranges.md
  • 85.
    class Person :IEquatable<Person> { public string First { get; } public string Last { get; } public Person(string First, string Last) => (this.First, this.Last) = (First, Last); public void Deconstruct(out string First, out string Last) => (First, Last) = (this.First, this.Last); public bool Equals(Person other) => other != null && First == other.First && Last == other.Last; public override bool Equals(object obj) => obj is Person other ? Equals(other) : false; public override int GetHashCode() => GreatHashFunction(First, Last); … } class Person(string First, string Last); https://github.com/dotnet/csharplang/blob/master/proposals/records.md
  • 86.
    extension class Enrolleeextends Person { // static field static Dictionary<Person, Professor> enrollees = new Dictionary<Person, Professor>(); // instance method public void Enroll(Professor supervisor) { enrollees[this] = supervisor; } // instance property public Professor Supervisor => enrollees.TryGetValue(this, out var supervisor) ? supervisor : null; // static property public static ICollection<Person> Students => enrollees.Keys; // instance constructor public Person(string name, Professor supervisor) : this(name) { this.Enroll(supervisor); } } https://github.com/dotnet/csharplang/issues/192
  • 87.
    public shape SGroup<T> { staticT operator +(T t1, T t2); static T Zero { get; } } https://github.com/dotnet/csharplang/issues/164
  • 88.
    public static AddAll<T>(T[]ts) where T : SGroup<T> { var result = T.Zero; foreach (var t in ts) { result += t; } return result; } https://github.com/dotnet/csharplang/issues/164
  • 89.
    public shape SGroup<T> { staticT operator +(T t1, T t2); static T Zero { get; } } https://github.com/dotnet/csharplang/issues/164
  • 91.
    w w w.l a m b d a 3 . c o m . b r Obrigado! Giovanni Bassi @giovannibassi /giggio.tech