C# 4.0 полное руководство - 2011 - Герберт Шилдт
Шрифт:
Интервал:
Закладка:
// Продемонстрировать применение оператора join.
using System; using System.Linq;
// Класс, связывающий наименование товара с его порядковым номером, class Item {
public string Name { get; set; } public int ItemNumber { get; set; }
public Item(string n, int inum) {
Name = n;
ItemNumber = inum;
}
}
// Класс, связывающий наименование товара с состоянием его запасов на складе, class InStockStatus {
public int ItemNumber { get; set; } public bool InStock { get; set; }
public InStockStatus (int n, bool b) {
ItemNumber = n;
InStock = b;
}
}
// Класс, инкапсулирующий наименование товара и // состояние его запасов на складе, class Temp {
public string Name { get; set; } public bool InStock { get; set; }
public Temp(string n, bool b) {
Name = n;
InStock = b;
}
}
class JoinDemo {
static void Main() {
Item[] items = {
new Item("Кусачки", 1424), new Item("Тиски", 7892), new Item("Молоток", 8534), new Item("Пила", 6411)
};
InStockStatus[] statusList = {
new InStockStatus(1424, true), new InStockStatus(7892, false), new InStockStatus(8534, true),
new InStockStatus(6411, true) 4
};
// Сформировать запрос, объединяющий объекты классов Item //и InStockStatus для составления списка наименований товаров // и их наличия на складе. Обратите внимание на формирование // последовательности объектов класса Temp, var inStockList = from item in items
join entry in statusList
on item.ItemNumber equals entry.ItemNumber select new Temp(item.Name, entry.InStock);
9
Console.WriteLine("Товар^Наличиеп");
// Выполнить запрос и вывести его результаты. foreach(Temp t in inStockList)
Console.WriteLine("{0}t{l}", t.Name, t.InStock);
}‘
Товар Наличие
Кусачки True Тиски - False Молоток True Пила True
Для того чтобы стал понятнее принцип действия оператора join, рассмотрим каждую строку запроса из приведенной выше программы по порядку. Этот запрос начинается, как обычно, со следующего оператора from.
var inStockList = from item in items
В этом операторе указывается переменная диапазона item для источника данных items, который представляет собой массив объектов класса Item. В классе Item инкапсулируются наименование товара и порядковый номер товара, хранящегося на складе.
Далее следует приведенный ниже оператор join.
join entry in statusList
on item.ItemNumber equals entry.ItemNumber
В этом операторе указывается переменная диапазона entry для источника данных statusList, который представляет собой массив объектов класса InStockStatus, связывающего порядковый номер товара с состоянием его запасов на складе. Следовательно, у массивов items и statusList имеется общее свойство: порядковый номер товара. Именно это свойство используется в части on/equals оператора join для описания связи, по которой из двух разных источников данных выбираются наименования товаров, когда их порядковые номера совпадают.
И наконец, оператор select возвращает объект класса Temp, содержащий наименование товара и состояние его запасов на складе.
select new Temp(item.Name, entry.InStock);
Таким образом, последовательность результатов, получаемая по данному запросу, состоит из объектов типа Temp.
Рассмотренный здесь пример применения оператора join довольно прост. Тем не менее этот оператор поддерживает и более сложные операции с источниками данных. Например, используя совместно операторы into и join, можно создать групповое объединение, чтобы получить результат, состоящий из первой последовательности и группы всех совпадающих элементов из второй последовательности. (Соответствующий пример будет приведен далее в этой главе.) Как правило, время и усилия, затраченные на полное освоение оператора join, окупаются сторицей, поскольку он дает возможность распознавать данные во время выполнения программы. Это очень ценная возможность. Но она становится еще ценнее, если используются анонимные типы, о которых речь пойдет в следующем разделе.
Анонимные типы
В C# предоставляется средство, называемое анонимным типом и связанное непосредственно с LINQ. Как подразумевает само название, анонимный тип представляет собой класс, не имеющий имени. Его основное назначение состоит в создании объекта, возвращаемого оператором select. Результатом запроса нередко оказывается последовательность объектов, которые составляются из членов, полученных из двух или более источников данных (как, например, в операторе join), или же включают в себя подмножество членов из одного источника данных. Но в любом случае тип возвращаемого объекта зачастую требуется только в самом запросе и не используется в остальной части программы. Благодаря анонимному типу в подобных случаях отпадает необходимость объявлять класс, который предназначается только для хранения результата запроса.
Анонимный тип объявляется с помощью следующей общей формы:
new { имя_А = значение_А, имя_В = значение_В, ... }
где имена обозначают идентификаторы, которые преобразуются в свойства, доступные только для чтения и инициализируемые значениями, как в приведенном ниже примере.
new { Count = 10, Max = 100, Min = 0 }
В данном примере создается класс с тремя открытыми только для чтения свойствами: Count, Мах и Min, которым присваиваются значения 10, 100 и 0 соответственно. К этим свойствам можно обращаться по имени из другого кода. Следует заметить, что в анонимном типе используются инициализаторы объектов для установки их полей и свойств в исходное состояние. Как пояснялось в главе 8, инициализаторы объектов обеспечивают инициализацию объекта без явного вызова конструктора. Именно это и требуется для анонимных типов, поскольку явный вызов конструктора для них невозможен. (Напомним, что у конструкторов такое же имя, как и у их класса. Но у анонимного класса нет имени, а значит, и нет возможности вызвать его конструктор.)
Итак, у анонимного типа нет имени, и поэтому для обращения к нему приходится использовать неявно типизированную переменную. Это дает компилятору возможность вывести надлежащий тип. В приведенном ниже примере объявляется переменная шуОЬ, которой присваивается ссылка на объект, создаваемый в выражении анонимного типа.
var myOb = new { Count = 10, Max = 100, Min = 0 }
Это означает, что следующие операторы считаются вполне допустимыми.
Console.WriteLine("Счет равен " + myOb.Count);
if(i <= myOb.Max && i >= myOb.Min) // ...
Напомним, что при создании объекта анонимного типа указываемые идентификаторы становятся свойствами, открытыми только для чтения. Поэтому их можно использовать в других частях кода.
Термин анонимный тип не совсем оправдывает свое название. Ведь тип оказывается анонимным только для программирующего, но не для компилятора, который присваивает ему внутреннее имя. Следовательно, анонимные типы не нарушают принятые в C# правила строгого контроля типов.
Для того чтобы стало более понятным особое назначение анонимных типов, рассмотрим переделанную версию прорраммы из предыдущего раздела, посвященного оператору join. Напомним, что в этой программе класс Temp требовался для инкапсуляции результата, возвращаемого оператором join. Благодаря применению
анонимного типа необходимость в этом классе-заполнителе отпадает, а исходный код программы становится менее громоздким. Результат выполнения программы при этом не меняется.
// Использовать анонимный тип для усовершенствования // программы, демонстрирующей применение оператора join-.
using System; using System.Linq;
// Класс, связывающий наименование товара с его порядковым номером, class Item {
public string Name { get; set; } public int ItemNumber { get; set; }
public Item(string nv int inum) {
Name = n;
ItemNumber = inum;
}
}
// Класс, связывающий наименование товара с состоянием его запасов на складе, class InStockStatus {
public int ItemNumber { get; set; } public bool InStock { get; set; }
public InStockStatus(int n, bool b) {
ItemNumber = n;
InStock = b;
}
}
class AnonTypeDemo { static void Main() {
Item[] items = {
new Item("Кусачки", 1424), new Item("Тиски", 7892), new Item("Молоток", 8534), new Item("nnna", 6411)
};
InStockStatus[] statusList = {
new InStockStatus(1424, true), new InStockStatus(7892, false), new InStockStatus(8534, true), new InStockStatus (6411, true)