Лекция-12_Пр-е.ppt
- Количество слайдов: 44
Расширения в С# 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 = "Hello"; double d = 1. 0; int[] numbers = new int[] {1, 2, 3}; Dictionary
Автоматические свойства В С# есть удобный механизм доступа к данным объекта - свойства. С их помощью можно открыть доступ к данным, не «выдумывая» интерфейсных методов типа "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 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("(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 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 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 = 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
Инициализаторы //Ex 200. cs using System; using System. Collections. Generic; public class Contact { public Contact() { Phone. Numbers = new List
Анонимные типы При программировании, особенно приложений баз данных, много времени уходит на создание простых классов для представления данных, которые не делают ничего, кроме предоставления свойств. Подобный класс просто хранит структурированные данные. В базе данных или электронной таблице он мог бы просто представлять какую-нибудь строку в таблице. Тогда класс-коллекция, способный хранить экземпляры этого класса, мог бы служить представлением множества строк в этой таблице. Написание кода для подобных классов превращается в рутинную работу, а внесение любых изменений в лежащую в основе схему данных требует добавлять, удалять или изменять их код. Анонимные типы предлагают способ для упрощения этой модели программирования. В частности, они позволяют не определять никаких простых типов для хранения данных, а использовать вместо этого компилятор С# для автоматического создания типов на базе данных, которые в них нужно хранить. Лекция 04. 12. 2012 г. 14
Анонимные типы 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 == "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; 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) — это методы, у которых прототип (определение метода) специфицирован в объявлении частичного класса, а реализация метода не представлена в том же объявлении частичного класса. Фактически, они могут не иметь никакой реализации ни в одном из объявлений одного и того же частичного класса. И если реализации метода нет ни в одном из объявлений частей этого класса, то компилятором не генерируется никакого IL-кода ни для объявления метода, ни для его вызова, ни для оценки аргументов, переданных методу. То есть все обстоит так, как будто этот метод вообще никогда не существовал. Лекция 04. 12. 2012 г. 18
Частичные методы Пример определения частичных методов: 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 { 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) позволяют расширять функциональные возможности типов без внесения изменений в сами типы. Их можно использовать для расширения даже таких типов, которые нельзя изменять, в частности, типов, поставляемых в составе. NET Framework (например, System. String). Под расширением функциональных возможностей типа подразумевается создание метода, который можно было бы вызывать через экземпляр этого типа. Такой метод и называется расширяющим методом. Он может принимать любое количество параметров и возвращать значение любого типа. Лекция 04. 12. 2012 г. 22
Расширяющие методы Для создания и использования расширяющего метода нужно: ü Создать не обобщенный статический класс. ü Добавить в созданный класс расширяющий метод в виде статического метода с использованием специального синтаксиса. ü Удостовериться в том, что в коде, где планируется использовать расширяющий метод, импортируется содержащее его класс пространство имен с помощью оператора using. ü Вызвать расширяющий метод через экземпляр расширяемого типа тем же самым образом, каким бы вызывался и любой другой метод этого типа. Лекция 04. 12. 2012 г. 23
Расширяющие методы Создаём класс (имя – любое) 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 и могут упрощать некоторые аспекты программирования, особенно в случае использования в сочетании с технологией LINQ. Лямбда-выражения использовались в языках программировании вроде LISP издавна. Впервые они были сформулированы в 1936 г. американским математиком Алонзо Чёрчем. Эти выражения представляют сокращённый синтаксис для спецификации алгоритмов. Прежде чем обратиться непосредственно к лямбда-выражениям, рассмотрим эволюцию способов спецификации алгоритма как аргумента метода, т. к. именно в этом и состоит назначение лямбда-выражений. 1. Использование именованных методов До появления C# 2. 0, когда метод или переменная была типизирована так, что требовала delegate, разработчик должен был создавать именованный метод и передавать его имя туда, где требовался delegate (см. пример на следующем слайде). Лекция 04. 12. 2012 г. 26
Лямбда-выражения 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. 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 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. Text; namespace Explicit. Lambda. Arguments { class Program { static void Main(string[] args) { Func
Лямбда-выражения Примеры лямбда-выражений using System; using System. Collections. Generic; using System. Linq; using System. Text; Обобщённый делегат using System. Diagnostics; Action
Лямбда-выражения Примеры лямбда-выражений 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
Лямбда-выражения Примеры лямбда-выражений using System; using System. Collections. Generic; using System. Linq; using System. Text; namespace Finding. Numbers { class Program { static void Main(string[] args) { List
Деревья выражений Дерево выражений— эффективное представление в форме дерева данных лямбда-выражения операций запросов. Эти представления деревьев выражений могут быть вычислены все сразу, так что единственный запрос может быть построен и выполнен на одном источнике данных, таком как база данных. В большинстве примеров, рассмотренных до сих пор, операции выражений выполнялись в последовательной линейной манере. Рассмотрим следующий код: int[] nums = new IEnumerable
Деревья выражений Когда выполняется этот запрос, сначала вызывается операция Where, за ней — операция Order. By. Такое линейное выполнение операций кажется оправданным для данного примера, но стоит подумать о запросе к очень большому источнику данных, такому как база данных. Имело бы смысл для запроса SQL сначала обратиться к базе данных только с оператором Where, чтобы изменить порядок последующих вызовов? Конечно, это не реально для запросов к базе данных, как потенциально и для других типов запросов. Именно здесь приходят на помощь деревья выражений. Поскольку дерево выражений допускает параллельное вычисление и выполнение всех операций в запросе, может быть произведен единственный общий запрос вместо отдельных запросов для каждой операции. Лекция 04. 12. 2012 г. 37
Деревья выражений В рассмотренных выше примерах -выражения были тесно связаны с делегатами, фактически они подменяли функциональность делегатов. Например, «конвертация» -выражения в делегат, происходит так: Func
Деревья выражений Типом переменной expr является Expression
Деревья выражений Важно понять, что деревья выражений являются самостоятельными конструкциями, не обязательно связанными с -выражениями. Их можно создавать, анализировать, модифицировать, комбинировать несколько деревьев выражений для получения более сложных деревьев, наконец, компилировать. Можно даже определить новый язык выражений или реализовать анализатор для существующего языка выражений. Фактически, компилятор работает как анализатор выражений, когда -выражение присваивается экземпляру типа Expression
Деревья выражений using System; using System. Linq. Expressions; public class Entry. Point { static void Main() { var n = Expression. Parameter( typeof(int), "n" ); var expr = Expression
Операции над выражениями Следующий пример демонстрирует, что выражения – это обычные данные, над которыми можно выполнять различные операции. Здесь конструируется, а затем и вычисляется выражение (2(n+1)+n)(2(n+1)): using System; using System. Linq. Expressions; public class Entry. Point { static void Main() { 1 Expression
Использование деревьев выражений Деревья выражений полезны в применении к 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 представлено в данных (как дерево выражений), а не в IL (как делегат), то им можно оперировать. Возможно, имеется алгоритм, который сможет оптимизировать выражение. Или, может оказаться, что когда конкретная реализация анализирует выражение, принимается решение, что все выражение может быть упаковано, отправлено по сети и полностью выполнено на стороне сервера. Деревья выражений обеспечивают эту важную возможность. Затем, когда операции с данными завершаются, можно транслировать дерево выражений в окончательную исполняемую операцию посредством механизма типа Lambda. Expression. Compile и запустить ее. Если бы выражение изначально было доступно только в виде код IL, гибкость была бы существенно ограничена. Лекция 04. 12. 2012 г. 44