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 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
4. 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)
5. 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 …
6. 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
• …
7. Esa presentación es de algo que está
en preview
Lo que está aquí puede cambiar… o
no xD
8. 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
9. 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)
10. 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
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 – 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
13. 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;
14. 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"};
15. 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); // ?????????
16. 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!
17. 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;
}
18. 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
};
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
• 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
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