Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Методические материалы

.pdf
Скачиваний:
9
Добавлен:
07.02.2016
Размер:
606.53 Кб
Скачать

Учебный центр ВМК МГУ & Softline Academy www.it-university.ru

Новые возможности

Visual Studio 2008

Москва, 2008 г.

2

Новые возможности Visual Studio 2008

Оглавление

 

 

НОВЫЕ ВОЗМОЖНОСТИ ЯЗЫКА C# .................................................................................................................................

 

3

АВТОМАТИЧЕСКОЕ СОЗДАНИЕ СВОЙСТВ..............................................................................................................................................

 

3

КЛЮЧЕВОЕ СЛОВО VAR, АНОНИМНЫЕ ТИПЫ И НОВЫЙ СИНТАКСИС ИНИЦИАЛИЗАЦИИ ОБЪЕКТА

......................................................4

АНОНИМНЫЕ ТИПЫ...............................................................................................................................................................................

 

6

ЧАСТИЧНЫЕ МЕТОДЫ............................................................................................................................................................................

 

7

ПРИСОЕДИНЕННЫЕ МЕТОДЫ.................................................................................................................................................................

 

8

ЛЯМБДА-ВЫРАЖЕНИЯ...........................................................................................................................................................................

 

9

LINQ ...................................................................................................................................................................................................

 

12

WPF И WCF – НОВЫЙ ПОДХОД К СТАРЫМ ЗАДАЧАМ............................................................................................

 

15

WINDOWS PRESENTATION FOUNDATION .............................................................................................................................................

 

15

WINDOWS COMMUNICATION FOUNDATION .........................................................................................................................................

 

21

ОБНОВЛЕНИЕ VISUAL STUDIO .........................................................................................................................................

 

22

ДИЗАЙНЕР LINQ TO SQL....................................................................................................................................................................

 

22

НОВЫЙ СТИЛЬ РАЗРАБОТКИ ИНТЕРФЕЙСА С ПОМОЩЬЮ WPF ...........................................................................................................

 

23

ASP.NET 3.5........................................................................................................................................................................................

 

24

ПРАКТИЧЕСКАЯ РАБОТА 1. ИСПОЛЬЗОВАНИЕ LINQ .............................................................................................

 

25

ЗАДАНИЕ 1. ФИЛЬТРАЦИЯ ПО ВРЕМЕНИ. ............................................................................................................................................

 

25

ЗАДАНИЕ 2. ФИЛЬТРАЦИЯ ПО ТИПУ....................................................................................................................................................

 

25

ЗАДАНИЕ 3. .........................................................................................................................................................................................

 

25

ПРАКТИЧЕСКАЯ РАБОТА 2. РАЗРАБОТКА ПРИЛОЖЕНИЯ WPF..........................................................................

 

26

СОЗДАТЬ ПРОЕКТ WPF........................................................................................................................................................................

 

26

РАЗДЕЛИТЬ ОКНО НА ДВА СТОЛБЦА....................................................................................................................................................

 

26

ДОБАВИТЬ СПИСОК .............................................................................................................................................................................

 

26

ДОБАВИТЬ ОБРАБОТЧИК СОБЫТИЯ......................................................................................................................................................

 

27

ЗАПУСТИТЬ ПРИЛОЖЕНИЕ...................................................................................................................................................................

 

27

ССЫЛКИ И КНИГИ................................................................................................................................................................

 

28

Новые возможности Visual Studio 2008

3

Новые возможности языка C#

С выходом второй версии .Net framework в языке C# произошел ряд изменений, наверное, самым главным из которых стало появление новой синтаксической конструкции – обобщения (generic). Обобщения сильно изменили стиль программирования на C#, значительно упростив разработку однотипных наборов классов.

Однако с появлением версии 3.0 никаких изменений в языке не произошло, да и вообще третья версия отличается от второй только появлением трех новых библиотек: WPF, WCF и WF, которые работают в среде .Net 2.0 по-отдельности не требуя специальной установки.

Как это ни странно, версия 3.5 настолько сильно отличается от 3.0, что мотивы, побудившие Microsoft присвоить ей промежуточный номер, совершенно непонятны.

Автоматическое создание свойств

Очень и очень часто программисту приходится «заворачивать» поля класса в свойства, не добавляя никакого содержательного кода:

class DumbProperty

{

private int _x;

public int X

{

get { return _x; } set { _x = value; }

}

}

Свойство X в данном случае совершенно бессмысленно – аксессоры get и set не делают ничего – просто «пропускают» сквозь себя значение. Тем не менее, такой подход считается хорошим стилем программирования. Если в дальнейшем возникнет необходимость выполнять дополнительные действия при изменении значения X, мы сможем легко и просто модифицировать аксессоры. И, тем не менее, это громоздкий и уродливый образец кода. Особенно ужасно выглядят классы, в которых таких свойств набирается 10-15 штук. Нельзя ли как-нибудь избежать этого? В C# 2.0 – никак.

Между тем, в C# есть синтаксическая конструкция во многом похожая на свойства. Я имею в виду события (event). Они тоже являются «оберткой», но не над обычными полями, а над полями-делегатами. У них тоже есть аксессоры, но не get и set, а add и remove. И в отличие от свойств, аксессоры для событий генерируются автоматически. Создадим для примера вот такой простой класс:

class EventSample

{

public event ThreadStart threadBody;

}

Если скомпилировать его посмотреть на получившуюся сборку с помощью Reflector то мы увидим, что компилятор добавил в класс дополнительное закрытое поле и методы-аксессоры, так что получилось приблизительно следующее:

4

Новые возможности Visual Studio 2008

class EventSample

{

private ThreadStart _threadBody;

public event ThreadStart threadBody

{

add { _threadBody += value; } remove { _threadBody -= value; }

}

}

Согласитесь, очень похоже на свойства. Только для свойств мы вынуждены писать все это вручную. Нельзя ли и для свойств создавать аксессоры автоматически? Теперь можно. Я могу переписать мой класс DumbProperty следующим образом:

class IntelligentProperty

{

public int X { get; set; }

}

Результат компиляции будет тот же что и раньше. Компилятор самостоятельно создаст закрытое поле для хранения значения X и аксессоры, обращающиеся к нему. Не правда ли так код выглядит значительно опрятнее? Еще одно важное преимущество такого кода состоит в том, что теперь программист не сможет напрямую обратиться к полю, минуя аксессор.

Ключевое слово var, анонимные типы и новый синтаксис инициализации объекта

Вообще говоря практически все нововведения в C# тесно связаны между собой и «поддерживают» друг друга. Особенно тесно связаны между собой анонимные типы, ключевое слово var и новый, более лаконичный способ конструирования объекта. С одной стороны это три отдельных новинки, но с другой

– совершенно непонятно, с которой из них начинать. Поэтому выберу наугад:

Ключевое слово var

В C# 3.5 переменную можно задать следующим, несколько странным образом:

var i = 10;

На первый взгляд кажется, что мы объявили переменную i типа var. Но на самом деле это не так. Типа var не существует. Var означает, что компилятор должен определить тип переменной по значению, которое заносится в эту переменную. Поскольку в данном случае мы заносим число десять, то компилятор решает, что i будет иметь тип System.Int32, более известный просто как int. Почему не System.Int16 или не System.UInt32? Так решили разработчики. По числу десять нельзя точно сказать, какого оно типа, поэтому компилятор вынужден проявить определенную смекалку.

Не нужно думать, что var позволяет динамически менять тип переменной, так как это можно делать в нестрого типизированных языках. Нет, тип жестко задается при объявлении переменной, и изменить его в дальнейшем нельзя. Например, такой фрагмент кода компилироваться не будет:

Новые возможности Visual Studio 2008

5

var str = "qwerty"; i = 10;

В первой строке компилятор определит, что тип переменной – string, а во второй обнаружит, что мы пытаемся поместить в эту переменную число, а этого делать нельзя, поскольку неявного преобразования числа в строку не существует. Использовать var для создания неинициализированных переменных нельзя, поскольку компилятору неоткуда определить тип переменной.

Var можно использовать для объявления переменных любых типов, а не только элементарных. Например, я могу создать переменную типа BindingSource:

var p = new BindingSource();

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

var d = AppDomain.CreateDomain("Example");

Переменная d получит тип AppDomain;

На первый взгляд var кажется практически бесполезной новинкой, и более того, даже вредной – программа, написанная с использованием этого ключевого слова, становится более запутанной, поскольку мы не можем явно увидеть тип переменной, и это может приводить к сложно обнаружимым ошибкам.

Тем не менее есть причина, оправдывающая существование var: это ключевое слово необходимо для того, чтобы использовать анонимные типы.

Новый синтаксис инициализации объекта

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

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

//конструктор с параметрами

Person p = new Person("Иванов", "Иван", 21);

//конструктор без параметров

Person p = new Person(); p.FirstName = "Иван"; p.LastName = "Иванов"; p.Age = 21;

Первый вариант гарантирует, что мы не сможем создать неинициализированный объект. Кроме того, он короче – всего одно выражение вместо четырех. И, тем не менее, второй вариант тоже имеет право на

6 Новые возможности Visual Studio 2008

жизнь. Если у объекта много параметров и некоторые из них необязательные, то задавать каждый из них по-отдельности значительно удобнее, чем создавать несколько конструкторов с разным набором параметров. Кроме того, многие библиотеки, например Spring.Net или WPF требуют, чтобы у конструктора не было параметров, а вся дополнительная информация передавалась через свойства класса.

Так или иначе, за первым способом есть одно неоспоримое преимущество: параметризованный конструктор создает готовый экземпляр объекта за один вызов.

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

Такой способ жизненно необходим для нестандартных атрибутов, поскольку мы должны описывать нестандартный атрибут одним единственным выражением. К сожалению так можно описывать только нестандартные атрибуты. Вот если бы так можно было создавать и экземпляры обычных классов…

И в C# 3.5 это можно сделать. Теперь мы можем задавать значения свойств класса почти так же, как параметры конструктора. Если непосредственно после конструктора указать в фигурных скобках несколько пар ключ-значение, они будут интерпретированы как присвоение значений свойствам класса. Например, такой фрагмент полностью аналогичен использовавшемуся выше.

Person p = new Person()

{ FirstName = "Иван", LastName = "Иванов", Age=21};

Заметьте, никаких изменений в существующие классы вносить не требуется! Это просто новый способ записи нескольких выражений.

Само по себе это нововведение конечно удобно, особенно в тех случаях когда мы вынуждены использовать класс, написанный в стиле «конструктор без аргументов». Без него чаще всего можно обойтись, задав значения параметров в виде нескольких выражений как раньше. Так может быть это нововведение и не нужно?

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

Кроме того, именно таким способом создаются анонимные типы.

Анонимные типы.

Давайте, совместим в одном выражении ключевое слово var и новый стиль инициализации: var p = new Person()

{ FirstName = "Иван", LastName = "Иванов", Age=21};

Компилятор определяет тип переменной p по конструктору, стоящему в правой части выражения, а затем инициализирует новую переменную заданными значениями. Вообще говоря, в этом выражении содержится довольно таки много информации о типе Person: мы видим, что у него есть три свойства, FirstName, LastName и Age. Типы этих свойств явно не видны, но мы можем предположить, что первые два имеют тип string, а третий – int. И Может так оказаться, что больше никаких членов в классе Person нет, то есть класс Person «пассивный», и просто служит контейнером для хранения данных. Такие типы нередко встречаются в приложениях работающих с базами данных, очень похожим образом устроены

Новые возможности Visual Studio 2008

7

классы, в которых обычно передаются параметры событиям. В общем, это нередкая ситуация. При этом программист вынужден где-то заранее описывать класс Person, даже если он потребуется всего один раз. В новом варианте языка C# я могу немного сократить приведенный выше пример:

var p = new

{ FirstName = "Иван", LastName = "Иванов", Age=21};

Крошечное изменение, куда-то потерялось название класса, экземпляр которого я собираюсь создать… И тем не менее эта строка скомпилируется. Но как, спросите вы: ведь мы не указали экземпляр какого типа мы собираемся создать. Неужели компилятор каким-то мистическим способом догадается, что это тип Point?

Нет, разумеется нет. Компилятор создаст экземпляр так называемого анонимного типа. То есть, не обнаружив названия типа он неявно создаст новый тип, в котором будут нужные свойства нужных типов. Причем после компиляции он станет вполне полноценным типом, и его можно будет обнаружить в получившейся сборке с помощью ILDASM или Reflector.

Частичные методы

С выходом .Net 2.0 в C# появилась новая синтаксическая конструкция, частичные классы. Обычный класс должен быть целиком описан в одном файле, а описание частичного класса может быть разбито по нескольким файлам. Программистам практически не нужна эта возможность – никому не приходит в голову разбивать описание по нескольким файлам. Однако это очень удобно, когда программист использует какое-нибудь инструментальное средство для генерации части кода. Например, дизайнер Windows Forms автоматически создает код, описывающий содержимое формы. В Visual Studio 2003 он записывал этот код в тот же самый файл, в котором программист описывал логику работы приложения. В Visual Studio 2005 этот код пишется в отдельный файл, и вероятность того, что программист случайно повредит его, минимальна.

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

Частичные методы могут использоваться только внутри частичных классов. Отсюда они вероятно и получили свое название. Один из файлов, в которых описывается класс, должен содержать описание метода – название, тип возвращаемого значения и список параметров, но без тела метода, например вот так:

partial class PartialMethodSample

{

partial void PrintMessage(string msg);

public void Connect()

{

PrintMessage("Connecting");

//Здесь должен быть код, //выполняющий соединение с базой данных

PrintMessage("Connected");

}

8

Новые возможности Visual Studio 2008

}

Частичный метод PrintMessage должен выводить сообщение, но мы не описываем его здесь а только определяем его сигнатуру. Метод Conect() использует этот метод для вывода сообщений.

В другом файле мы напишем тело этого метода:

partial class PartialMethodSample

{

partial void PrintMessage(string msg)

{

Console.WriteLine(msg);

}

}

Ну и зачем нам нужно ключевое слово partial? - спросите вы. Все это можно было сделать и раньше, да к тому же не нужно было заранее объявлять метод в первом файле!

Все дело в том, что у частичного метода тела может и не быть. Даже если я целиком уберу второй файл, проект все равно будет отлично компилироваться. Но как же так? Получается, что мы вызываем метод, у которого нет тела! Но это же бессмысленно!

Если компилятор обнаруживает вызов частичного метода, для которого отсутствует тело, а есть только описание сигнатуры, он просто пропускает этот вызов и не компилирует его. Так, как если бы вызова этого метода вообще бы и не было.

Такое поведение кажется странным, но давайте вспомним, зачем были придуманы частичные классы. Для удобства автоматической генерации кода. И частичные методы нужны для того же самого. Предположим, что первый файл сгенерировал какой-нибудь мастер или визуальный редактор. Он не знает, нужно ли выводить сообщения, а если и нужно, то куда: в консоль, в базу данных или каждое из них нужно показать отдельным окошком. Тем не менее, наш мастер создает частичный метод, использует его в своем коде, но тела не определяет. Предполагается, что программист, использующий этот автоматически сгенерированный класс, просто создаст дополнительный файл, в котором укажет, куда и как выводить сообщения. В нашем примере – на консоль.

Этот пример совершенно не надуман. Например sqlmetal, автоматически генерирующий классы для доступа к базам данных, поступает именно так.

Присоединенные методы

При работе с различными библиотечными классами у меня достаточно часто возникает желание добавить к ним какой-либо свой метод. С моей точки зрения разработчики этих классов чего-то не учли. Мне нужен метод, а его нет.

Теоретически, я мог бы создать потомка этого класса и объявить в нем все недостающие мне методы и свойства, но на практике это часто оказывается неосуществимым: либо наследование запрещено, либо экземпляры этого класса создает не мой код, а код в другом библиотечном классе. Что я могу сделать в такой ситуации? Могу создать класс-обертку, но это довольно-таки трудоемкая задача. Могу поступить проще – создать отдельный класс, в нем объявить статический метод, и этому методу передавать первым параметром экземпляр класса, к которому я хочу как бы «добавить» этот метод.

Давайте представим себе, что некий сторонний разработчик предоставил нам такой класс: class Triangle

{

Новые возможности Visual Studio 2008

9

public double a; public double b; public double c;

public double GetTotalSideLength()

{

return a + b + c;

}

}

Он представляет треугольник со сторонами a, b, и c и позволяет узнать суммарную длину сторон с помощью метода GetTotalSideLength. К сожалению, разработчик забыл добавить метод, вычисляющий площадь этого треугольника, а мне он очень нужен. Я создаю класс TriangleExtension:

static class TriangleExtension

{

public static double Area(Triangle t)

{

return Math.Sqrt(t.a * t.a * t.b * t.b – Math.Pow((t.c * t.c - t.a * t.a - t.b * t.b), 2))/2;

}

}

Теперь я могу использовать свой метод для вычисления площади: double area = TriangleExtension.Area(tri);

Таким способом я имитирую добавление нового метода к классу. На самом деле я бы конечно хотел бы получить немного иное. Я бы хотел, чтобы в результате моих действий я мог написать

double area = tri.Area();

но при этом не модифицировать класс Triangle.

Я думаю, вы уже догадываетесь, что в C# 3.5 это можно сделать. Необходимо всего лишь добавить ключевое слово this к описанию метода Area.

static class TriangleExtension

{

public static double Area(this Triangle t)

{

return Math.Sqrt(t.a * t.a * t.b * t.b – Math.Pow((t.c * t.c - t.a * t.a - t.b * t.b), 2))/2;

}

}

Ключевое слово this можно указывать только для первого параметра и только в статических методах внутри статических классов. Методы, отмеченные этим ключевым словом называют присоединенными методами (extension methods).

Лямбда-выражения

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

10 Новые возможности Visual Studio 2008

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

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

Класс List включает в себя метод Sort который позволяет отсортировать элементы списка. Этот метод реализует алгоритм сортировки, но он ничего не знает о том, как сравнивать между собой элементы. У этого метода существует несколько перегруженных версий, и в одной из них ему нужно передать делегат, определяющий способ сравнения двух элементов между собой. Фактически мы передаем в качестве параметра алгоритм сравнения двух элементов.

Допустим, я хочу отсортировать строки по их длине. Это можно сделать следующим способом:

class Program

{

public static int CompareLength(string s1, string s2)

{

return s1.Length - s2.Length;

}

static void Main()

{

List<string> city = new List<string>() {"Москва", "Санкт-Петербург", "Екатеринбург", "Новосибирск"};

city.Sort(new Comparison<string>(Program.CompareLength));

foreach (string s in city)

{

Console.WriteLine(s);

}

}

}

Такой фрагмент кода даст необходимый результат, но какой он неопрятный и разбухший! Во-первых, алгоритм сортировки пришлось вынести в отдельную функцию, причем нам пришлось придумывать название этой функции. Во-вторых, нам пришлось создавать делегат для того, чтобы «обернуть» в него нашу функцию, ведь в C# нельзя передавать функции в виде параметров – только делегаты. Такой синтаксис был необходим в .Net 1.1, но с тех пор язык C# стал значительно лаконичнее.

Во-первых, сейчас нам не нужно явно создавать делегат. Мы можем написать так:

city.Sort(Program.CompareLength);

Компилятор самостоятельно догадается, что в данном случае нужно создать делегат типа Comparison<string> и в конечном итоге сгенерирует идентичный код.

Во-вторых, мы можем не выносить алгоритм сравнения в отдельный метод. В C# 2.0 появились так называемые анонимные методы. Выглядят они следующим образом:

city.Sort(delegate(string s1, string s2)

{