Продолжается подписка на наши издания! Вы не забыли подписаться?

Концепции языка C# 3.0

Автор: Tomas Petricek
Опубликовано: 19.04.2007

1 Введение

C# 3.0 (в настоящее время доступный в предварительной версии) – часть проекта LINQ [1]. Цель этого проекта состоит в том, чтобы внедрить лучшую поддержку работы с данными в господствующие языки программирования общего назначения, созданные в Microsoft для платформы .NET (C# и VB.Net).

Большинство идей, реализованных в C# 3.0, уже доступно в (главным образом) исследовательских и экспериментальных языках, ранее созданных в Microsoft Research. Два самых интересных языка, разработанных в Microsoft Research - Cw [2] и F# [3].

1.1 Вклад F#

F# – это функциональный язык, основанный на ML (фактически он основан на языке OCaml, который добавляет несколько возможностей к Standard ML). Функциональные языки очень отличаются от императивных языков (большинство широко используемых языков – C++, Java и C# – императивны). Самый большой вклад F# состоит в том, что он показывает, как функциональные языки могут быть скомпилированы для .NET-рантайма (CLR), потому что .NET CLR был первоначально разработан для выполнения кода, написанного на императивных языках. Другая цель F# - способность к взаимодействию с другими языками на платформе .NET. Частью исследований, связанных с F#, является проект ILX [6], который показывает, как можно расширить .NET-рантайм, чтобы обеспечить лучше поддержку функциональных языков (например, функций как первоклассных значений).

Большинство нововведений C# 3.0 появилось под влиянием идей функционального программирования:

Пожалуй, все здесь, за исключением функций как первоклассных значений, не имеет непосредственного отношения к функциональному программированию, но действительно появилось или получило распространение в функциональных языках программирования – прим.ред.

Большинство этих возможностей, введенных в C# 3.0, очень ограничено по сравнению с их реализациями в F# и других функциональных языках, но очень интересно наблюдать, как возрастает важность концепций функционального программирования, и как от этого выигрывают не-функциональные языки.

1.2 Вклад Cw

Cw (читается Си-Омега) – это основанный на C# язык, расширяющий его в двух наиболее важных областях. Первая область – это лучшая поддержка работы со структурированными данными (XML) и реляционными данными (базы данных).

Вторая область – поддержка конструкций параллелизма как части языка. В большинстве широко используемых языков программирования поддержка параллелизма реализуется в виде библиотеки классов. Благодаря включению в язык таких конструкций программы становятся более удобочитаемыми (позволяя лучше выразить намерения программиста), и больше работы может быть выполнено компилятором автоматически.

В целом C# 3.0 и проект LINQ вдохновлены первой частью расширений Cw, и расширения синтаксиса в C# 3.0 очень близки к концепциям, появившимся в Cw. Самое большое различие состоит в том, что C# 3.0 обеспечивает лучшую расширяемость, позволяющую разработчикам обеспечить механизм для работы с различными источниками данных. Эта расширяемость является возможностью языка, а не компилятора, как в случае Cw. С другой стороны, некоторые расширения Cw были упрощены в C# 3.0 (например, анонимные типы, которые будут упомянуты позже), и я приведу примеры того, что можно сделать в Cw, но нельзя в C# 3.0.

1.3 Обзор C# 3.0

Основная цель C# 3.0 – упростить работу с данными и позволить гораздо проще общаться с источниками реляционных данных (БД). В C# 3.0 это реализуется за счет расширяемости, а не простого добавления нескольких специальных конструкций. Благодаря новым конструкциям языка можно писать такие выражения:

var query = from c in db.Customers 
              where City == "London"
              orderby c.ContactName;
              select new { CustomerID, ContactName };

Это выражение похоже на SQL-запрос. Это достигается благодаря появившимся в языке операторам запросов (from, where, select, orderby и некоторым другим). Такие операторы – это синтаксический сахар, упрощающий написание запросов, они отображаются на нижележащие функции, выполняющие проекцию, фильтрацию или сортировку (функции Select, Where, OrderBy).

Возможность передавать функции другим функциям (точнее, ссылки на функции) была в C# с самого начала. Однако в C# 1.х эта возможность была реализована крайне неудобно. Функцию нужно было явно оборачивать в делегат, и можно было передавать ссылки только на методы. В C# 2.0 появилось две возможности: неявно преобразовывать имя функции к совместимому с ней делегату, что позволило передавать функции в более естественном виде, и возможность объявлять анонимные методы (функции), то есть объявлять функции непосредственно в выражении, где их требуется использовать. Такие функции могут использовать переменные, объявленные в том же контексте выше (эта возможность называется замыканиями, closures). Уже это сделало применение функционального стиля в C# более естественным. Однако синтаксис анонимных методов оказался громоздким, что в сочетании с необходимостью использовать оператор return приводило к объемному и неэстетичному коду. В C# 3.0 было решено добавить новую конструкцию – лямбда, которая, по сути, является тем же самым, что и анонимные методы, но за счет автоматического вывода типов для параметров и необязательности применения оператора return ее синтаксис очень компактен и удобен для передачи небольших выражений другим функциям в качестве параметра. Удивительно, как небольшое изменение синтаксиса может привести к изменению восприятия некоторой возможности.

Как вы можете видеть, этот запрос возвращает только CustomerID и ContactName из более сложной структуры Customer. Не обязательно явно объявлять новый класс, который содержит только два этих члена, потому что C# 3.0 позволяет разработчикам использовать анонимные типы. Также не требуются объявлять тип переменной запроса, поскольку использование ключевого слова var заставляет C# автоматически выводить тип.

1.4 Cw и интеграция доступа к данным

Исходно идея встраивания доступа к данным в язык программирования общего назначения появилась в исследовательском проекте Cw в Microsoft Research. Интегрированные в Cw возможности доступа к данным включают работу с БД и структурированными XML-данными. Проект LINQ в основном основан на Cw, но есть и некоторые отличия.

На самом деле подобные эксперименты проводились уже достаточно давно в таких языках, как Haskell, и некоторые участвовавшие в их создании разработчики перешли работать в Microsoft. Так что Cw стал развитием идей, родившихся в функциональных языках. – прим.ред.

Возможности, имеющиеся и в C# 3.0, и в Cw, включают анонимные типы (в Cw они не ограничены локальной областью видимости), локальный вывод типа и операторы запросов. Одна из концепций, отсутствовавших в Cw – это расширяемость через деревья выражений. В Cw нельзя было написать собственную реализацию источника данных, который выполнял бы запросы к другим данным, находящимся в памяти. Следующий код показывает, как выглядит в Cw работа с БД (это очень похоже на предыдущий пример C# 3.0):

query = select CustomerID, ContactName 
          from db.Customers 
          where City == "London"
          order by ContactName;

Проект Cw еще будет упомянут в других разделах статьи, поскольку многие возможности C# исходно появились в Cw, причем там они были мощнее.

2 Функции как первоклассные сущности

Язык программирования, о котором можно сказать, что в нем функции являются первоклассными сущностями, – это язык, в котом функции могут создаваться в процессе исполнения программ, храниться в структурах данных, передаваться в качестве параметров другим функциям и возвращаться в качестве значений других функций.
Wikipedia.org

Поддержка функций как первоклассных значений необходима для функционального стиля программирования. Например, функции, работающие в Haskell со списками (map, foldl и т.д…) – это функции высшего порядка, что означает, что они принимают функции в качестве параметра.

Приведенная выше цитата суммирует, что такое функции первого класса, и в чем состоят преимущества языков, поддерживающих эту возможность. Более точный перечень всего, что должен поддерживать язык, чтобы рассматривать функции как первоклассные объекты, можно найти в работе «Concepts in Programming Languages» [5]. Согласно этой книге, язык содержит функции первого класса, если функции могут быть:

Пункты 2 и 3 выполняются во многих языках, включая С/C++, где можно передавать как аргумент указатель на функцию. В первой версии C# можно было использовать делегаты, которые можно легко описать как типобезопасные указатели на функции; однако ни C/C++, ни первая версия C# не позволяли объявлять функции в любой области видимости, в частности, в теле другой функции (или метода).

2.1 Функции в F#

Сначала я расскажу, как работа с функциями реализована в F#, смешанном императивно-функциональном языке для платформы .NET. Я выбрал язык F#, поскольку на его примере видно, что можно реализовать возможности функциональных языков на платформе .NET. Сперва я покажу некоторые возможности, уникальные для F# (по сравнению с остальными .NET-языками):

// ‘add’ – это объявление локальной функции 
// с помощью лямбда-выражения (анонимной функции)
// Синтаксис fun <список параметров> -> <выражение>
// в F# описывает безымянную лямбда-функцию
let add = (fun x y -> x + y);;

// Объявление локальной функции (более краткая версия,
// но результат тот же, что и в предыдущем случае)
let add x y = x + y;;

// Currying – создает функцию, прибавляющую к параметру 10.
// Объявление функции путем частичного задания 
// списка параметров другой функции.
// Этот пример задает один из параметров значением 10, оставляя второй 
// свободным (несвязанным). Это приводит к появлению новой безымянной функции,
// прибавляющей значение 10 к своему аргументу.
// Получившаяся безымянная функция присваивается переменной add10, которую 
// в дальнейшем можно использовать как имя функции 
let add10 = add 10;;

Первое, что показывает этот пример, – в F# функции это значение, имеющее собственный тип, такое же, как любое другой. Это демонстрирует первый пример функции add, являющейся просто глобальной переменной, чье значение – это функция, созданная с использованием лямбда-выражения. Второй пример показывает упрощенный синтаксис объявления глобальных функций (но единственное отличие от первой версии функции add – это синтаксис!).

Следующий пример показывает currying, операцию, которая берет функцию и связывает ее первый параметр с указанным значением. В данном случае параметры – это функция add (принимающая два параметра типа int и возвращающая значение типа int) и константа 10. Результат – это функция add10, принимающая один параметр типа int и возвращающая int. Следующие примеры показывают случаи использования первоклассных функций, которые я позже покажу и на C#:

// передача функций другим функциям
let words = ["Hello"; "world"] in
iter (fun str -> Console.WriteLine(str); ) words;

// Возврат функции 
let compose f g = 
  (fun x -> f (g x));;

Первый пример сперва объявляет список, содержащий строки, и затем использует функцию iter, которая вызывает функцию, передаваемую ей в качестве первого параметра, для каждого элемента списка.

Второй пример – это функция, возвращающая композицию из двух функций, переданных в качестве параметров. Это хороший пример возврата функции, объявленной в локальной области видимости. Второй интересный момент в этом примере – это типы функций. Явно никаких типов не объявлено, и алгоритм вывода типов выводит типы параметров и возвращаемого значения.

<...>

Ссылки

  1. Проект LINQ. D. Box и A. Hejlsberg. См. «Технология клиент-сервер» 2’2006
  2. Cw language. E. Meijer, W. Schulte, G. Bierman и другие. См. http://research.microsoft.com/Comega/
  3. F# language. D. Syme. См. http://research.microsoft.com/fsharp/
  4. Языковой инструментарий: новая жизнь языков предметной области, Мартин Фаулер см. Технология клиент-сервер, http://www.optim.su/cs/2005/3/fowler/fowler.asp
  5. Concepts in Programming Languages. John C. Mitchell. Cambridge University Press, Cambridge UK, 2003
  6. ILX project. D. Syme. См. http://research.microsoft.com/projects/ilx/ilx.aspx

v


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
........................
"С полным содержанием данной статьи можно ознакомиться в печатной версии журнала"

Copyright © 1994-2016 ООО "К-Пресс"