10_C#_Методы_расширения_LINQ.pptx
- Количество слайдов: 40
LINQ (Language Integrated Query) ü LINQ — это название набора технологий для интеграции непосредственно в язык C# (и в другие языки платформы. NET) запросов к поставщикам данных. ü LINQ поддерживается версиями. NET Framework 3. 0 и выше. ü LINQ — это на самом деле две сущности: • набор операторов, предоставляемых как методы для операций с наборами данных; • конструкции языка C# для поддержки запросов непосредственно в языке. ü Для поддержки LINQ в версию 3. 0 C# были введены новые конструкции языка C# : • расширяющие методы (extension methods); • лямбда-выражения; • тип var.
LINQ (Language Integrated Query) ü LINQ предоставляет модель для работы с данными из различных источников данных, в которой запрос является одним из структурных элементов языка. ü Многие операторы, включенные в LINQ, являются аналогами известных операций, например, Select. Many, Where, Join, Group. By ( всего около 50). ü В. NET Framework Standard Query Operators API определен шаблон для методов, поддерживающих операции, который не описывает, для каких наборов данных предназначены эти операции и как именно их следует реализовать. ü Шаблон реализуется «провайдерами LINQ» для различных источников данных и целевых сред (наборов в памяти, баз данных SQL, объектнореляционных систем (ORM), вычислительных кластеров HPC Server, временных и потоковых источников данных и т. д. ). ü Один из наиболее часто применяемых провайдеров — LINQ to Objects, он предоставляет полный набор LINQ-операторов, реализованных поверх IEnumerable
Методы расширения ü Методы расширения предоставляют возможность "добавить" новые методы в ранее определенные типы без перекомпиляции или определения нового производного типа. ü Методы расширения определяются как методы статического класса. Их первый параметр имеет модификатор this и определяет тип, для которого метод может работать как расширение. ü Метод расширения вызывается как экземплярный метод без первого параметра. ü Методы расширения имеют более низкий приоритет, чем экземплярные методы типа с тем же именем и сигнатурой. Компилятор сначала выполняет поиск совпадения с методами экземпляра типа и только, когда такого метода нет, выполняет поиск методов расширения и делает привязку к первому найденному методу расширения. ü Методы расширения не могут получить доступ к закрытым полям типа, для расширения которого они используются.
Определение и вызов метода расширения ü Чтобы определить свой метод расширения, надо • определить статический класс, который будет содержать метод расширения; • метод расширения в статическом классе должен иметь по меньшей мере такую же видимость, что и содержащий его статический класс; • первый параметр метода расширения должен иметь модификатор this, этот параметр определяет тип, для которого метод можно использовать как расширение. ü При вызове метода расширения • метод расширения вызывается как экземплярный; • первый параметр не указывается, так как представляет собой тип, к которому применяется оператор, - компилятору уже известен тип объекта.
Определение и вызов методов расширения. Пример interface IDemo { void F(string str); } class Old_Class : IDemo { public void F 1 (string str) { Console. Write. Line(" F 1 from Old_Class " + str); } } public void F (string str) { Console. Write. Line(" F from Old_Class " + str); } class Program { static void Main(string[] args) { Old_Class obj = new Old_Class(); obj. F 1("111"); obj. F 2("222"); obj. F 3("333"); obj. Foo("abc"); } } string s = ""; s. Foo("efg"); static class Extentions_Class { public static void F 1 (this Old_Class obj, string str) { Console. Write. Line(" F 1 from Extentions_Class " + str); } public static void F 2 (this Old_Class obj, string str) { Console. Write. Line(" F 2 from Extentions_Class " + str); } // Метод расширения для типов, реализующих IDemo public static void F 3 (this IDemo obj, string str) { Console. Write. Line(" F 3 from Extentions_Class " + str); obj. F("Call F from F 3"); } public static void Foo (this object obj, string str) { Console. Write. Line(" Foo " + str); } } Вывод : F 1 from Old_Class 111 F 2 from Extentions_Class 222 F 3 from Extentions_Class 333 F from Old_Class Call F from F 3 Foo abc Foo efg
Неявно типизированные локальные переменные. Анонимные типы ü Начиная с версии 3. 0, в C# поддерживается ключевое слово var для объявления неявно типизированных локальных переменных. Эти переменные имеют строгую типизацию, как и при явном задании типа, их тип определяет компилятор. ü В примере переменная test_1 имеет тип string, переменная test_2 - тип double. var test_1 = “abc”; var test_2 = 1. 234; test_2 = 10; // test_1 = test_2; // ошибка при компиляции ü Ключевое слово var также используется при создании анонимных типов. ü Анонимные типы — это ссылочные типы (class), состоящие из одного или более открытых свойств, доступных только для чтения. Непосредственный базовый класс для анонимного типа – System. Object. ü Имя типа создается компилятором. Тип свойств определяется инициализаторами свойств. ü В примере создается анонимный тип с двумя свойствами: Title и Year. var v = new { Title = "Fortran", Year = 1962 }; Console. Write. Line("Title = " + v. Title);
Анонимные типы. Пример var v_1 = new { Title = "Fortran", Year = 1962 }; Console. Write. Line (v_1); Console. Write. Line (v_1. Title); var v_2 = new { Title = "Fortran", Year = 1962 }; Console. Write. Line (object. Reference. Equals(v_1, v_2)); v_1 = v_2; var v_3 = new { Name = "Fortran", Year = 1962 }; // v_3 = v_1; var v_4 = new { Title = "Fortran", Author = "Иванов", Date = Date. Time. Today }; Console. Write. Line (v_1. Get. Type()); Console. Write. Line (v_2. Get. Type()); Console. Write. Line (v_3. Get. Type()); Console. Write. Line (v_4. Get. Type()); object obj = v_1; Console. Write. Line (obj); ü В примере определяются три анонимных типа. ü С точки зрения среды CLR анонимный тип ничем не отличается от любого другого ссылочного типа, за исключением того, что он может быть приведен только к типу object. ü Обычно анонимный тип используется в запросе select для возврата подмножества свойств объектов из исходной коллекции. Вывод: { Title = Fortran, Year = 1962 } Fortran False <>f__Anonymous. Type 0`2[System. String, System. Int 32] <>f__Anonymous. Type 1`2[System. String, System. Int 32] <>f__Anonymous. Type 2`3[System. String, System. Date. Time] { Title = Fortran, Year = 1962 }
Статический класс Enumerable ü Запрос представляет собой выражение для получения данных из источника данных. ü Стандартные операторы запросов к источникам данных реализованы как методы расширений статических классов Enumerable и Queryable. Классы находятся в сборке System. Core. dll в пространстве имен System. Linq. ü Стандартные операторы запросов применимы к объектам, реализующим интерфейсы IEnumerable
Выражения запросов. Пример ü Классы Book и Books. Library для примеров, в которых создаются и выполняются запросы разных типов. partial class Books. Library : IEnumerable
Выражения запросов. Пример ü В следующем примере создается и выполняется два раза выражение запроса. static void Main(string[] args) { Books. Library lib = new Books. Library(); Book book_1 = new Book("C# ", "Trey Nash", 2010); lib. Add. Book(book_1); Book book_2 = new Book("C++", "Bjarn Straustroup", 2008); lib. Add. Book(book_2); lib. Add. Book(new Book("C#", "Эндрю Троелсен", 2005)); lib. Add. Book(new Book("Темные аллеи", "Иван Бунин", 2004)); lib. Add. Book(new Book("Война и мир", "Лев Толстой", 2007)); Console. Write. Line("nn======= Query "); // Определение выражения запроса IEnumerable
Источник данных, создание и выполнение запроса ü Запрос представляет собой выражение для получения данных из источника данных. ü Операция запроса LINQ включает • получение источника данных; • создание запроса; • выполнение запроса. ü В LINQ выполнение запроса отделено от самого запроса — создание переменной запроса само по себе не связано с получением данных, в ней просто хранятся сведения, необходимые для последующего выполнения запроса. ü Источник данных LINQ — это любой объект, поддерживающий универсальный интерфейс IEnumerable
Выражение запроса ü Все переменные в выражении запроса имеют строгую типизацию, хотя обычно нет необходимости явно указывать тип – компилятор сам его вычисляет. ü При компиляции выражения запросов преобразуются в вызовы методов расширения, которые поддерживают стандартные операторы запросов. ü Любой запрос, который может быть выражен с помощью синтаксиса запросов, также может быть выражен с помощью синтаксиса методов, нет различий в производительности. ü Синтаксис запросов и методов можно использовать одновременно. ü Некоторые запросы, например, Count или Max, не имеют соответствующих предложений в выражениях запросов, их можно создать только с помощью вызова методов.
Немедленное или отложенное выполнение запроса ü Переменная запроса хранит только команды запроса и не содержит результатов запроса. Запросы выполняются одним из двух способов: немедленным или отложенным. ü При немедленном выполнении операция выполняется в той точке кода, где объявлен запрос. Немедленно выполняются все стандартные операторы запросов, возвращающие один неперечислимый результат, например, поиск максимального элемента или среднего значения. ü При отложенном выполнении запроса операция выполняется только после перечисления переменной запроса, например в операторе foreach. Результат выполнения запроса зависит от содержимого источника данных в момент выполнения запроса, а не при его определении. Один и тот же запрос может возвращать разные результаты, если между его выполнениями изменяются данные в источнике.
Сохранение результатов запроса в памяти ü Чтобы принудительно вызвать немедленное выполнение любого запроса и сохранить его результат, можно вызвать один из методов public static TSource[] To. Array
Сохранение результатов запроса в памяти. Пример ü В примере результат запроса сохраняется в коллекции List
Операции с данными, выполняемые с помощью стандартных операций запросов • Сортировка данных • Группировка данных • Операции над множествами • Операции создания • Фильтрация данных • Операции равенства • Операции квантификаторов • Операции с элементами • Операции проецирования • Преобразование типов данных • Разделение данных • Операции объединения • Операции соединения • Операции статистической обработки
Некоторые операции статистической обработки ü Статистические операции вычисляют одно неперечислимое значение для последовательности. ü Для этих операций нет соответствующих предложений в выражениях запросов, используются вызовы методов. Запрос выполняется в точке создания. Average Вычисляет среднее значение для элементов коллекции. 20 перегрузок. Перегружен для применения функции преобразования к каждому элементу входной последовательности. Count Возвращает число элементов в коллекции. Перегружен для подсчета элементов, которые удовлетворяют функции предиката. Max Возвращает максимальный элемент в коллекции. Более 20 перегрузок. Min Возвращает минимальный элемент в коллекции. Более 20 перегрузок. Sum Вычисляет сумму значений элементов последовательности. Перегружен для применения функции преобразования к каждому элементу входной последовательности.
Максимальный элемент. Примеры ü Метод возвращает максимальное значение в последовательности, реализующей IEnumerable
Максимальный элемент. Пример 1 ü В классе Book реализованы интерфейсы IComparable
Максимальный элемент. Пример 2 ü Первый метод расширения вызывает функцию преобразования ( отвечающую делегату Func
Выражения запросов ü Для создания выражений запросов LINQ в C# добавлены новые ключевые слова from let by join ascending in where descending orderby group on select into equals ü Выражение запроса начинается с предложения from и оканчивается предложением select или group. ü Между ними может содержаться одно или несколько необязательных предложений where, orderby, join, let или других предложений from. IEnumerable
Предложение from ü Предложение from задает источник данных вместе с переменной диапазона - локальной переменной для представления элемента входной последовательности. ü Переменная диапазона строго типизирована, ее тип определяется на основе типа элементов в источнике данных. ü В следующем примере переменная диапазона item имеет тип Book, так как определенный в классе Books. Library итератор последовательно возвращает элементы из коллекции List
Предложение select ü Выражение запроса должно завершаться предложением select или group. ü Простое предложение select создает последовательность объектов с тем же типом, как и у элементов исходной последовательности. ü С помощью предложения select можно создавать последовательности объектов новых типов. Такое преобразование называют проекцией. ü При компиляции предложение select преобразуется в вызов одного из методов расширения public static IEnumerable
Операции проецирования. Пример ü В следующем примере • в первом запросе предложение select создает проекцию, состоящую из объектов типа string со значениями свойства Title переменной диапазона; • во втором запросе предложение select создает проекцию, состоящую из объектов анонимного типа, который имеет свойства типа string и типа int. var Query_1 = from item in lib select item. Title; foreach (var item in Query_1) Console. Write. Line(item); var Query_2 = from item in lib select new { Name = item. Title, Year = item. Year }; foreach (var item in Query_2) Console. Write. Line(item); ü Исходная последовательность ======= Books. Library C# Trey Nash 2010 C++ Bjarn Straustroup 2008 ü Результат выполнения запросов C# C++ { Name = C#, Year = 2010 } { Name = C++, Year = 2008 }
Фильтрация данных. Предложение where ü Фильтрацией называется операция выбора элементов, которые удовлетворяют заданному условию. ü В языке запросов для фильтрации используется предложение where, которое при компиляции преобразуется в вызов одного из следующих методов расширения public static IEnumerable
Фильтрация данных. Пример 1 ü В примере из коллекции класса Books. Library выбираются только те элементы, в название которых входит подстрока C#. var Query_1 = from item in lib where item. Title. Contains("C#") select item; foreach (Book book in Query_1) Console. Write. Line(book); ü Тот же самый запрос можно реализовать в форме вызова метода расширения с использованием для предиката лямбда-выражения или обычного метода var Query_2 = lib. Where(x => x. Title. Contains("C#")); foreach (Book book in Query_2) Console. Write. Line(book); var Query_3 = lib. Where( selector_by_title ); foreach (Book book in Query_3) Console. Write. Line(book); ü Метод selector_by_title отвечает делегату Func
Фильтрация данных. Пример 2 ü В одном предложении where можно указать несколько предикатов, если использовать операторы && и ||. ü В следующем примере из коллекции класса Books. Library выбираются только те элементы, в название которых входит подстрока C# или подстрока C++. var Query_4 = from item in lib where item. Title. Contains("C++") || item. Title. Contains("C#") select item; foreach (Book book in Query_4) Console. Write. Line(book); ü Исходная последовательность ======= Books. Library C# Trey Nash 2010 C++ Bjarn Straustroup 2008 C# Эндрю Троелсен 2005 Темные аллеи Иван Бунин 2004. . ü Результат выполнения запроса C# Trey Nash 2010 C++ Bjarn Straustroup 2008 C# Эндрю Троелсен 2005
Фильтрация данных. Пример 3 ü Предложение where может содержать методы, возвращающие значения булевского типа. В следующем примере предложение where использует метод, проверяющий выполнение некоторого условия. var Query_5 = from item in lib where complex_selector (item) select item. Author; foreach (string author in Query_5) Console. Write. Line(author); ü Метод, проверяющий выполнение условия: static bool complex_selector(Book book) { return book. Author[0]=='B'; } ü Исходная последовательность ======= Books. Library C# Trey Nash 2010 C++ Bjarn Straustroup 2008 C# Эндрю Троелсен 2005 Темные аллеи Иван Бунин 2004 ü Результат выполнения запроса Bjarn Straustroup
Сортировка ü Предложение orderby используется для сортировки результатов в порядке возрастания или убывания. ü Сортировка использует функцию сравнения для элементов типа T на основе: • универсального интерфейса System. IComparable
Сортировка. Пример ü При выполнении сортировки можно задать порядок дополнительной сортировки. ü В следующем примере выполняется основная сортировка объектов Book по свойству Title в порядке убывания, дополнительная сортировка выполняется по свойству Year. По умолчанию выполняется сортировка по возрастанию. var Query_6 = from item in lib orderby item. Title descending, item. Year select item; foreach (Book book in Query_6) Console. Write. Line(book); ü До сортировки C# Trey Nash 2010 C++ Bjarn Straustroup 2008 C# Эндрю Троелсен 2005 Темные аллеи Иван Бунин 2004 Война и мир Лев Толстой 2007 ü После сортировки Темные аллеи Иван Бунин 2004 Война и мир Лев Толстой 2007 C++ Bjarn Straustroup 2008 C# Эндрю Троелсен 2005 C# Trey Nash 2010 ü Дополнительная сортировка выполняется методами расширения Then. By и Then. By. Descending.
Группирование данных ü Группированием называется операция размещения по группам данных с равными значениями ключа. ü Для группировки используется предложение group в форме group … by или group … by … into …. , которые при компиляции преобразуются в вызов одного из методов расширения Group. By (8 перегрузок) public static IEnumerable
Группирование данных. Пример ü В следующем примере в первом запросе выполняется группировка объектов Book по первому символу в свойстве Title, во втором запросе – свойства Title по числу символов в значении свойства. var Query_8 = from item in lib group item by item. Title[0]; foreach (var group in Query_8) { Console. Write. Line("n Key = " + group. Key ); foreach (Book book in group) Console. Write. Line(book); } var Query_9 = from item in lib group item. Title by item. Title. Length; foreach (var group in Query_9) { Console. Write. Line(“ Key = " + group. Key); ü Результат выполнения запроса 1 foreach (object item in group) Console. Write. Line(item); } ü Исходная последовательность ü Результаты выполнения запросов ======= Books. Library C# Trey Nash 2010 C++ Bjarn Straustroup 2008 C# Эндрю Троелсен 2005 Темные аллеи Иван Бунин 2004 Война и мир Лев Толстой 2007 Key = C C# Trey Nash 2010 C++ Bjarn Straustroup 2008 C# Эндрю Троелсен 2005 Key = Т Темные аллеи Иван Бунин 2004 Key = В Война и мир Лев Толстой 2007 Key = 2 C# C# Key = 3 C++ Key = 12 Темные аллеи Key = 11 Война и мир
Группировка по составному ключу ü Группировка по нескольким ключам выполняется с помощью составных ключей. Для составного ключа можно использовать анонимный тип. ü В следующем примере выполняется группировка объектов Book по значениям свойств Author и Year. В запросе используется анонимный тип. var Query_10 = from item in lib group item by new { key 1 = item. Title, key 2 = item. Year }; foreach (var group in Query_10) { Console. Write. Line("n Key = " + group. Key); foreach (object item in group) Console. Write. Line(item); } ü Исходные данные : ======= Books. Library C# Trey Nash 2010 C++ Bjarn Straustroup 2008 C# Эндрю Троелсен 2005 C# Герберт Шилдт 2005 ü Результат : Key = { key 1 = C#, key 2 = 2010 } C# Trey Nash 2010 Key = { key 1 = C++, key 2 = 2008 } C++ Bjarn Straustroup 2008 Key = { key 1 = C#, key 2 = 2005 } C# Эндрю Троелсен 2005 C# Герберт Шилдт 2005
Предложение into ü Если после операций выбора или группирования , выполняются другие операции, в предложениях select и group можно использовать ключевое слово into для создания временного идентификатора, в котором хранится запрос. ü В примере сначала выполняется группировка по значению свойства Title, а затем отбираются только группы, содержащие более одного элемента. var Query_11 = from item in lib group item by item. Title into title. Group where title. Group. Count() > 1 select title. Group; foreach (var group in Query_11) { Console. Write. Line("n Key = " + group. Key); foreach (object item in group) Console. Write. Line(item); } ü Исходные данные : ======= Books. Library C# Trey Nash 2010 C++ Bjarn Straustroup 2008 C# Эндрю Троелсен 2005 C# Герберт Шилдт 2005 C++ Herbert Schildt 1998 Темные аллеи Иван Бунин 2004 Война и мир Лев Толстой 2007 ü Результат : Key = C# C# Trey Nash 2010 C# Эндрю Троелсен 2005 C# Герберт Шилдт 2005 Key = C++ Bjarn Straustroup 2008 C++ Herbert Schildt 1998
Предложение let ü Предложение let создает и инициализирует новую переменную диапазона, которую можно использовать в следующих предложениях. ü В следующем примере создается новая переменная диапазона с именем names, сохраняющая результат вызова метода Split для свойства Author, которая затем используется в запросе сортировки по длине строк. var Query_12 = from item in lib let names = item. Author. Split(' ') from name in names orderby name. Length select name; foreach (var item in Query_12) Console. Write. Line(item); ü Исходные данные : ======= Books. Library C# Trey Nash 2010 C++ Bjarn Straustroup 2008 C# Эндрю Троелсен 2005 C++ Herbert Schildt 1998 ü Результат : Trey Nash Bjarn Эндрю Herbert Schildt Троелсен Straustroup
Операции соединения ü Соединение — это операция связывания элементов из двух источников данных на основе общего ключа. ü LINQ поддерживает два метода соединения Join и Group. Join, которые выполняют объединение по эквивалентности — соединение двух источников данных на основании совпадения их ключей. ü Операции выполняются над двумя последовательностями с элементами разных типов. Можно использовать анонимный тип, чтобы объединить свойства связанных элементов в новый тип для выходной последовательности. ü Запрос join … on … equals … cоединяет две последовательности на основе функции выбора ключа и извлекает пары значений. ü Запрос join … on … equals … into … соединяет две последовательности на основе функции выбора ключа и группирует полученные совпадения для каждого элемента.
Операции соединения ü Запрос join … on … equals … компилируется в метод расширения public static IEnumerable
Операции квантификаторов ü Квантификаторы возвращают значение булевского типа, которое указывает, удовлетворяют ли условию некоторые или все элементы в последовательности. Выполняются только с помощью вызовов методов расширения. All Определяет, все ли элементы последовательности удовлетворяют условию. Any Определяет, удовлетворяют ли некоторые элементы последовательности условию. Contains Определяет, содержит ли последовательность указанный элемент. Некоторые операции с элементами ü Операции с элементами возвращают один определенный элемент из последовательности. Выполняются только с помощью вызовов методов расширения. First Возвращает первый элемент коллекции, удовлетворяющий условию, или генерирует исключение. First. Or. Default Возвращает первый элемент коллекции, удовлетворяющий условию, или значение по умолчанию. Single Возвращает единственный элемент коллекции элемент, удовлетворяющий условию, или генерирует исключение.
Операции над множествами ü Операции образуют последовательность, основанную на наличии или отсутствии совпадающих элементов внутри одной или двух последовательностей. Выполняются только с помощью вызовов методов расширения. ü Разные перегрузки методов используют метод проверки на равенство элементов по умолчанию или метод, который передается как параметр. Distinct Удаляет повторяющиеся значения из последовательности. Except Возвращает разность множеств — набор элементов одной коллекции, которые не содержатся во второй коллекции. Intersect Возвращает пересечение множеств. Union Возвращает объединение множеств.
Операции разделения данных ü Операции выполняют разделение входной последовательности на два раздела без изменения порядка элементов. Выполняются только с помощью вызовов методов расширения. Skip Пропускает заданное число элементов последовательности. Skip. While Пропускает элементы, которые удовлетворяют условию функции предиката, до тех пор, пока не встретится элемент, для которого условие не выполнено. Возвращает оставшиеся элементы. Take Принимает заданное число элементов с начала последовательности. Take. While Принимает элементы до тех пор, пока элемент не удовлетворит условию функции предиката.