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

ASP_NET_MVC_4_Framework_s_primerami_na_C_dlya_p

.pdf
Скачиваний:
25
Добавлен:
19.03.2016
Размер:
17.66 Mб
Скачать

Резюме

В этой главе мы показали вам архитектурный паттерн MVC и сравнили его с некоторыми другими паттернами, которые вы, возможно, видели или слышали. Мы обсудили значимость доменной модели и создали простой ее пример. Мы также показали внедрение зависимости, что позволяет отделять компоненты друг от друга для обеспечения строгого разделения различных частей нашего приложения. Мы продемонстрировали некоторые простые модульные тесты, и вы увидели, как разделенные компоненты и внедрение зависимостей помогают сделать модульное тестирование простым и легким. Попутно мы продемонстрировали наше увлечение TDD и показали, как мы пишем юнит тесты, прежде чем писать код приложения. Наконец, мы затронули интеграционное тестирование и сравнили его с модульным тестированием.

71

Основные особенности языка

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

Мы предоставим только краткий обзор каждой функции. Если вы хотите более глубокого охвата C# или LINQ, для вас могут представлять интерес три книги Адама. Для полного руководства по C# попробуйте Introducing Visual C#; для углубленного изучения LINQ посмотрите Pro LINQ in C#; а для детального изучения .NET поддержки асинхронного программирования обратите внимание на Pro .Net Parallel Programming in C#. Все эти книги изданы Apress.

Создание проекта-примера

Для демонстрации возможностей языка в этой части книги, мы создали новое Visual Studio проект ASP.NET MVC 4 Web Application под названием LanguageFeatures и выбрали опцию шаблона Empty. Функции не являются специфическими для MVC, но Visual Studio Express 2012 для веб не поддерживает создание проектов, которые могут выводить на консоль, так что вам придется создать MVC приложение, если вы хотите следовать за примерами.

Нам понадобится простой контроллер для демонстрации этих особенностей языка, поэтому мы создали файл HomeController.cs в папке Controllers, используя технические приемы, которые мы показали вам в главе 2. Вы можете увидеть начальное содержимое контроллера Home в листинге 4-1.

Листинг 4-1: Начальное содержимое контроллера Home

using LanguageFeatures.Models; using System;

using System.Collections.Generic; using System.Linq;

using System.Web; using System.Web.Mvc;

namespace LanguageFeatures.Controllers

{

public class HomeController : Controller

{

public string Index()

{

return "Navigate to a URL to show an example";

}

}

}

Для каждого примера мы будем создавать методы действия, поэтому результатом метода действия Index является простое сообщение, чтобы не усложнять проект. Для отображения результатов наших методов действия мы добавили представление с именем Result.cshtml в папку Views/Home. Вы можете посмотреть содержимое файла представления в листинге 4-2.

Листинг 4-2: Содержимое файла представления Result

@model String @{

Layout = null;

}

<!DOCTYPE html> <html>

<head>

72

<meta name="viewport" content="width=device-width" /> <title>Result</title>

</head>

<body>

<div>

@Model

</div>

</body>

</html>

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

Использование автоматически реализуемых свойств

Возможности свойств C# позволяют представить часть данных из класса таким образом, что мы получаем чистые данные, независимо от того, как они были установлены и получены. В листинге 4-3 содержится простой пример класса Product, который мы добавили в папку Models проекта

LanguageFeatures.

Листинг 4-3: Определение свойства

using System;

using System.Collections.Generic; using System.Linq;

using System.Web;

namespace LanguageFeatures.Models

{

public class Product

{

private string name; public string Name

{

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

}

}

}

Свойство Name выделено жирным шрифтом. Выражения в блоке кода get выполняются при чтении значения свойства, а выражения в блоке кода set выполняются, когда значение присваивается свойству (специальная переменная value представляет присвоенное значение). Свойство используется другими классами, как показано в листинге 4-4, который представляет метод действия AutoProperty, что мы добавили к контроллеру Home.

Листинг 4-4: Использование свойства

using LanguageFeatures.Models; using System;

using System.Collections.Generic; using System.Linq;

using System.Web; using System.Web.Mvc;

namespace LanguageFeatures.Controllers

{

public class HomeController : Controller

{

public string Index()

{

73

return "Navigate to a URL to show an example";

}

public ViewResult AutoProperty()

{

//создается новый объект Product Product myProduct = new Product();

//устанавливается значение свойства myProduct.Name = "Kayak";

//читается свойство

string productName = myProduct.Name; // генерируется представление return View("Result",

(object)String.Format("Product name: {0}", productName));

}

}

}

Вы видите, что значение свойства читается и устанавливается, как обычное поле. Использование свойств предпочтительнее использования полей, потому что вы можете менять выражения в блоках get и set без необходимости изменения всех классов, которые зависят от свойства.

Совет

Вы заметили, что в листинге 4-4 мы привели второй аргумент для метода View к типу object. Это потому что метод View перегружен, то есть принимает два аргумента String, и это имеет другое значение, чем принимать String и object.

Чтобы избежать не того вызова, мы явно привели второй аргумент. Мы вернемся к методу View и всем его перегрузкам в главе 18.

Вы можете увидеть результат выполнения этого примера, запустив проект и перейдя к /Home/AutoProperty (это направлено на вызов метода действия AutoProperty, и мы получаем шаблон для тестирования каждого примера в этой главе). Поскольку мы передали из метода действия в представление только строку, мы покажем вам результат в виде текста, а не в виде скриншота. Результат листинга 4-4:

Product name: Kayak

Все вроде бы хорошо, но это станет утомительным, когда у нас появится класс, имеющий много свойств, которые служат связующим звеном доступа к полю. И в итоге мы получим нечто излишне громоздкое, как показано в листинге 4-5. Тут представлено, как эти свойства выглядят в файле Product.cs.

Листинг 4-5: Много определений свойств

using System;

using System.Collections.Generic; using System.Linq;

using System.Web;

namespace LanguageFeatures.Models

{

public class Product

{

private int productID; private string name; private string description; private decimal price; private string category; public int ProductID

{

get { return productID; }

74

set { productID = value; }

}

public string Name

{

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

}

public string Description

{

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

}

//...и так далее...

}

}

Мы хотим гибкости свойств, но на данный момент нам не нужны пользовательские выражения получения и установки. Решением является автоматически реализуемое свойство, также известное как автоматическое свойство. При помощи автоматического свойства вы можете создать шаблон свойства, поддерживающего поле, без определения поля или указания кода для выражений получения и установки, как показано в листинге 4-6.

Листинг 4-6: Использование автоматически реализуемых свойств

using System;

using System.Collections.Generic; using System.Linq;

using System.Web;

namespace LanguageFeatures.Models

{

public class Product

{

public int ProductID { get; set; } public string Name { get; set; } public string Description { get; set; } public decimal Price { get; set; } public string Category { set; get; }

}

}

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

С помощью автоматических свойств мы экономим время на наборе текста, создаем код, который легче читать, и при этом сохраняем ту гибкость, которую предоставляют свойства. Если наступит момент, когда нам нужно будет изменить способ реализации свойства, мы сможем вернуться к обычному формату свойства. Давайте представим, что мы должны изменить способ реализации свойства Name, как показано в листинге 4-7.

Листинг 4-7: Возвращение от автоматического к обычному свойству

using System;

using System.Collections.Generic; using System.Linq;

using System.Web;

namespace LanguageFeatures.Models

{

public class Product

{

75

private string name;

public int ProductID { get; set; } public string Name

{

get

{

return ProductID + name;

}

set

{

name = value;

}

}

public string Description { get; set; } public decimal Price { get; set; } public string Category { set; get; }

}

}

Примечание

Обратите внимание, что мы должны реализовать выражения получения и установки, чтобы вернуться к обычному свойству. C# не поддерживает смешивание автоматического и обычного стиля выражений получения и установки в одном свойстве.

Использование инициализаторов объектов и коллекций

Другой утомительной задачей программирования является создание нового объекта, а затем присвоение значений свойствам, как показано в листинге 4-8, который демонстрирует добавление метода действия CreateProduct в контроллер Home.

Листинг 4-8: Создание и инициализация объекта со свойствами

using LanguageFeatures.Models; using System;

using System.Collections.Generic; using System.Linq;

using System.Web; using System.Web.Mvc;

namespace LanguageFeatures.Controllers

{

public class HomeController : Controller

{

public string Index()

{

return "Navigate to a URL to show an example";

}

public ViewResult AutoProperty()

{

// ...выражения опущены для краткости...

}

public ViewResult CreateProduct()

{

//создание нового объекта Product

Product myProduct = new Product();

//установка значений свойств myProduct.ProductID = 100; myProduct.Name = "Kayak";

76

myProduct.Description = "A boat for one person"; myProduct.Price = 275M;

myProduct.Category = "Watersports"; return View("Result",

(object)String.Format("Category: {0}", myProduct.Category));

}

}

}

Мы должны пройти три этапа, чтобы создать объект Product и получить результат: создать объект, установить значения параметров, а затем вызвать метод View, чтобы мы могли увидеть результат через представление. К счастью, мы можем использовать функцию инициализации объекта, которая позволяет создавать и заполнять экземпляр объекта Product за один шаг, как показано в листинге 4-9.

Листинг 4-9: Использование функции инициализации объекта

public ViewResult CreateProduct() {

// создание и заполнение нового объекта Product Product myProduct = new Product {

ProductID = 100, Name = "Kayak", Description = "A boat for one person", Price = 275M, Category = "Watersports"

};

return View("Result", (object)String.Format("Category: {0}", myProduct.Category));

}

Фигурные скобки ({}) после вызова названия Product образуют инициализатор, который мы используем для передачи значений параметров как часть процесса создания. Эта же функция позволяет нам инициализировать содержимое коллекций и массивов как часть процесса создания, что показано в листинге 4-10.

Листинг 4-10: Инициализация коллекций и массивов

using LanguageFeatures.Models; using System;

using System.Collections.Generic; using System.Linq;

using System.Web; using System.Web.Mvc;

namespace LanguageFeatures.Controllers

{

public class HomeController : Controller

{

public string Index()

{

return "Navigate to a URL to show an example";

}

// ...другие методы действия опущены для краткости...

public ViewResult CreateCollection()

{

string[] stringArray = { "apple", "orange", "plum" }; List<int> intList = new List<int> { 10, 20, 30, 40 }; Dictionary<string, int> myDict = new Dictionary<string, int> {

{ "apple", 10 }, { "orange", 20 }, { "plum", 30 } };

return View("Result", (object)stringArray[1]);

}

}

}

77

В листинге показано, как создать и инициализировать массив и два класса из общей библиотеки коллекции. Эта функция располагает удобным синтаксисом – она просто делает C# более приятным в использовании, но не имеет никакого другого воздействия или выгоды.

Использование методов расширения

Методы расширения представляют собой удобный способ добавления методов в классы, которые вам не принадлежат и поэтому не могут быть изменены напрямую. В листинге 4-11 показан класс ShoppingCart, который мы добавили в папку Models и который представляет собой коллекцию объектов Product.

Листинг 4-11: Класс ShoppingCart

using System;

using System.Collections.Generic; using System.Linq;

using System.Web;

namespace LanguageFeatures.Models

{

public class ShoppingCart

{

public List<Product> Products { get; set; }

}

}

Это очень простой класс, который действует как оболочка вокруг List объектов Product ( нам нужен простой класс для данного примера). Предположим, что нам нужна возможность определять общую стоимость объектов Product в классе ShoppingCart, но мы не можем изменить сам класс, например, потому что он получен от третьей стороны и у нас нет исходного кода. К счастью, мы можем использовать метод расширения для получения необходимого функционала. В листинге 4-12 показан класс MyExtensionMethods, который мы также добавили в папку Models.

Листинг 4-12: Определение метода расширения

namespace LanguageFeatures.Models

{

public static class MyExtensionMethods

{

public static decimal TotalPrices(this ShoppingCart cartParam)

{

decimal total = 0;

foreach (Product prod in cartParam.Products)

{

total += prod.Price;

}

return total;

}

}

}

Ключевое слово this перед первым параметром отмечает TotalPrices как расширенный метод. Первый параметр говорит .NET, к какому классу можно применять метод расширения, в нашем случае к ShoppingCart. Мы можем обратиться к экземпляру ShoppingCart, к которому был применен расширенный метод, с помощью параметра cartParam. Наш метод перечисляет объекты Product в ShoppingCart и возвращает сумму свойства Product.Price. В листинге 4-13 показано, как мы применяем метод расширения в новом методе действия UseExtension, который мы добавили контроллеру Home.

78

Примечание

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

Листинг 4-13: Применение метода расширения

using System.Linq; using System.Web; using System.Web.Mvc;

namespace LanguageFeatures.Controllers

{

public class HomeController : Controller

{

public string Index()

{

return "Navigate to a URL to show an example";

}

// ...другие методы действия опущены для краткости...

public ViewResult UseExtension()

{

//создание и заполнение ShoppingCart ShoppingCart cart = new ShoppingCart

{

Products = new List<Product> {

new Product {Name = "Kayak", Price = 275M},

new Product {Name = "Lifejacket", Price = 48.95M}, new Product {Name = "Soccer ball", Price = 19.50M}, new Product {Name = "Corner flag", Price = 34.95M}

}

};

//получение общей стоимости продуктов в корзине decimal cartTotal = cart.TotalPrices();

return View("Result", (object)String.Format("Total: {0:c}", cartTotal));

}

}

}

Ключевым выражением является вот это:

...

decimal cartTotal = cart.TotalPrices();

...

Как вы видите, мы называем метод TotalPrices для объекта ShoppingCart, как будто это часть класса ShoppingCart, даже если это расширенный метод, определенный другим классом. .NET найдет расширенные методы, если они находятся в рамках текущего класса, что обозначает, что они являются частью одного и того же пространства имен или находятся в пространстве имен, которое является предметом выражения using. Вот результат использования метода действия

UseExtension:

Total: $378.40

Применение методов расширения к интерфейсу

Мы также можем создать расширенные методы, применяемые к интерфейсу, который позволяет вызвать метод расширения для всех классов, реализующих этот интерфейс. В листинге 4-14 показан класс ShoppingCart, обновленный для реализации интерфейса IEnumerable<Product>.

79

Листинг 4-14: Реализация интерфейса в классе ShoppingCart

using System;

using System.Collections;

using System.Collections.Generic; using System.Linq;

using System.Web;

namespace LanguageFeatures.Models

{

public class ShoppingCart : IEnumerable<Product>

{

public List<Product> Products { get; set; } public IEnumerator<Product> GetEnumerator()

{

return Products.GetEnumerator();

}

IEnumerator IEnumerable.GetEnumerator()

{

return GetEnumerator();

}

}

}

Теперь мы можем обновить наш метод расширения, чтобы он работал с IEnumerable<Product>, как показано в листинге 4-15.

Листинг 4-15: Метод расширения, который работает с интерфейсом

using System.Collections.Generic; namespace LanguageFeatures.Models

{

public static class MyExtensionMethods

{

public static decimal TotalPrices(this IEnumerable<Product> productEnum)

{

decimal total = 0;

foreach (Product prod in productEnum)

{

total += prod.Price;

}

return total;

}

}

}

Первый тип параметра изменился на IEnumerable<Product>, это обозначает, что цикл foreach в теле метода работает непосредственно с объектами Product. В противном случае метод расширения остается неизменным. Переход на интерфейс означает, что мы можем рассчитать общую стоимость объектов Product, перечисленных любым IEnumerable<Product>, который включает в себя экземпляры ShoppingCart, а также массивы объектов Product, как показано в листинге 4-16.

Листинг 4-16: Применение метода расширения к различным реализациям одного интерфейса

using LanguageFeatures.Models; using System;

using System.Collections.Generic; using LanguageFeatures.Models; using System;

using System.Collections.Generic; using System.Linq;

using System.Web; using System.Web.Mvc;

namespace LanguageFeatures.Controllers

{

80

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]