C#9: Más C# que nunca!
Eduard Tomàs
@eiximenis
https://eiximenis.dev
¿Quien soy yo?
• Cervecero aficionado
• Desarrollador en Plain Concepts
• Microsoft MVP* desde 2012 hasta
la actualidad
• https://eiximenis.dev
* o eso espero, que hoy hay renovación xD
Un breve recorrido por la historia de C#
C# 1 Inspirado en Java: clases, interfaces, events, structs, delegates, properties,
attributes
C# 2 Genéricos, Tipos parciales, métodos anónimos, iteradores
C# 3
Lambdas, Auto-properties, Árboles de expresión, Inferencia de tipos, Métodos
de extensión, Tipos anónimos, query expressions, métodos parciales
C# 4 Dynamic, parámetros nombrados, varianza en genéricos
C# 5 async/await, Caller info attributes
Un breve recorrido por la historia de C#
C# 6 Null propagator (?.), auto property inits, expression bodied members,
interpolación de cadenas, nameof, static imports, index initializers
C# 7
Out variables, tuplas y deconstrucción, pattern matching, funciones locales,
property expression bodied, ref locals and returns
C# 7.1
async main, pattern matching on generic type parameters, tuple inferred
names
C# 7.2 in, ref readonly, readonly struct, ref struct, private protected access
C# 7.3
Focus en que el código seguro sea igual de rápido que el código inseguro, más
restricciones en genéricos (enum, delegate, unmanaged)
Un breve recorrido por la historia de C#
C# 8 Métodos readonly, default interface methods, más pattern matching, Índices y
rangos, operador ??=, unmanaged constructed types,
C# 9 …
Un breve recorrido por la historia de C#
C# 8 Métodos readonly, default interface methods, más pattern matching, Índices y
rangos, operador ??=, unmanaged constructed types,
C# 9 • Init-only properties and init accessors
• Records
• Top-Level programs
• More pattern matching
• Target-typed new, ?? and ?:
• Covariant returns
• …
Esa presentación es de algo que está
en preview
Lo que está aquí puede cambiar… o
no xD
Init properties
• Propiedades que solo se pueden inicializar al crear el objeto
public class Beer {
public string Name {get; init;}
}
var mahou = new Beer() { Name="Mahou"}
mahou.Name="Mahou 5 estrellas"; // ERROR!!!
👍 Evita constructores con muchas sobrecargas/parámetros
Records
• La GRAN novedad de C#9
• Definen objetos con semántica de valor (como las structs)
• Pensados para objetos inmutables (a diferencia de las structs)
Records – Semántica de valor
Semántica de valor (Equals y GetHashCode autogenerado)
A día de hoy operadores = y == se comportan con semántica de referencia
public record Beer {
public string Name {get; init;}
public double Abv {get; init;}
}
var mahou = new Beer() {Name = "Mahou", Abv=4.3};
var mahou2 = new Beer() {Name = "Mahou", Abv=4.3};
mahou.Equals(mahou2); // TRUE
Records - With
• Clona un record y modifica propiedades sobre el record clonado
public record Beer {
public string Name {get; init;}
public double Abv {get; init;}
}
var mahou = new Beer() {Name = "Mahou", Abv = 4.3};
var mahou5 = mahou with { Name = "Mahou 5 estrellas"};
Records – Constructor de copia
• Todos los records tienen un constructor de copia generado por el
compilador que es usado por with
public record Beer {
public string Name {get; init;}
public double Abv {get; init;}
}
public record Beer {
public string Name {get; init;}
public double Abv {get; init;}
public Beer(){}
protected Beer (Beer other) {Name = other.Name; Abv=other.Abv;}
}
• Lo podemos redefinir
Records – Parámetros posicionales
• Se pueden declarar parámetros posicionales que se pasan en el
constructor
• La deconstrucción se soporta automáticamente
public record Beer (string Name, double Abv);
var mahou = new Beer("Mahou", 5.4);
var (_,abv) = mahou;
Records – Parámetros posicionales
• Los parámetros posicionales son propiedades init
public record Beer (string Name="", double Abv=default);
var mahou = new Beer("Mahou", 5.4);
var estrella = new Beer("Estrella") {Abv=3.2};
var vollDamm = new Beer(Abv: 7.4) { Name = "Voll Damm"};
Records – Herencia e igualdad
• La herencia añade complejidad a la igualdad
• Si x.Equals(y) entonces debe cumplirse que y.Equals(x)
public record Beer {
public string Name {get; init;}
public double Abv {get; init;}
}
public record PricedBeer : Beer {
public decimal Price {get; init; }
}
var mahou = new Beer {Name="Mahou", Abv=4.3};
var mahouOferta = new PricedBeer {Name="Mahou", Abv=4.3, Price=1M};
mahou.Equals(mahouOferta); // ?????????
Records – Herencia e igualdad
• Desde el punto de vista de mahou, ambos objetos son iguales (tienen
mismo nombre y abv)
• Desde el punto de vista de mahouOferta ambos objetos son distintos
• Cada record tiene una propiedad
protected (Type EqualityContract)
que es usada por el método Equals
generado
• Ojo si sobreescribimos Equals!
Pattern Matching
public record Beer {
public string Name {get; init;}
public double Abv {get; init;}
}
int AlcoholicGrade (in Beer beer)
{
var alcoholicGrade = 0;
if (beer.Abv == 0.0) {alcoholicGrade=0;}
else if (beer.Abv > 0 && beer.Abv < 1) {alcoholicGrade=1;}
else if (beer.Abv >1 && beer.Abv < 5) {alcoholicGrade=2;}
else alcoholicGrade = 3;
return alcoholicGrade;
}
Pattern Matching
public record Beer {
public string Name {get; init;}
public double Abv {get; init;}
}
int AlcoholicGrade (in Beer beer) =>
beer.Abv switch {
0.0 => 0,
>0 and <1 => 1,
>1 and <5 => 2,
_ => 3
};
int AlcoholicGrade (in Beer beer) =>
beer.Abv switch {
0.0 => 0,
<1 => 1,
<5 => 2,
_ => 3
};
Pattern Matching – ¿Quien quiere ifs?
• Supongamos que el nivel de graduación es distinto para IPAs que para
otras cervezas…
public record Beer {
public string Name {get; init;}
public double Abv {get; init;}
}
public record IPA : Beer {}
int AlcoholicGrade (in Beer beer) =>
beer switch {
IPA ipa => ipa.Abv switch {
0.0 => 0,
>0 and <5 => 1,
>5 and <10 => 2,
_ => 3
},
Beer => beer.Abv switch {
0.0 => 0,
>0 and <1 => 1,
>1 and <5 => 2,
_ => 3
}
};
var impaled = new IPA() {Name="IMPALED", Abv=9.5};
var megaMahou = new Beer() {Name="Mahou", Abv=9.5};
var grade = AlcoholicGrade(impaled); // 2
var grade2 = AlcoholicGrade(megaMahou); // 3
Retornos covariantes
• Cuando se diseña una API puede dares el caso de querer sobreescribir
una función heredada pero cambiar el valor de retorno por uno más
especifico
class WebResponse { }
class WebRequest {
protected virtual WebResponse GetResponse() { … }
}
class FtpWebResponse : WebResponse { }
class FtpWebRequest : WebRequest {
protected override WebResponse GetResponse() { … }
}
Aquí nos intersaría poder devolver un FtpWebResponse pero estamos
obligados a devolver un WebResponse
Los clientes de FtpWebRequest siempre deben ir haciendo casts
Retornos covariantes permitirá eso
Otras pequeñas mejoras
• is not (por fin!! No más !(x is Foo))
• Target type new ( y de ?? y de ?: )
IPA impaled = new() {Name = "IMPALED", Abv=9.5};
() es necesario
• Top-Level programs (Main no necesario, ideal para Hello Worlds)
• ¡Y más… mucho más!
https://github.com/dotnet/roslyn/blob/master/docs/Language%20Feature%20Status.md
¡Gracias!
Eduard Tomàs
@eiximenis

C#9 - Más C# que nunca

  • 1.
    C#9: Más C#que nunca! Eduard Tomàs @eiximenis https://eiximenis.dev
  • 2.
    ¿Quien soy yo? •Cervecero aficionado • Desarrollador en Plain Concepts • Microsoft MVP* desde 2012 hasta la actualidad • https://eiximenis.dev * o eso espero, que hoy hay renovación xD
  • 3.
    Un breve recorridopor la historia de C# C# 1 Inspirado en Java: clases, interfaces, events, structs, delegates, properties, attributes C# 2 Genéricos, Tipos parciales, métodos anónimos, iteradores C# 3 Lambdas, Auto-properties, Árboles de expresión, Inferencia de tipos, Métodos de extensión, Tipos anónimos, query expressions, métodos parciales C# 4 Dynamic, parámetros nombrados, varianza en genéricos C# 5 async/await, Caller info attributes
  • 4.
    Un breve recorridopor la historia de C# C# 6 Null propagator (?.), auto property inits, expression bodied members, interpolación de cadenas, nameof, static imports, index initializers C# 7 Out variables, tuplas y deconstrucción, pattern matching, funciones locales, property expression bodied, ref locals and returns C# 7.1 async main, pattern matching on generic type parameters, tuple inferred names C# 7.2 in, ref readonly, readonly struct, ref struct, private protected access C# 7.3 Focus en que el código seguro sea igual de rápido que el código inseguro, más restricciones en genéricos (enum, delegate, unmanaged)
  • 5.
    Un breve recorridopor la historia de C# C# 8 Métodos readonly, default interface methods, más pattern matching, Índices y rangos, operador ??=, unmanaged constructed types, C# 9 …
  • 6.
    Un breve recorridopor la historia de C# C# 8 Métodos readonly, default interface methods, más pattern matching, Índices y rangos, operador ??=, unmanaged constructed types, C# 9 • Init-only properties and init accessors • Records • Top-Level programs • More pattern matching • Target-typed new, ?? and ?: • Covariant returns • …
  • 7.
    Esa presentación esde algo que está en preview Lo que está aquí puede cambiar… o no xD
  • 8.
    Init properties • Propiedadesque solo se pueden inicializar al crear el objeto public class Beer { public string Name {get; init;} } var mahou = new Beer() { Name="Mahou"} mahou.Name="Mahou 5 estrellas"; // ERROR!!! 👍 Evita constructores con muchas sobrecargas/parámetros
  • 9.
    Records • La GRANnovedad de C#9 • Definen objetos con semántica de valor (como las structs) • Pensados para objetos inmutables (a diferencia de las structs)
  • 10.
    Records – Semánticade valor Semántica de valor (Equals y GetHashCode autogenerado) A día de hoy operadores = y == se comportan con semántica de referencia public record Beer { public string Name {get; init;} public double Abv {get; init;} } var mahou = new Beer() {Name = "Mahou", Abv=4.3}; var mahou2 = new Beer() {Name = "Mahou", Abv=4.3}; mahou.Equals(mahou2); // TRUE
  • 11.
    Records - With •Clona un record y modifica propiedades sobre el record clonado public record Beer { public string Name {get; init;} public double Abv {get; init;} } var mahou = new Beer() {Name = "Mahou", Abv = 4.3}; var mahou5 = mahou with { Name = "Mahou 5 estrellas"};
  • 12.
    Records – Constructorde copia • Todos los records tienen un constructor de copia generado por el compilador que es usado por with public record Beer { public string Name {get; init;} public double Abv {get; init;} } public record Beer { public string Name {get; init;} public double Abv {get; init;} public Beer(){} protected Beer (Beer other) {Name = other.Name; Abv=other.Abv;} } • Lo podemos redefinir
  • 13.
    Records – Parámetrosposicionales • Se pueden declarar parámetros posicionales que se pasan en el constructor • La deconstrucción se soporta automáticamente public record Beer (string Name, double Abv); var mahou = new Beer("Mahou", 5.4); var (_,abv) = mahou;
  • 14.
    Records – Parámetrosposicionales • Los parámetros posicionales son propiedades init public record Beer (string Name="", double Abv=default); var mahou = new Beer("Mahou", 5.4); var estrella = new Beer("Estrella") {Abv=3.2}; var vollDamm = new Beer(Abv: 7.4) { Name = "Voll Damm"};
  • 15.
    Records – Herenciae igualdad • La herencia añade complejidad a la igualdad • Si x.Equals(y) entonces debe cumplirse que y.Equals(x) public record Beer { public string Name {get; init;} public double Abv {get; init;} } public record PricedBeer : Beer { public decimal Price {get; init; } } var mahou = new Beer {Name="Mahou", Abv=4.3}; var mahouOferta = new PricedBeer {Name="Mahou", Abv=4.3, Price=1M}; mahou.Equals(mahouOferta); // ?????????
  • 16.
    Records – Herenciae igualdad • Desde el punto de vista de mahou, ambos objetos son iguales (tienen mismo nombre y abv) • Desde el punto de vista de mahouOferta ambos objetos son distintos • Cada record tiene una propiedad protected (Type EqualityContract) que es usada por el método Equals generado • Ojo si sobreescribimos Equals!
  • 17.
    Pattern Matching public recordBeer { public string Name {get; init;} public double Abv {get; init;} } int AlcoholicGrade (in Beer beer) { var alcoholicGrade = 0; if (beer.Abv == 0.0) {alcoholicGrade=0;} else if (beer.Abv > 0 && beer.Abv < 1) {alcoholicGrade=1;} else if (beer.Abv >1 && beer.Abv < 5) {alcoholicGrade=2;} else alcoholicGrade = 3; return alcoholicGrade; }
  • 18.
    Pattern Matching public recordBeer { public string Name {get; init;} public double Abv {get; init;} } int AlcoholicGrade (in Beer beer) => beer.Abv switch { 0.0 => 0, >0 and <1 => 1, >1 and <5 => 2, _ => 3 }; int AlcoholicGrade (in Beer beer) => beer.Abv switch { 0.0 => 0, <1 => 1, <5 => 2, _ => 3 };
  • 19.
    Pattern Matching –¿Quien quiere ifs? • Supongamos que el nivel de graduación es distinto para IPAs que para otras cervezas… public record Beer { public string Name {get; init;} public double Abv {get; init;} } public record IPA : Beer {} int AlcoholicGrade (in Beer beer) => beer switch { IPA ipa => ipa.Abv switch { 0.0 => 0, >0 and <5 => 1, >5 and <10 => 2, _ => 3 }, Beer => beer.Abv switch { 0.0 => 0, >0 and <1 => 1, >1 and <5 => 2, _ => 3 } }; var impaled = new IPA() {Name="IMPALED", Abv=9.5}; var megaMahou = new Beer() {Name="Mahou", Abv=9.5}; var grade = AlcoholicGrade(impaled); // 2 var grade2 = AlcoholicGrade(megaMahou); // 3
  • 20.
    Retornos covariantes • Cuandose diseña una API puede dares el caso de querer sobreescribir una función heredada pero cambiar el valor de retorno por uno más especifico class WebResponse { } class WebRequest { protected virtual WebResponse GetResponse() { … } } class FtpWebResponse : WebResponse { } class FtpWebRequest : WebRequest { protected override WebResponse GetResponse() { … } } Aquí nos intersaría poder devolver un FtpWebResponse pero estamos obligados a devolver un WebResponse Los clientes de FtpWebRequest siempre deben ir haciendo casts Retornos covariantes permitirá eso
  • 21.
    Otras pequeñas mejoras •is not (por fin!! No más !(x is Foo)) • Target type new ( y de ?? y de ?: ) IPA impaled = new() {Name = "IMPALED", Abv=9.5}; () es necesario • Top-Level programs (Main no necesario, ideal para Hello Worlds) • ¡Y más… mucho más! https://github.com/dotnet/roslyn/blob/master/docs/Language%20Feature%20Status.md
  • 22.