Перейти к содержанию

Обзор C# 9

Кратко перечислим самые главные нововведения в C# 9.

Init-only свойства

В C# 9 был представлен новый метод изменения свойства – init. Идея в том, чтобы значение свойства можно было задать один раз, при объектной инициализации (object initialization).

Записи (records)

Новое ключевое слово record. Позволяет создать неизменяемый объект ссылочного типа с поведением, как у объекта value-типа. Свойства можно задать как через init модификатор, так и через конструктор.

public record Product
{
    string Name { get; init; }
    int CategoryId { get; init; }
}
//или
public record Product(string Name, int CategoryId);

При этом инициализация записи возможна как через конструктор, так и через объектную инициализацию. Присваивание происходит по значению, с созданием новой копии. При этом существует специальный синтаксис для измения только некоторых свойств записи.
var product = new Product {Name = "VideoGame", CategoryId = 1};
var newProduct = product with {CategoryId = 2};

Метод Equals() у записей переопределен - сравнение происходит по значению полей (как у struct).
var product = new Product {Name = "VideoGame", CategoryId = 1};
var anotherProduct = new Product {Name = "VideoGame", CategoryId = 1};
product.Equals(anotherProduct); //вернёт true

В целом, record полезен при определении DTO, так как обеспечивает неизменяемость и сравнение по значению из коробки.

Улучшенный pattern matching

Pattern matching улучшился настолько, что позволяет использовать операции сравнения, а также булевы операторы and, or и not, а также комбинировать их.

private static int GetTax(Product p) => p.CategoryId switch
{
    0 or 1 => 0,
    > 1 and < 5 => 5,
    > 20 => 15,
    _ => 10
}

Оператор not также можно использовать в выражении if (а также в тернарном выражении)
//if
if(p is not ElectronicProduct)
    return 25;

//тернарное выражение
private static int GetDiscountTernary (Product p) => p is not ElectronicProduct ? 25 : 0;

Улучшенное выведение типа

Теперь, можно не указывать тип в момент создания экземпляра (то есть справа от слова new), если тип можно вывести. Например, он указан слева от имени переменной (не используется var). То же применимо и к условному оператору. Например

//Пусть HeadSet и Book унаследованы от Product.
Book aBook = new ("gRPC", 1);
HeadSet headset = new ("Logitech", 2);
Product anotherProduct = aBook ?? headset;

Ковариантный return

Одной из самых недооценённых фич C# 9 является ковариантный return. Обычно в C#, когда вы унаследуетесь от класса, вы можете переопределить (override) метод, если он объявлен абстрактным или виртуальным, но вы не можете сменить тип возвращаемого значения. Начиная с C# 9, вы можете вернуть ковариантный тип от изначального типа.
В примере тип Book, унаследованный от Product, в котором есть абстрактный метод Order, возвращающий ProductOrder, переопределяет метод Order так, что он начинает возвращать тип BookOrder, унаследованный от ProductOrder.

public abstract class Product
{
    protected string Name { get; }
    protected int Id { get; }

    protected Product(string name, int id)
    {
        Name = name;
        Id = id;
    }
    public abstract ProductOrder Order(int quantity);
}
public class Book : Product
{
    public string ISBN { get; }
    public Book(string name, int id, string isbn) : base(name, id)
    {
        ISBN = isbn;
    }
    //Тут переопределяем Order
    public override BookOrder Order(int quantity) => new BookOrder {Quantity = quantity, Product = this};
}
public class ProductOrder
{
    public int Quantity { get; set; }
}
public class BookOrder : ProductOrder
{
    public Book Product { get; set; }
}

Статические лямбды

В C# 9 появилась возможность делать лямбды статическими. Это позволяет уменьшить аллокации. В следующем примере переменная _text захватывается лямбдой, что приводит к дополнительным аллокациям памяти.

class Program
{
    private string _text = "{0} is a beautiful product!";
    static void Main()
    {
        PromoteProduct(product => string.Format(this._text, "Surface book 3"));
    }
    private void PromoteProduct(Func<string, string> func)
    {
        Console.WriteLine(func(country));
    }
}

В C*9*{: #9 .hash} можно исправить это, сделав _text константой, а лямбду пометить ключевым словом static.
class Program
{
    private const string _text = "{0} is a beautiful product!";
    static void Main()
    {
        PromoteProduct(static product => string.Format(this._text, "Surface book 3"));
    }
    private void PromoteProduct(Func<string, string> func)
    {
        Console.WriteLine(func(country));
    }
}

Программы верхнего уровня

Программа верхнего уровня (top-level program) - фича C# 9, позволяющая написать более лёгковесный код в файле Program.cs, отбрасывая весь обрамляющий код - определение пространства имён, объявление класса Program и метода Main.


Последнее обновление : 2 декабря 2023 г.
Дата создания : 6 апреля 2023 г.

Комментарии

Комментарии