По каким-то причинам мы часто не используем этот функционал.
Возможно, мы еще не успели к этому привыкнуть.
И иногда мы его используем, даже не догадываясь, что это функционал из F#.
Прежде чем перейти к его рассмотрению, давайте быстро пробежимся по небольшой ретроспективе наиболее интересных возможностей, появившихся с годами в разных версиях языка.
Обратите внимание, что каждый раз новая версия языка выпускается вместе с новой версией Visual Studio. Для кого-то это может быть очевидно, но даже для разработчиков, работающих с C# несколько лет, это может быть новостью (не все обращают на это внимание).
Ретроспектива С# 1.0 Visual Studio 2002 C# 1.1 Visual Studio 2003 — #line, pragma, комментарии к документу xml С# 2.0 Visual Studio 2005 – Дженерики , Анонимные методы, итераторы/доходность, статические классы С# 3.0 Visual Studio 2008 – ЛИНК , Лямбда-выражения, Неявная типизация, Методы расширения C# 4.0 Visual Studio 2010 — динамические, необязательные параметры и именованные аргументы С# 5.0 Visual Studio 2012 — асинхронный/ожидание , Информация о вызывающем абоненте, некоторые важные изменения C# 6.0 Visual Studio 2015 — операторы с нулевым условием, интерполяция строк С# 7.0 Visual Studio 2017 — Кортежи , Сопоставление с образцом, Локальные функции Какой-то функционал используется довольно редко, а какой-то используется постоянно.
Скажем, даже сейчас довольно часто можно встретить использование OnPropertyChanged с указанным именем свойства.
То есть что-то вроде OnPropertyChanged("Цена"); Хотя уже с 5-й версии языка появилась возможность получить имя вызываемого объекта с помощью CallerMemberName.
Логирование можно организовать аналогичным образом.public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged([CallerMemberName] string prop = "") { PropertyChanged?.
Invoke(this, new PropertyChangedEventArgs(prop)); }
Тот же атрибут CallerMemberName поможет вам получить имя метода, из которого происходит вызов.
Таким же образом вы можете получить имя файла и номер строки, используя [CallerFilePath] и [CallerLineNumber].
Кстати, эти атрибуты можно использовать и в F#, но речь не о них.
Говоря о новых возможностях, появившихся в C#, в последнее время нельзя не упомянуть «вмешательство» F#, которое началось начиная с 6-й версии языка.
Все началось со всеми любимого LINQ. Вот список лишь некоторых функций, появившихся в C#: LINQ, неизменяемость, фильтры исключений, автоматические инициализаторы свойств, функциональные члены с телом выражения, сопоставление с образцом, кортежи.
Судя по всему, даже если вы не начнете изучать F# в ближайшее время, функциональное программирование вскоре станет для вас немного знакомым.
Давайте рассмотрим несколько «функциональных» особенностей C#.
Неизменяемость
Это не что иное, как неизменяемость объектов.То есть значение объекта нельзя изменить после его создания.
В F# все переменные по умолчанию неизменяемы.
Как это можно реализовать на C#? Начиная с версии 6, вы можете создавать свойства, доступные только для чтения, без указания set. Например, вот так: public string Name { get;}
Таким образом становится возможным комфортно создавать собственные неизменяемые объекты.
Фильтры исключений
А это возможность при «ловле» ошибок указать параметр, при котором будет работать захват. Например, вот так: try
{
SomeMethod(param);
}
catch (Exception e) when (param == null)
{
}
catch (Exception e)
{
}
В первом блоке ошибка будет обнаружена только в том случае, если param==null. Во вторую будут включены ошибки, произошедшие со значениями параметров, отличными от нуля.
Автоматические инициализаторы свойств
Это возможность инициализировать свойство сразу после средств доступа.
Например, вот так: public string AppUrl { get; set; } = " http://lalala.com ";
Или вы даже можете перенести инициализацию в отдельный метод, возвращающий значение.
public string AppUrl { get; set; } = InitializeProperty();
public static string InitializeProperty()
{
return " http://lalala.com ";
}
Функциональные члены с телом выражения
Удобный способ сократить код с помощью лямбда-выражений.
Методы, которые раньше были написаны так: public int Sum(int x, int y)
{
return x+y;
}
public string ServerIP { get { return "65.23.135.201"; } }
Теперь мы можем написать это гораздо короче: public int Sum(int x, int y) => x+y;
public string ServerIP => "65.23.135.201";
Все перечисленные выше возможности появились в шестой версии C#.
Теперь давайте немного подробнее рассмотрим, что пришло из F# в 7-й версии языка.
Кортежи или кортежи
Этот функционал, по мнению многих, по важности и простоте использования можно сравнить с LINQ. Что это нам дает само по себе? Прежде всего, это возможность возвращать из метода несколько значений без создания экземпляра какого-либо класса.Потому что мы можем передать методу несколько параметров, но вернуть можем только один.
Опять же, до сих пор типичным решением этой проблемы было создание экземпляра класса.
Теперь мы можем создать кортеж.
Типичный пример кортежа: var unnamed = (35, "What is your age?");
Как видите, это не что иное, как переменная, содержащая два значения в круглых скобках.
В этом случае кортеж называется безымянным и к значениям можно получить доступ по имени Item с номером.
Например, unnamed.Item1 содержит 35, а unnamed.Item2 содержит строку с текстом «Какой ваш возрастЭ» Если кто-то заметит, что кортежи похожи на анонимные типы, то он будет прав.
Но есть нюанс, о котором не следует забывать.
Анонимные типы можно использовать только в области метода.
Существуют именованные кортежи.
var named = (Answer: 35, Question: "What’s your age again?");
Доступ к переменным из именованного кортежа можно получить по имени.
В этом случае в Name.Answer хранится 35, а в Name.Question — строка с текстом вопроса.
Самый простой пример.
Метод, возвращающий значение в виде кортежа: (int, string) GetXY()
{
int x = 1;
string y = "One";
return (x, y);
}
Возвращаемое значение метода — кортеж, первое значение которого — целое число, а второе — строка.
Мы получаем значение в переменную следующим образом: var xy = GetXY();
Теперь мы можем получить доступ к элементам кортежа с помощью xy.Item1 и xy.Item2.
У кортежей есть интересная функция, называемая деконструкцией.
Это получение значений из кортежа обычными переменными.
Ну или можно сказать разложение кортежа на отдельные переменные.
Например, есть метод, который возвращает список и некоторый идентификатор, связанный с этим списком: (int, List<string>) GetListWithId()
{
int x = 1;
List<string> y = new List<string>();
return (x, y);
}
Чтобы получить значения непосредственно как переменные, а не как кортеж, вы можете использовать один из следующих двух методов: (int x, List<string> y) = GetListWithId();
var (x, y) = GetXY();
В C# версии 7.1 появилась функция, называемая выводом имен кортежей.
На русский это можно перевести примерно так: предполагаемые имена кортежей.
Это значит, что к безымянному элементу кортежа можно будет обращаться не только по Item1, Item2 и т.д., но и по имени, которое формируется на основе имени переменной, принимавшей участие в создании кортежа.
Пример: var tuple = (x.a, y);
Доступ к элементам этого кортежа можно получить по именам tuple.a и tuple.y.
Что интересно, это изменение 7.1 нарушает совместимость.
То есть обратно несовместимо.
Но поскольку разрыв между выпуском C#7 и C#7.1 небольшой, решили добавить его.
Дело в том.
Некоторый код, который работал определенным образом в C# 7, будет работать по-другому в C# 7.1. В этом случае вероятность встретить такой код в реальном проекте крайне мала.
Пример.
Допустим, у вас есть этот код: int x=1;
Action y = () => SomeMethod();
var tuple = (a: x, y);
tuple.y();
Обратите внимание на последнюю строку, которая, очевидно, вызывает метод SomeMethod (и он вызывает этот метод, но только начиная с C# 7.1).
Итак, вот оно.
В C# 7.0 вызывался бы не SomeMethod, а метод расширения с именем y. Скажем так: public static class ExtClass
{
public static void y(this (int, Action) z)
{
// some code
}
}
Согласитесь, что методы расширения кортежей встречаются редко.
Если вы используете проект с версией .
NET Framework ниже 4,7, то кортежи можно использовать только путем установки пакета NuGet System.ValueTuple. Однако Visual Studio 2017 должна предложить вам сделать это, если вы начнете использовать синтаксис кортежа.
Возможно, именно поэтому кортежи до сих пор используются не так часто.
Вы должны использовать не только последнюю версию Visual Studio, но и одну из последних версий платформы (или установить пакет NuGet).
Сопоставление с образцом
Это довольно неоднозначное название содержит на удивление довольно простой функционал.
Давайте посмотрим на пример кода, который использовался раньше и, безусловно, может быть использован сейчас: if (someObject is Customer)
{
var c = (Customer)someObject;
c.Balance = c.Balance + 1000;
}
Код совершенно нормальный, типовой, часто используемый, и нельзя сказать, что с ним что-то не так.
Однако теперь есть возможность немного его сократить: if (someObject is Customer c) c.Balance = c.Balance + 1000;
Получается, что если someObject принадлежит классу Customer, то он преобразуется в переменную типа Customer. И вы сразу можете начать работать с этой переменной.
Можно добавить, что сопоставление с образцом — это не только синтаксический сахар, но и возможность для разработчиков, которые имели дело с подобными конструкциями в других языках, использовать их в C#.
Сопоставление с образцом можно использовать не только с конструкциями if, но и с конструкциями Swith. Пример: switch (userRole)
{
case Manager m:
return m.Salary;
case Partner p:
return p.Income;
}
Кроме того, вы можете внести уточнения, используя условия switch (userRole)
{
case Manager m with Salary<1500:
return m.Salary*1.2;
case Manager m:
return m.Salary;
case Partner p:
return p.Income;
}
В этом случае первый случай будет выполнен только в том случае, если userRole является менеджером и ее значение Salary меньше 1500. То есть m.Salary<1500
В список предлагаемых нововведений C# 8 входят функциональные возможности, недавно появившиеся в Java. А именно: методы интерфейса по умолчанию.
В частности, с помощью этого функционала можно будет изменить устаревший код. Например, самим Java-разработчикам удалось использовать эту функциональность для улучшения API коллекций и добавления поддержки лямбда-выражений.
Может быть, разработчики C# тоже хотят что-то изменить в самом C#, используя эту функциональность? Несмотря на сходство, интерфейсы все же сильно отличаются от абстрактных классов.
Множественное наследование в C# давно отказалось из-за множества возникших трудностей.
Но в этом случае, используя только один метод, сложностей должно возникнуть меньше.
Если класс наследуется от двух и более интерфейсов и несколько интерфейсов содержат метод с таким же именем, то класс должен указать имя метода, указав имя интерфейса.
Проще отслеживать только один метод (особенно с помощью IDE).
Язык C# используется в довольно большом количестве различных типов проектов.
Несмотря на то, что он не занимает первое место по популярности, он уникален по широте охвата типов проектов.
Веб-разработка, настольная, кроссплатформенная, мобильная, игры.
За счет включения некоторых возможностей функционального программирования или параллельного развития других языков C# становится более универсальным.
Становится проще перейти на C# с другого языка.
Однако опытным разработчикам C# становится легче понимать синтаксис других языков.
Теги: #C++ #F# #tuples #tuples #pattern #matching #functional #.
NET #C++ #F#
-
Неделя Безопасности 08: Вирусы Возвращаются
19 Oct, 24 -
Ос Микротик Роутер. «Честный» Обмен Каналами
19 Oct, 24 -
S3 Снижает Цены
19 Oct, 24 -
Adobe Посадила В Тюрьму Двух Москвичей
19 Oct, 24 -
Бэкдоры В Брандмауэрах Juniper
19 Oct, 24