Что нового в C# 7

Уже прошло достаточно времени после выпуска C# версии 7. Visual Studio 2017 также стала достаточно стабильной. Думаю, можно начинать использовать возможности седьмой версии в реальных проектах. Давайте посмотрим, что же нового было добавлено.

 

Out-переменные (out variables)

Данное нововведение позволяет не объявлять out переменные заранее, до их непосредственной передачи в качестве аргумента в метод.

Например, у нас есть следующих код:

static void Main(string[] args)
{
    string firstName;
    string lastName;
    GetUserInfo(out firstName, out lastName);
    Console.WriteLine($"{firstName} {lastName}");
}

public static void GetUserInfo(out string firstName, out string lastName)
{
    firstName = "Андрей";
    lastName = "Амельченя";
}

Метод GetUserInfo позволяет получить какую-то информацию о пользователе, записав её в out переменные.

В C# 7 мы можем не объявлять переменные заранее и написать так:

static void Main(string[] args)
{
    GetUserInfo(out string firstName, out string lastName);
    Console.WriteLine($"{firstName} {lastName}");
}

public static void GetUserInfo(out string firstName, out string lastName)
{
    firstName = "Андрей";
    lastName = "Амельченя";
}

Указывать конкретный тип не обязательно, мы можем использовать var, как при объявлении переменных

GetUserInfo(out var firstName, out var lastName);

Сопоставление с образцом (Pattern matching)

Данный функционал уже давно ждали. Многие писали свою реализацию pattern matching, но вот, наконец-то, в C# это добавили официально.

Чтобы лучше понять, рассмотрим следующий код, в котором новые возможности еще не используются:

public class User
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
 
public class MobilePhone
{
    public string Phone { get; set; }
}
 
class Program
{
    public static string GetInfo(object o)
        {
            if (o == null) return null;
            if (o is User)
            {
                var user = (User)o;
                return user.FirstName;
            }
            if (o is MobilePhone)
            {
                var phone = (MobilePhone)o;
                return phone.Phone;
            }
            if (o is int)
            {
                var age = (int)o;
                return $"{age * 365} дней";
            }
            return String.Empty;
        }
 
    static void Main(string[] args)
    {
        var user = new User() {FirstName = "Андрей", LastName = "Амельченя"};
        var phone = new MobilePhone() {Phone = "+79150000000"};
        Console.WriteLine(GetInfo(user));
        Console.WriteLine(GetInfo(phone));
        Console.WriteLine(GetInfo(27));
    }
}

Используя возможности C#7 метод GetInfo примет вид:

public static string GetInfo(object o)
{
    if (o == null) return null;
    if (o is User user)
    {
        return user.FirstName;
    }
    if (o is MobilePhone phone)
    {
        return phone.Phone;
    }
    if (o is int age)
    {
        return (age * 365).ToString();
    }
    return String.Empty;
}

Также можно воспользоваться switch. Используя ключевое слово when можно добавить дополнительные проверки

public static string GetInfo(object o)
{
    if (o == null) return null;
    switch (o)
    {
        case User user when user.FirstName == "Root":
            return null;
        case User user:
            return user.FirstName;
        case MobilePhone phone:
            return phone.Phone;
        case int age when age > 100:
            return "Максимальный возраст превышен";
        case int age:
            return $"{age * 365} дней";
    }
    return String.Empty;
}

Кортежи (tuples)

Наконец-то с кортежами стало удобно работать! Рассмотрите код, как приходилось писать раньше:

public static Tuple<int, int, int> GetTime()
{
    return new Tuple<int, int, int>(14, 15, 0);
}

static void Main(string[] args)
{
    var time = GetTime();
    Console.WriteLine($"{time.Item1}:{time.Item2}:{time.Item3}");
}

Обилие этих Item1, Item2…порой подбишивало, ведь в них легко запутаться.

Сейчас данный код можно свести к этому:

public static (int hour, int minutes, int seconds) GetTime()
{
    return (14, 15, 0);
}

static void Main(string[] args)
{
    var time = GetTime();
    Console.WriteLine($"{time.hour}:{time.minutes}:{time.seconds}");
}

Но нужно не забыть подключить библиотеку System.ValueTuple

Дополнительно, мы легко можем произвести деконструкцию кортежа в локальные переменные, делается это так:

var (hour, minutes, seconds) = GetTime();
Console.WriteLine($"{hour}:{minutes}:{seconds}");

Локальные методы (local functions)

Вообще использовать локальные методы можно было и раньше, использую ламбда-переменные. Например, вот в таком виде:

var length = new Func<int, int, int, int, double>((x1,y1, x2, y2) => Math.Sqrt(Math.Pow(x1-x2,2)+Math.Pow(y1-y2,2)));
Console.WriteLine(length(0,0,5,5));

Но в версии 7 это стало удобнее:

double Length(int x1, int y1, int x2, int y2)
{
    return Math.Sqrt(Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2));
}
Console.WriteLine(Length(0, 0, 5, 5));

Еще стоит обратить внимание на то, что локальная функция может располагаться где угодно в методе. Её можно сначала вызывать, а уже потом объявлять:

Console.WriteLine(Length(0, 0, 5, 5));
double Length(int x1, int y1, int x2, int y2)
{
    return Math.Sqrt(Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2));
}

Здесь главное использовать это без фанатизма, когда действительно необходимо.

Возвращаемые значения по ссылке (ref returns)

Раньше значения значимого типа можно было передавать в методы по ссылке, используя ключевое слово ref. В седьмой версии добавлена возможность возвращать значения по ссылке:

static void Main(string[] args)
{
    var a = new[] {1, 2, 3, 4, 5};
    ref int element = ref FindElement(3, a);
    element = -50;
    Console.WriteLine(a[2]); //-50
}

public static ref int FindElement(int value, int[] array)
{
    for (int i = 0; i < array.Length; i++)
    {
        if (array[i] == value)
        {
            return ref array[i];
        }
    }
    throw new IndexOutOfRangeException("Value not found");
}

Бинарные литералы (binary literals)

Сейчас можно задавать бинарные значения в удобном виде непосредственно сразу в коде, записав 0b а затем бинарное значение, например:

var b = 0b100;//4

Также сейчас можно разделять цифры любым количеством подчеркиваний, если вам так удобнее, например:

var x = 100_000_000.0___01;
var y = 0b1000_0000_0000;

Лично мне нравится путь развития C#. В его добавляют всё больше функциональных возможностей и делают его удобнее для работы. В данном релизе я очень рад добавлению Pattern matching и изменению работы с кортежами. В общем, C# 7 уже можно использовать в product проектах, а пока ждём восьмую версию:)

Приятного программирования.

Добавить комментарий