Скачать презентацию Расширения в С# 3. 0 неявно типизированные локальные Скачать презентацию Расширения в С# 3. 0 неявно типизированные локальные

Лекция-12_Пр-е.ppt

  • Количество слайдов: 44

Расширения в С# 3. 0 неявно типизированные локальные переменные; автоматические свойства; инициализаторы объектов и Расширения в С# 3. 0 неявно типизированные локальные переменные; автоматические свойства; инициализаторы объектов и коллекций; анонимные типы; частичные методы; расширяющие методы; деревья выражений; лямбда-выражения. Лекция 04. 12. 2012 г. 1

Неявно типизированные локальные переменные Язык С# - это строго типизированный язык и каждая переменная Неявно типизированные локальные переменные Язык С# - это строго типизированный язык и каждая переменная в программе имеет фиксированный тип. Переменные объявляются так: <тип> <имя_переменной>; или так: <тип> <имя_переменной> = <значение>; В С# 3. 0 появилось новое ключевое слово var, которое можно использовать в качестве имени типа: var <имя_переменной> = <значение>; Здесь переменная неявным образом приводится к типу значения и никакого типа с именем var не существует! Например, в результате объявления var my. Var = 5; переменная my. Var получит тип int, а не var. Использование var не означает объявления переменной без типа или же переменной с типом, который может изменяться. Если бы это было так, язык С# не являлся бы строго типизированным. При невозможности определить тип переменной, объявленной с var, компилятор не будет компилировать код. Поэтому в таком объявлении нужно обязательно инициализировать переменную. Лекция 04. 12. 2012 г. 2

Неявно типизированные локальные переменные int i = 5; string s = Неявно типизированные локальные переменные int i = 5; string s = "Hello"; double d = 1. 0; int[] numbers = new int[] {1, 2, 3}; Dictionary orders = new Dictionary(); var var var i = 5; s = "Hello"; d = 1. 0; numbers = new int[] {1, 2, 3}; orders = new Dictionary(); “var” означает тот же тип, что и тип инициализатора Это также работает в инструкциях for и for … each Лекция 04. 12. 2012 г. 3

Автоматические свойства В С# есть удобный механизм доступа к данным объекта - свойства. С Автоматические свойства В С# есть удобный механизм доступа к данным объекта - свойства. С их помощью можно открыть доступ к данным, не «выдумывая» интерфейсных методов типа "put" и "get ". Объявление свойств достаточно громоздко, однако C# 3. 0 позволяет сделать код более сжатым с использованием упрощенной записи. Пример обычного объявления свойства: public class Person { private string first. Name; public string First. Name { get { return first. Name; } set { first. Name = value; } } private string last. Name; public string Last. Name { get { return last. Name; } set { last. Name = value; } } } Лекция 04. 12. 2012 г. 4

Автоматические свойства То же самое можно записать более компактно: public class Person { public Автоматические свойства То же самое можно записать более компактно: public class Person { public string First. Name { get; set; } public string Last. Name { get; set; } } В компактной записи также есть возможность открыть доступ только для чтения, либо только для записи: public class Person { public string First. Name { get; private set; } public string Last. Name { get; private set; } } Можно пользоваться и другими модификаторами, например internal или protected. Лекция 04. 12. 2012 г. 5

Автоматические свойства Компилятор С# автоматически заполняет все «пробелы» в объявлении свойств. Так, компилятор объявляет Автоматические свойства Компилятор С# автоматически заполняет все «пробелы» в объявлении свойств. Так, компилятор объявляет закрытое (private) поле, которое применяется для хранения, и использует его в блоках get и set внутри свойства, позволяя не беспокоиться о деталях. Уровень доступности, тип и имя автоматического свойства определяются программистом обычным образом, но реализации блоков get и set внутри этого свойства, а также поле, соответствующее данному свойству, ему недоступны и генерируются самим компилятором. В случае применения автоматического свойства доступ к данным возможен только через это свойство, т. к. имя соответствующего приватного поля неизвестно. Лекция 04. 12. 2012 г. 6

Инициализаторы объектов представляют собой способ, позволяющий создавать экземпляры и инициализировать объекты без добавления дополнительного Инициализаторы объектов представляют собой способ, позволяющий создавать экземпляры и инициализировать объекты без добавления дополнительного кода. Они подразумевают предоставление при создании экземпляра объекта значений для открытых (public) свойств или полей с использованием пары "имя-значение" для каждого свойства или поля, которое требуется инициализировать. Синтаксис: <имя_класса> <имя_переменной> = new <имя_класса> { <свойство_или_поле 1> = <значение 1>, <свойство_или_поле 2> = <значение 2>, <свойство_или_поле. N> = <значение 2 N> }; Лекция 04. 12. 2012 г. 7

Инициализаторы public class Point { public override string To. String() { return string. Format( Инициализаторы public class Point { public override string To. String() { return string. Format("(X={0}, Y={1})", X, Y); } public int X { get; set; } public int Y { get; set; } } Point a = new Point { X = 0, Y = 1 }; Пары <имя=значение> Point a = new Point(); a. X = 0; a. Y = 1; Используются фигурные скобки Лекция 04. 12. 2012 г. 8

Инициализаторы public class Rectangle { public Point P 1 { get; private set; } Инициализаторы public class Rectangle { public Point P 1 { get; private set; } public Point P 2 { get; private set; } } Не работает, т. к. P 1 и P 2 – ссылочные типы и конструктор по умолчанию не размещает их в памяти Rectangle r = new Rectangle { P 1 = { X = 0, Y = 1 }, P 2 = { X = 2, Y = 3 } }; Вызов конструктора по умолчанию подразумевается Rectangle r = new Rectangle(); r. P 1. X = 0; r. P 1. Y = 1; r. P 2. X = 2; r. P 2. Y = 3; Лекция 04. 12. 2012 г. 9

Инициализаторы public class Rectangle { public Rectangle() { P 1 = new Point(); P Инициализаторы public class Rectangle { public Rectangle() { P 1 = new Point(); P 2 = new Point(); } public Point P 1 { get; private set; } public Point P 2 { get; private set; } } Rectangle r = new Rectangle { P 1 = { X = 0, Y = 1 }, P 2 = { X = 2, Y = 3 } }; Определили конструктор по умолчанию Теперь работает Rectangle r = new Rectangle(); r. P 1. X = 0; r. P 1. Y = 1; r. P 2. X = 2; r. P 2. Y = 3; Лекция 04. 12. 2012 г. 10

Инициализаторы static void Main(string[] args) { var a = new Point { X = Инициализаторы static void Main(string[] args) { var a = new Point { X = 0, Y = 1 }; var r = new Rectangle { P 1 = { X = 0, Y = 1 }, P 2 = { X = 2, Y = 3 } }; Console. Write. Line("a = {0}", a); Console. Write. Line("P 1 : {0}, P 2 : {1}", r. P 1, r. P 2); Console. Read. Line(); } Лекция 04. 12. 2012 г. 11

Инициализаторы коллекций предлагают простой синтаксис для создания и заполнения коллекций за один шаг. Тип Инициализаторы коллекций предлагают простой синтаксис для создания и заполнения коллекций за один шаг. Тип должен реализовывать интерфейс ICollection List powers = new List { 1, 100, 10000 }; List powers = new List(); powers. Add(10); powers. Add(1000); powers. Add(10000); Лекция 04. 12. 2012 г. 12

Инициализаторы //Ex 200. cs using System; using System. Collections. Generic; public class Contact { Инициализаторы //Ex 200. cs using System; using System. Collections. Generic; public class Contact { public Contact() { Phone. Numbers = new List(); } public string Name { get ; set ; } public List Phone. Numbers { get; private set; } } class Program { static void Main(string[] args) { var contacts = new List { new Contact {Name = "Иван Сидоров", Phone. Numbers = { "919 -555 -0101", "928 -882 -8080" } }, new Contact {Name = "Борис Петров", Phone. Numbers = { "863 -525 -0199" } } }; foreach (var x in contacts) { Console. Write. Line("Name = {0}", x. Name); foreach (var z in x. Phone. Numbers) Console. Write. Line(" Ph. Num : {0}", z); } Console. Read. Line(); } } Лекция 04. 12. 2012 г. 13

Анонимные типы При программировании, особенно приложений баз данных, много времени уходит на создание простых Анонимные типы При программировании, особенно приложений баз данных, много времени уходит на создание простых классов для представления данных, которые не делают ничего, кроме предоставления свойств. Подобный класс просто хранит структурированные данные. В базе данных или электронной таблице он мог бы просто представлять какую-нибудь строку в таблице. Тогда класс-коллекция, способный хранить экземпляры этого класса, мог бы служить представлением множества строк в этой таблице. Написание кода для подобных классов превращается в рутинную работу, а внесение любых изменений в лежащую в основе схему данных требует добавлять, удалять или изменять их код. Анонимные типы предлагают способ для упрощения этой модели программирования. В частности, они позволяют не определять никаких простых типов для хранения данных, а использовать вместо этого компилятор С# для автоматического создания типов на базе данных, которые в них нужно хранить. Лекция 04. 12. 2012 г. 14

Анонимные типы public class Customer { public string Name {get; set; } class ? Анонимные типы public class Customer { public string Name {get; set; } class ? ? ? public string Region {get; set; } { public string Phone {get; set; } public string Name; } public string Phone; public class Contact { } public string Name; public string Phone; } class Program { static void Main(string[] args) { Customer c = new Customer { Name = "Иванов", Region = "ROV", Phone = "928 -882 -8080" }; 1 Contact x = new Contact { Name = c. Name, Phone = c. Phone }; 2 var y = new { Name = c. Name, Phone = c. Phone }; 3 var z = new { c. Name, c. Phone }; Console. Write. Line("Name : {0} Phone : {1}", z. Name, z. Phone); Console. Read. Line(); } } Проекционный стиль инициализатора Лекция 04. 12. 2012 г. 15

Анонимные типы var contacts = from c in customers where c. Region == Анонимные типы var contacts = from c in customers where c. Region == "ROV" select new { c. Name, c. Phone }; IEnumerable class ? ? ? { public string Name; public string Phone; } var contacts = customers. . Where(c => c. Region == "ROV"). Select(c => new { c. Name, c. Phone }); ? ? ? foreach (var c in contacts) { Console. Write. Line(c. Name); Console. Write. Line(c. Phone); } Лекция 04. 12. 2012 г. 16

Анонимные типы //Ex 201. cs using System; using System. Linq; using System. Collections. Generic; Анонимные типы //Ex 201. cs using System; using System. Linq; using System. Collections. Generic; public class Customer { public string Name {get; set; } public string Region {get; set; } public string Phone {get; set; } } public class Contact { public string Name; public string Phone; } class Program { static void Main(string[] args) { Customer[] customers = { new Customer { Name = "Иванов", Region = "ROV", Phone = "928 -882 -8080" }, new Customer { Name = "Быков", Region = "ROV", Phone = "929 -811 -1234" }, new Customer { Name = "Петров", Region = "MOV", Phone = "926 -877 -9090" } }; var contacts = from c in customers where c. Region == "ROV" select new { c. Name, c. Phone }; foreach (var c in contacts) { Console. Write. Line(c. Name); Console. Write. Line(c. Phone); } Console. Read. Line(); } } Лекция 04. 12. 2012 г. 17

Частичные методы (partial methods) — это методы, у которых прототип (определение метода) специфицирован в Частичные методы (partial methods) — это методы, у которых прототип (определение метода) специфицирован в объявлении частичного класса, а реализация метода не представлена в том же объявлении частичного класса. Фактически, они могут не иметь никакой реализации ни в одном из объявлений одного и того же частичного класса. И если реализации метода нет ни в одном из объявлений частей этого класса, то компилятором не генерируется никакого IL-кода ни для объявления метода, ни для его вызова, ни для оценки аргументов, переданных методу. То есть все обстоит так, как будто этот метод вообще никогда не существовал. Лекция 04. 12. 2012 г. 18

Частичные методы Пример определения частичных методов: public partial class My. Widget { partial void Частичные методы Пример определения частичных методов: public partial class My. Widget { partial void My. Widget. Start(int count); partial void My. Widget. End(int count); public My. Widget(){ Увеличиваем счетчик int count = 0; My. Widget. Start(++count); Console. Write. Line("In the constructor of My. Widget. "); My. Widget. End(++count); Ещё раз увеличиваем Console. Write. Line("count = " + count); } } class Program { static void Main(string[] args) { А результат = 0 !! My. Widget my. Widget = new My. Widget(); Console. Read. Line(); } } Никакого кода IL не было создано ни для одного из вызовов этих двух частичных методов Лекция 04. 12. 2012 г. 19

Частичные методы Добавляем реализации частичных методов: . . . public partial class My. Widget Частичные методы Добавляем реализации частичных методов: . . . public partial class My. Widget { partial void My. Widget. Start(int count) { Console. Write. Line("In My. Widget. Start (count } partial void My. Widget. End(int count) { Console. Write. Line("In My. Widget. End(count is } } class Program { static void Main(string[] args) { My. Widget my. Widget = new My. Widget(); Console. Read. Line(); } } is {0})", count); Как видим, теперь не только вызваны реализации частичных методов, но и переданные аргументы также были вычислены. Это доказывает значение count = 2 в конце вывода. Лекция 04. 12. 2012 г. 20

Частичные методы должны подчиняться определённым правилам: Ø Они могут определяться только в частичных классах. Частичные методы должны подчиняться определённым правилам: Ø Они могут определяться только в частичных классах. Ø Они должны специфицироваться модификатором partial. Ø Эти методы являются частными (закрытыми), но не должны специфицироваться модификатором private, иначе компилятор выдаст ошибку. Ø Частичные методы должны возвращать void. Ø Они могут быть не реализованными. Ø Частичные методы могут не иметь аргументов. Лекция 04. 12. 2012 г. 21

Расширяющие методы (extension methods) позволяют расширять функциональные возможности типов без внесения изменений в сами Расширяющие методы (extension methods) позволяют расширять функциональные возможности типов без внесения изменений в сами типы. Их можно использовать для расширения даже таких типов, которые нельзя изменять, в частности, типов, поставляемых в составе. NET Framework (например, System. String). Под расширением функциональных возможностей типа подразумевается создание метода, который можно было бы вызывать через экземпляр этого типа. Такой метод и называется расширяющим методом. Он может принимать любое количество параметров и возвращать значение любого типа. Лекция 04. 12. 2012 г. 22

Расширяющие методы Для создания и использования расширяющего метода нужно: ü Создать не обобщенный статический Расширяющие методы Для создания и использования расширяющего метода нужно: ü Создать не обобщенный статический класс. ü Добавить в созданный класс расширяющий метод в виде статического метода с использованием специального синтаксиса. ü Удостовериться в том, что в коде, где планируется использовать расширяющий метод, импортируется содержащее его класс пространство имен с помощью оператора using. ü Вызвать расширяющий метод через экземпляр расширяемого типа тем же самым образом, каким бы вызывался и любой другой метод этого типа. Лекция 04. 12. 2012 г. 23

Расширяющие методы Создаём класс (имя – любое) namespace Netsplore. Utilities { public static class Расширяющие методы Создаём класс (имя – любое) namespace Netsplore. Utilities { public static class String. Conversions { public static double To. Double(this string s) { Определяем return Double. Parse(s); статический метод } спец. вида public static bool To. Bool(this string s) { return Boolean. Parse(s); } } Подключаем } пространство имён. . . Вызываем using Netsplore. Utilities; статический метод class Program { как экземплярный static void Main(string[] args) { double pi = "3. 1415926535". To. Double(); Console. Write. Line(pi); Console. Read. Line(); } } Лекция 04. 12. 2012 г. 24

Расширяющие методы Преимущество расширяющих методов. Расширяющие методы выглядят действительно полезной концепцией, особенно когда необходимо Расширяющие методы Преимущество расширяющих методов. Расширяющие методы выглядят действительно полезной концепцией, особенно когда необходимо расширить класс, который нельзя расширить путём наследования, например, запечатанный (sealed) класс, либо класс, исходный код которого недоступен. В предыдущем примеры расширяющие методы эффективно добавляли новую функциональность в класс string. Без расширяющих методов этого сделать нельзя, т. к. класс string запечатан. Лекция 04. 12. 2012 г. 25

Лямбда-выражения (lambda expressions) являются новой конструкцией в С# 3. 0 и могут упрощать некоторые Лямбда-выражения (lambda expressions) являются новой конструкцией в С# 3. 0 и могут упрощать некоторые аспекты программирования, особенно в случае использования в сочетании с технологией LINQ. Лямбда-выражения использовались в языках программировании вроде LISP издавна. Впервые они были сформулированы в 1936 г. американским математиком Алонзо Чёрчем. Эти выражения представляют сокращённый синтаксис для спецификации алгоритмов. Прежде чем обратиться непосредственно к лямбда-выражениям, рассмотрим эволюцию способов спецификации алгоритма как аргумента метода, т. к. именно в этом и состоит назначение лямбда-выражений. 1. Использование именованных методов До появления C# 2. 0, когда метод или переменная была типизирована так, что требовала delegate, разработчик должен был создавать именованный метод и передавать его имя туда, где требовался delegate (см. пример на следующем слайде). Лекция 04. 12. 2012 г. 26

Лямбда-выражения 1. Использование именованных методов public class Common { Делегат public delegate bool Int. Лямбда-выражения 1. Использование именованных методов public class Common { Делегат public delegate bool Int. Filter(int i); public static int[] Filter. Array. Of. Ints(int[] ints, Int. Filter filter) { Array. List a. List = new Array. List(); foreach (int i in ints) if (filter(i)) a. List. Add(i); return ((int[])a. List. To. Array(typeof(int))); } Именованный метод } public class Application { public static bool Is. Odd(int i) { return ((i & 1) == 1); } } Передача метода. . . по имени static void Main(string[] args) { int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int[] odd. Nums = Common. Filter. Array. Of. Ints(nums, Application. Is. Odd); foreach (int i in odd. Nums) Console. Write. Line(i); Console. Read. Line(); } Лекция 04. 12. 2012 г. 27

Лямбда-выражения 2. Использование анонимных методов public class Common { Делегат public delegate bool Int. Лямбда-выражения 2. Использование анонимных методов public class Common { Делегат public delegate bool Int. Filter(int i); public static int[] Filter. Array. Of. Ints(int[] ints, Int. Filter filter) { Array. List a. List = new Array. List(); foreach (int i in ints) if (filter(i)) a. List. Add(i); return ((int[])a. List. To. Array(typeof(int))); } Именованный метод не нужен } public class Application { public static bool Is. Odd(int i) { return ((i & 1) == 1); Передача анонимного } метода }. . . static void Main(string[] args) { int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int[] odd. Nums = Common. Filter. Array. Of. Ints(nums, delegate(int i) { return ((i & 1) == 1); }); foreach (int i in odd. Nums) Console. Write. Line(i); Console. Read. Line(); } Лекция 04. 12. 2012 г. 28

Лямбда-выражения 3. Использование лямбда-выражений public class Common { Делегат public delegate bool Int. Filter(int Лямбда-выражения 3. Использование лямбда-выражений public class Common { Делегат public delegate bool Int. Filter(int i); public static int[] Filter. Array. Of. Ints(int[] ints, Int. Filter filter) { Array. List a. List = new Array. List(); foreach (int i in ints) if (filter(i)) a. List. Add(i); return ((int[])a. List. To. Array(typeof(int))); } } Передача. . . лямбда-выражения static void Main(string[] args) { int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int[] odd. Nums = Common. Filter. Array. Of. Ints(nums, i => ((i & 1) == 1)); foreach (int i in odd. Nums) Console. Write. Line(i); Console. Read. Line(); } Лекция 04. 12. 2012 г. 29

Лямбда-выражения Лямбда-выражение - это разделенный запятыми список параметров, за которым следует лямбда-операция (в С# Лямбда-выражения Лямбда-выражение - это разделенный запятыми список параметров, за которым следует лямбда-операция (в С# это =>), а за ней— выражение или блок операторов. Если параметров более одного, входные параметры заключаются в скобки. Т. о. , лямбда-выражение в С# выглядит так: (параметр1, параметр2, . . . , параметр. N) => выражение Или, когда требуется более высокая сложность, может быть использован блок операторов: (параметр1, параметр2, . . . , параметр. N) => { оператор1; оператор2; … оператор. N; return (тип_возврата_лямбда_выражения) ; } Лекция 04. 12. 2012 г. 30

Лямбда-выражения Важно знать, что лямбда-выражение и соответствующий ему делегат должны находиться в соответствии относительно Лямбда-выражения Важно знать, что лямбда-выражение и соответствующий ему делегат должны находиться в соответствии относительно количества и типов входных параметров, а также типа возвращаемого значения. Например: delegate int D(int i, int j); . . . (x, у) => { if (x > у) return (x); else return (y); } delegate bool D 1(int i, int j); . . . (x, у) => x == y; Лекция 04. 12. 2012 г. 31

Лямбда-выражения Примеры лямбда-выражений using System; using System. Collections. Generic; using System. Linq; using System. Лямбда-выражения Примеры лямбда-выражений using System; using System. Collections. Generic; using System. Linq; using System. Text; namespace Explicit. Lambda. Arguments { class Program { static void Main(string[] args) { Func add = (int x, int y) => x + y; Console. Write. Line("Result: " + add(3, 4)); Console. Read. Line(); } } Обобщённый делегат } Func Обобщённый делегат Func представляет метод с двумя параметрами, возвращающий значение типа, указанного в параметре TResult. public delegate TResult Func(T arg 1, T arg 2) Лекция 04. 12. 2012 г. 32

Лямбда-выражения Примеры лямбда-выражений using System; using System. Collections. Generic; using System. Linq; using System. Лямбда-выражения Примеры лямбда-выражений using System; using System. Collections. Generic; using System. Linq; using System. Text; Обобщённый делегат using System. Diagnostics; Action using System. IO; namespace Lambda. Expression. Action { class Program { static void Main(string[] args) { Action print = (s, writer) => writer. Write. Line(s); print("Console", Console. Out); Stream. Writer stream = File. Append. Text("c: \temp\text. txt"); stream. Auto. Flush = true; print("File", stream); Console. Read. Line(); } } } Обобщённый делегат Action представляет метод с двумя параметрами, не возвращающий значение. Лекция 04. 12. 2012 г. 33

Лямбда-выражения Примеры лямбда-выражений using System; using System. Collections. Generic; using System. Linq; using System. Лямбда-выражения Примеры лямбда-выражений using System; using System. Collections. Generic; using System. Linq; using System. Text; namespace Searching { class Program { static void Main(string[] args) { var recipes = new[]{"Crepes Florentine", "Shrimp Che Paul", "Beef Burgundy", "Rack of Lamb", "Tacos", "Rosemary Roasted Chicken", "Juevos Rancheros", "Buffalo Chicken Nachos"}; var results = recipes. Where(s => s. Contains("Chicken")); Array. For. Each(results. To. Array(), s => Console. Write. Line(s)); Console. Read. Line(); } } } Лекция 04. 12. 2012 г. 34

Лямбда-выражения Примеры лямбда-выражений using System; using System. Collections. Generic; using System. Linq; using System. Лямбда-выражения Примеры лямбда-выражений using System; using System. Collections. Generic; using System. Linq; using System. Text; namespace Finding. Numbers { class Program { static void Main(string[] args) { List fibo. List = new List(); fibo. List. Add. Range(new[]{1, 1, 2, 3, 5, 8, 13, 21, 34, 55}); Predicate match = n => n % 2 == 1; var odds = fibo. List. Find. All(match); Object. Dumper. Write(odds); Console. Read. Line(); } } } Лекция 04. 12. 2012 г. 35

Деревья выражений Дерево выражений— эффективное представление в форме дерева данных лямбда-выражения операций запросов. Эти Деревья выражений Дерево выражений— эффективное представление в форме дерева данных лямбда-выражения операций запросов. Эти представления деревьев выражений могут быть вычислены все сразу, так что единственный запрос может быть построен и выполнен на одном источнике данных, таком как база данных. В большинстве примеров, рассмотренных до сих пор, операции выражений выполнялись в последовательной линейной манере. Рассмотрим следующий код: int[] nums = new IEnumerable. Where (i => i. Order. By (i => int[] { 6, 2, 7, 1, 9, 3 }; nums. Less. Than. Four = nums < 4) i); Этот запрос содержит две операции — Where и Order. By, которые ожидают делегатов методов в качестве своих аргументов. При компиляции этого кода генерируется IL-код, идентичный анонимному методу вместо каждого лямбда-выражения операций запросов. Лекция 04. 12. 2012 г. 36

Деревья выражений Когда выполняется этот запрос, сначала вызывается операция Where, за ней — операция Деревья выражений Когда выполняется этот запрос, сначала вызывается операция Where, за ней — операция Order. By. Такое линейное выполнение операций кажется оправданным для данного примера, но стоит подумать о запросе к очень большому источнику данных, такому как база данных. Имело бы смысл для запроса SQL сначала обратиться к базе данных только с оператором Where, чтобы изменить порядок последующих вызовов? Конечно, это не реально для запросов к базе данных, как потенциально и для других типов запросов. Именно здесь приходят на помощь деревья выражений. Поскольку дерево выражений допускает параллельное вычисление и выполнение всех операций в запросе, может быть произведен единственный общий запрос вместо отдельных запросов для каждой операции. Лекция 04. 12. 2012 г. 37

Деревья выражений В рассмотренных выше примерах -выражения были тесно связаны с делегатами, фактически они Деревья выражений В рассмотренных выше примерах -выражения были тесно связаны с делегатами, фактически они подменяли функциональность делегатов. Например, «конвертация» -выражения в делегат, происходит так: Func func 1 = n => n+1; Тип делегата Делегат -выражение В этом примере -выражение преобразуется в делегат, принимающий единственный целочисленный параметр и возвращающий int. Однако компилятор С# 3. 0 также обладает способностью преобразовывать выражения в деревья выражений, составленные из экземпляров типов пространства имен System. Linq. Expressions: Expression> expr = n => n+1; Переменная типа Expression -выражение В этой строке кода -выражение преобразуется в структуру данных, представляющую операцию. Лекция 04. 12. 2012 г. 38

Деревья выражений Типом переменной expr является Expression<T>, где T заменяется типом делегата, в который Деревья выражений Типом переменной expr является Expression, где T заменяется типом делегата, в который может быть преобразовано -выражение. Компилятор «замечает» , что выполняется конвертация -выражения в экземпляр Expression>, и генерирует весь необходимый код, чтобы это произошло. В некоторый момент позднее можно скомпилировать выражение в полезный делегат, как показано в примере: using System; using System. Linq. Expressions; public class Entry. Point { static void Main() { Expression> expr = n => n+1; Func func = expr. Compile(); for( int i = 0; i < 10; ++i ) Console. Write. Line( func(i) ); } } Лекция 04. 12. 2012 г. 39

Деревья выражений Важно понять, что деревья выражений являются самостоятельными конструкциями, не обязательно связанными с Деревья выражений Важно понять, что деревья выражений являются самостоятельными конструкциями, не обязательно связанными с -выражениями. Их можно создавать, анализировать, модифицировать, комбинировать несколько деревьев выражений для получения более сложных деревьев, наконец, компилировать. Можно даже определить новый язык выражений или реализовать анализатор для существующего языка выражений. Фактически, компилятор работает как анализатор выражений, когда -выражение присваивается экземпляру типа Expression. “За кулисами” он генерирует код для построения дерева выражений. Предыдущий пример может быть переписан без использования -выражений, как показано на следующем слайде: Лекция 04. 12. 2012 г. 40

Деревья выражений using System; using System. Linq. Expressions; public class Entry. Point { static Деревья выражений using System; using System. Linq. Expressions; public class Entry. Point { static void Main() { var n = Expression. Parameter( typeof(int), "n" ); var expr = Expression>. Lambda>( Expression. Add(n, Expression. Constant(1)), n ); Func func = expr. Compile(); for( int i = 0; i < 10; ++i ) Console. Write. Line( func(i) ); } } Выделенные строки заменяют единственную строку предшествующего примера, где переменной expr присваивается -выражение n => n+1. Лекция 04. 12. 2012 г. 41

Операции над выражениями Следующий пример демонстрирует, что выражения – это обычные данные, над которыми Операции над выражениями Следующий пример демонстрирует, что выражения – это обычные данные, над которыми можно выполнять различные операции. Здесь конструируется, а затем и вычисляется выражение (2(n+1)+n)(2(n+1)): using System; using System. Linq. Expressions; public class Entry. Point { static void Main() { 1 Expression> expr = n => n + 1, expr 1; 2 expr = Expression>. Lambda>( Expression. Multiply(expr. Body, Expression. Constant(2)), expr. Parameters); 3 expr 1 = expr; 4 expr = Expression>. Lambda>( Expression. Add(expr. Body, expr. Parameters[0]), expr. Parameters); 5 expr = Expression>. Lambda>( Expression. Multiply(expr. Body, expr 1. Body), expr. Parameters); Func func = expr. Compile(); for( int i = 0; i < 10; ++i ) Console. Write. Line( func(i) ); } } Лекция 04. 12. 2012 г. 42

Использование деревьев выражений Деревья выражений полезны в применении к LINQ представляет естественный и выразительный Использование деревьев выражений Деревья выражений полезны в применении к LINQ представляет естественный и выразительный синтаксис описания операций над данными, которые невозможно смоделировать объектно-ориентированным способом. Например, можно создать выражение LINQ для поиска таких элементов в большом массиве, находящемся в памяти (или другом типе IEnumerable), которые соответствуют определенному шаблону. LINQ – расширяемый язык и может предоставлять средства оперирования с другими типами хранилищ, такими как XML и реляционные базы данных. Фактически C# 3. 0 представляет реализацию LINQ для реляционных баз данных (включая LINQ to SQL, LINQ to Dataset, LINQ to Entities, LINQ to XML и LINQ to Objects), которые все вместе позволяют выполнять операции LINQ на любых типах, поддерживающих IEnumerable. Предположим, что LINQ to SQL используется для запроса к реляционной базе данных. Пользовательская база может находиться на другом конце земного шара, и может оказаться слишком дорого выполнять простой запрос. К тому же заранее нельзя сказать, насколько сложным может оказаться пользовательское выражение LINQ. Поэтому естественно стремление обеспечить максимальную эффективность. Лекция 04. 12. 2012 г. 43

Использование деревьев выражений Если выражение LINQ представлено в данных (как дерево выражений), а не Использование деревьев выражений Если выражение LINQ представлено в данных (как дерево выражений), а не в IL (как делегат), то им можно оперировать. Возможно, имеется алгоритм, который сможет оптимизировать выражение. Или, может оказаться, что когда конкретная реализация анализирует выражение, принимается решение, что все выражение может быть упаковано, отправлено по сети и полностью выполнено на стороне сервера. Деревья выражений обеспечивают эту важную возможность. Затем, когда операции с данными завершаются, можно транслировать дерево выражений в окончательную исполняемую операцию посредством механизма типа Lambda. Expression. Compile и запустить ее. Если бы выражение изначально было доступно только в виде код IL, гибкость была бы существенно ограничена. Лекция 04. 12. 2012 г. 44